/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.addthis.hydra.data.filter.value;

import javax.annotation.Nonnull;

import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.addthis.bundle.value.ValueArray;
import com.addthis.bundle.value.ValueFactory;
import com.addthis.bundle.value.ValueMap;
import com.addthis.bundle.value.ValueObject;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

/**
 * This {@link AbstractValueFilter ValueFilter} <span class="hydra-summary">pretty prints the value as a string</span>.
 * <p>Values that are arrays and maps do not follow the traditional value filter iterating behavior. Instead
 * they are printed as one object.</p>
 * <p>Example:</p>
 * <pre>
 *     {op:"field", from:"FOO", to:”BAR”,  filter:{op:"pretty-print"}},
 * </pre>
 *
 * @user-reference
 * @exclude-fields once
 */
public class ValueFilterPrettyPrint extends AbstractValueFilter {

    private static final Pattern DOUBLE_QUOTE_PATTERN = Pattern.compile("\"");

    private static final String DOUBLE_QUOTE_REPLACEMENT = Matcher.quoteReplacement("\\\"");

    public static enum Format { JSON }

    /**
     * Formatting for output. Current options
     * are ["json"]. Default is "json".
     */
    @Nonnull private final Format format;

    @JsonCreator public ValueFilterPrettyPrint(@JsonProperty("format") Format format) {
        this.format = format;
    }

    @Override
    public ValueObject filter(ValueObject v) {
        return ValueFactory.create(prettyPrint(v, format));
    }

    @Override
    public ValueObject filterValue(ValueObject v) {
        return ValueFactory.create(prettyPrint(v, format));
    }

    public static String prettyPrint(ValueObject input, @Nonnull Format format) {
        if (input == null) {
            return null;
        }
        switch (format) {
            case JSON:
                return prettyPrintAsJson(input);
            default:
                throw new IllegalArgumentException("unknown format " + format);
        }
    }

    public static String prettyPrintAsJson(@Nonnull ValueObject input) {
        ValueObject.TYPE type = input.getObjectType();
        switch (type) {
            case INT:
            case FLOAT:
                return input.toString();
            case STRING:
            case CUSTOM:
                return prettyPrintString(input.toString());
            case BYTES: {
                byte[] bytes = input.asBytes().asNative();
                StringBuilder builder = new StringBuilder();
                builder.append("[");
                for (int i = 0; i < bytes.length; i++) {
                    builder.append(bytes[i]);
                    if (i < (bytes.length - 1)) {
                        builder.append(",");
                    }
                }
                builder.append("]");
                return builder.toString();
            }
            case ARRAY: {
                ValueArray array = input.asArray();
                StringBuilder builder = new StringBuilder();
                builder.append("[");
                Iterator<ValueObject> iterator = array.iterator();
                while (iterator.hasNext()) {
                    builder.append(prettyPrintAsJson(iterator.next()));
                    if (iterator.hasNext()) {
                        builder.append(",");
                    }
                }
                builder.append("]");
                return builder.toString();
            }
            case MAP: {
                ValueMap map = input.asMap();
                StringBuilder builder = new StringBuilder();
                builder.append("{");
                Iterator<Map.Entry<String,ValueObject>> iterator = map.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<String,ValueObject> entry = iterator.next();
                    builder.append(prettyPrintString(entry.getKey()));
                    builder.append(":");
                    builder.append(prettyPrintAsJson(entry.getValue()));
                    if (iterator.hasNext()) {
                        builder.append(",");
                    }
                }
                builder.append("}");
                return builder.toString();
            }
            default:
                throw new IllegalStateException("Unrecognized object type " + type);
        }
    }

    public static String prettyPrintString(@Nonnull String input) {
        return "\"" + DOUBLE_QUOTE_PATTERN.matcher(input).replaceAll(DOUBLE_QUOTE_REPLACEMENT) + "\"";
    }

}

@@ -647,153 +647,33 @@
 ava.
-util.Iterator;%0Aimport java.util.Map;%0Aimport java.util.regex.Matcher;%0Aimport java.util.regex.Pattern;%0A%0Aimport com.addthis.bundle.value.ValueArray;
+io.UncheckedIOException;%0A
 %0Aimp
@@ -719,50 +719,8 @@
 ry;%0A
-import com.addthis.bundle.value.ValueMap;%0A
 impo
@@ -759,16 +759,58 @@
 eObject;
+%0Aimport com.addthis.codec.jackson.Jackson;
 %0A%0Aimport
@@ -909,16 +909,75 @@
 roperty;
+%0Aimport com.fasterxml.jackson.core.JsonProcessingException;
 %0A%0A/**%0A *
@@ -1473,182 +1473,8 @@
  %7B%0A%0A
-    private static final Pattern DOUBLE_QUOTE_PATTERN = Pattern.compile(%22%5C%22%22);%0A%0A    private static final String DOUBLE_QUOTE_REPLACEMENT = Matcher.quoteReplacement(%22%5C%5C%5C%22%22);%0A%0A
     
@@ -2487,1149 +2487,93 @@
     
-ValueObject.TYPE type = input.getObjectType();%0A        switch (type) %7B%0A            case INT:%0A            case FLOAT:%0A                return input.toString();%0A            case STRING:%0A            case CUSTOM:%0A                return prettyPrintString(input.toString());%0A            case BYTES: %7B%0A                byte%5B%5D bytes = input.asBytes().asNative();%0A                StringBuilder builder = new StringBuilder();%0A                builder.append(%22%5B%22);%0A                for (int i = 0; i %3C bytes.length; i++) %7B%0A                    builder.append(bytes%5Bi%5D);%0A                    if (i %3C (bytes.length - 1)) %7B%0A                        builder.append(%22,%22);%0A                    %7D%0A                %7D%0A                builder.append(%22%5D%22);%0A                return builder.toString();%0A            %7D%0A            case ARRAY: %7B%0A                ValueArray array = input.asArray();%0A                StringBuilder builder = new StringBuilder();%0A                builder.append(%22%5B%22);%0A                Iterator%3CValueObject%3E iterator = array.iterator();%0A                while (iterator.hasNext()) %7B%0A                    builder.append(prettyPrintAsJson(iterator.next
+try %7B%0A            return Jackson.defaultMapper().writeValueAsString(input.asNative
 ())
-)
 ;%0A  
@@ -2582,1310 +2582,104 @@
     
-            if (iterator.hasNext()) %7B%0A                        builder.append(%22,%22);%0A                    %7D%0A                %7D%0A                builder.append(%22%5D%22);%0A                return builder.toString();%0A            %7D%0A            case MAP: %7B%0A                ValueMap map = input.asMap();%0A                StringBuilder builder = new StringBuilder();%0A                builder.append(%22%7B%22);%0A                Iterator%3CMap.Entry%3CString,ValueObject%3E%3E iterator = map.entrySet().iterator();%0A                while (iterator.hasNext()) %7B%0A                    Map.Entry%3CString,ValueObject%3E entry = iterator.next();%0A                    builder.append(prettyPrintString(entry.getKey()));%0A                    builder.append(%22:%22);%0A                    builder.append(prettyPrintAsJson(entry.getValue()));%0A                    if (iterator.hasNext()) %7B%0A                        builder.append(%22,%22);%0A                    %7D%0A                %7D%0A                builder.append(%22%7D%22);%0A                return builder.toString();%0A            %7D%0A            default:%0A                throw new IllegalStateException(%22Unrecognized object type %22 + type);%0A        %7D%0A    %7D%0A%0A    public static String prettyPrintString(@Nonnull String input) %7B%0A        return %22%5C%22%22 + DOUBLE_QUOTE_PATTERN.matcher(input).replaceAll(DOUBLE_QUOTE_REPLACEMENT) + %22%5C%22%22;
+%7D catch (JsonProcessingException ex) %7B%0A            throw new UncheckedIOException(ex);%0A        %7D
 %0A   

Simplify pretty-print value filter. Ref T53384.