All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.org.snakeyaml.engine.v2.representer.StandardRepresenter Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/*
 * Copyright (c) 2018, http://www.snakeyaml.org
 *
 * 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.hazelcast.org.snakeyaml.engine.v2.representer;

import com.hazelcast.org.snakeyaml.engine.v2.api.DumpSettings;
import com.hazelcast.org.snakeyaml.engine.v2.api.RepresentToNode;
import com.hazelcast.org.snakeyaml.engine.v2.common.FlowStyle;
import com.hazelcast.org.snakeyaml.engine.v2.common.NonPrintableStyle;
import com.hazelcast.org.snakeyaml.engine.v2.common.ScalarStyle;
import com.hazelcast.org.snakeyaml.engine.v2.exceptions.YamlEngineException;
import com.hazelcast.org.snakeyaml.engine.v2.nodes.Node;
import com.hazelcast.org.snakeyaml.engine.v2.nodes.Tag;
import com.hazelcast.org.snakeyaml.engine.v2.scanner.StreamReader;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;

/**
 * Represent standard Java classes
 */
public class StandardRepresenter extends BaseRepresenter {

    protected Map, Tag> classTags;
    protected DumpSettings settings;

    public StandardRepresenter(DumpSettings settings) {
        this.defaultFlowStyle = settings.getDefaultFlowStyle();
        this.defaultScalarStyle = settings.getDefaultScalarStyle();

        this.nullRepresenter = new RepresentNull();
        this.representers.put(String.class, new RepresentString());
        this.representers.put(Boolean.class, new RepresentBoolean());
        this.representers.put(Character.class, new RepresentString());
        this.representers.put(UUID.class, new RepresentUuid());
        this.representers.put(Optional.class, new RepresentOptional());
        this.representers.put(byte[].class, new RepresentByteArray());

        RepresentToNode primitiveArray = new RepresentPrimitiveArray();
        representers.put(short[].class, primitiveArray);
        representers.put(int[].class, primitiveArray);
        representers.put(long[].class, primitiveArray);
        representers.put(float[].class, primitiveArray);
        representers.put(double[].class, primitiveArray);
        representers.put(char[].class, primitiveArray);
        representers.put(boolean[].class, primitiveArray);

        this.parentClassRepresenters.put(Number.class, new RepresentNumber());
        this.parentClassRepresenters.put(List.class, new RepresentList());
        this.parentClassRepresenters.put(Map.class, new RepresentMap());
        this.parentClassRepresenters.put(Set.class, new RepresentSet());
        this.parentClassRepresenters.put(Iterator.class, new RepresentIterator());
        this.parentClassRepresenters.put(new Object[0].getClass(), new RepresentArray());
        this.parentClassRepresenters.put(Enum.class, new RepresentEnum());
        classTags = new HashMap();
        this.settings = settings;
    }

    protected Tag getTag(Class clazz, Tag defaultTag) {
        if (classTags.containsKey(clazz)) {
            return classTags.get(clazz);
        } else {
            return defaultTag;
        }
    }

    /**
     * Define a tag for the Class to serialize.
     *
     * @param clazz Class which tag is changed
     * @param tag   new tag to be used for every instance of the specified
     *              Class
     * @return the previous tag associated with the Class
     */
    public Tag addClassTag(Class clazz, Tag tag) {
        if (tag == null) {
            throw new NullPointerException("Tag must be provided.");
        }
        return classTags.put(clazz, tag);
    }

    //remove and move to BaseRe
    protected class RepresentNull implements RepresentToNode {
        public Node representData(Object data) {
            return representScalar(Tag.NULL, "null");
        }
    }

    public static final Pattern MULTILINE_PATTERN = Pattern.compile("\n|\u0085|\u2028|\u2029");

    protected class RepresentString implements RepresentToNode {
        public Node representData(Object data) {
            Tag tag = Tag.STR;
            ScalarStyle style = ScalarStyle.PLAIN;
            String value = data.toString();
            if (settings.getNonPrintableStyle() == NonPrintableStyle.BINARY && !StreamReader.isPrintable(value)) {
                tag = Tag.BINARY;
                final byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
                // sometimes above will just silently fail - it will return incomplete data
                // it happens when String has invalid code points
                // (for example half surrogate character without other half)
                final String checkValue = new String(bytes, StandardCharsets.UTF_8);
                if (!checkValue.equals(value)) {
                    throw new YamlEngineException("invalid string value has occurred");
                }
                value = Base64.getEncoder().encodeToString(bytes);
                style = ScalarStyle.LITERAL;
            }
            // if no other scalar style is explicitly set, use literal style for
            // multiline scalars
            if (defaultScalarStyle == ScalarStyle.PLAIN && MULTILINE_PATTERN.matcher(value).find()) {
                style = ScalarStyle.LITERAL;
            }
            return representScalar(tag, value, style);
        }
    }

    protected class RepresentBoolean implements RepresentToNode {
        public Node representData(Object data) {
            String value;
            if (Boolean.TRUE.equals(data)) {
                value = "true";
            } else {
                value = "false";
            }
            return representScalar(Tag.BOOL, value);
        }
    }

    protected class RepresentNumber implements RepresentToNode {
        public Node representData(Object data) {
            Tag tag;
            String value;
            if (data instanceof Byte || data instanceof Short || data instanceof Integer
                    || data instanceof Long || data instanceof BigInteger) {
                tag = Tag.INT;
                value = data.toString();
            } else {
                Number number = (Number) data;
                tag = Tag.FLOAT;
                if (number.equals(Double.NaN)) {
                    value = ".NaN";
                } else if (number.equals(Double.POSITIVE_INFINITY)) {
                    value = ".inf";
                } else if (number.equals(Double.NEGATIVE_INFINITY)) {
                    value = "-.inf";
                } else {
                    value = number.toString();
                }
            }
            return representScalar(getTag(data.getClass(), tag), value);
        }
    }

    protected class RepresentList implements RepresentToNode {
        @SuppressWarnings("unchecked")
        public Node representData(Object data) {
            return representSequence(getTag(data.getClass(), Tag.SEQ), (List) data, FlowStyle.AUTO);
        }
    }

    protected class RepresentIterator implements RepresentToNode {
        @SuppressWarnings("unchecked")
        public Node representData(Object data) {
            Iterator iter = (Iterator) data;
            return representSequence(getTag(data.getClass(), Tag.SEQ), new IteratorWrapper(iter),
                    FlowStyle.AUTO);
        }
    }

    private static class IteratorWrapper implements Iterable {
        private final Iterator iter;

        public IteratorWrapper(Iterator iter) {
            this.iter = iter;
        }

        public Iterator iterator() {
            return iter;
        }
    }

    protected class RepresentArray implements RepresentToNode {
        public Node representData(Object data) {
            Object[] array = (Object[]) data;
            List list = Arrays.asList(array);
            return representSequence(Tag.SEQ, list, FlowStyle.AUTO);
        }
    }

    /**
     * Represents primitive arrays, such as short[] and float[], by converting
     * them into equivalent {@link List} using the appropriate
     * autoboxing type.
     */
    protected class RepresentPrimitiveArray implements RepresentToNode {
        public Node representData(Object data) {
            Class type = data.getClass().getComponentType();

            if (byte.class == type) {
                return representSequence(Tag.SEQ, asByteList(data), FlowStyle.AUTO);
            } else if (short.class == type) {
                return representSequence(Tag.SEQ, asShortList(data), FlowStyle.AUTO);
            } else if (int.class == type) {
                return representSequence(Tag.SEQ, asIntList(data), FlowStyle.AUTO);
            } else if (long.class == type) {
                return representSequence(Tag.SEQ, asLongList(data), FlowStyle.AUTO);
            } else if (float.class == type) {
                return representSequence(Tag.SEQ, asFloatList(data), FlowStyle.AUTO);
            } else if (double.class == type) {
                return representSequence(Tag.SEQ, asDoubleList(data), FlowStyle.AUTO);
            } else if (char.class == type) {
                return representSequence(Tag.SEQ, asCharList(data), FlowStyle.AUTO);
            } else if (boolean.class == type) {
                return representSequence(Tag.SEQ, asBooleanList(data), FlowStyle.AUTO);
            }

            throw new YamlEngineException("Unexpected primitive '" + type.getCanonicalName() + "'");
        }

        private List asByteList(Object in) {
            byte[] array = (byte[]) in;
            List list = new ArrayList<>(array.length);
            for (int i = 0; i < array.length; ++i)
                list.add(array[i]);
            return list;
        }

        private List asShortList(Object in) {
            short[] array = (short[]) in;
            List list = new ArrayList<>(array.length);
            for (int i = 0; i < array.length; ++i)
                list.add(array[i]);
            return list;
        }

        private List asIntList(Object in) {
            int[] array = (int[]) in;
            List list = new ArrayList<>(array.length);
            for (int i = 0; i < array.length; ++i)
                list.add(array[i]);
            return list;
        }

        private List asLongList(Object in) {
            long[] array = (long[]) in;
            List list = new ArrayList<>(array.length);
            for (int i = 0; i < array.length; ++i)
                list.add(array[i]);
            return list;
        }

        private List asFloatList(Object in) {
            float[] array = (float[]) in;
            List list = new ArrayList<>(array.length);
            for (int i = 0; i < array.length; ++i)
                list.add(array[i]);
            return list;
        }

        private List asDoubleList(Object in) {
            double[] array = (double[]) in;
            List list = new ArrayList<>(array.length);
            for (int i = 0; i < array.length; ++i)
                list.add(array[i]);
            return list;
        }

        private List asCharList(Object in) {
            char[] array = (char[]) in;
            List list = new ArrayList<>(array.length);
            for (int i = 0; i < array.length; ++i)
                list.add(array[i]);
            return list;
        }

        private List asBooleanList(Object in) {
            boolean[] array = (boolean[]) in;
            List list = new ArrayList<>(array.length);
            for (int i = 0; i < array.length; ++i)
                list.add(array[i]);
            return list;
        }
    }

    protected class RepresentMap implements RepresentToNode {
        @SuppressWarnings("unchecked")
        public Node representData(Object data) {
            return representMapping(getTag(data.getClass(), Tag.MAP), (Map) data,
                    FlowStyle.AUTO);
        }
    }

    protected class RepresentSet implements RepresentToNode {
        @SuppressWarnings("unchecked")
        public Node representData(Object data) {
            Map value = new LinkedHashMap<>();
            Set set = (Set) data;
            for (Object key : set) {
                value.put(key, null);
            }
            return representMapping(getTag(data.getClass(), Tag.SET), value, FlowStyle.AUTO);
        }
    }


    protected class RepresentEnum implements RepresentToNode {
        public Node representData(Object data) {
            Tag tag = new Tag(data.getClass());
            return representScalar(getTag(data.getClass(), tag), ((Enum) data).name());
        }
    }

    protected class RepresentByteArray implements RepresentToNode {
        public Node representData(Object data) {
            return representScalar(Tag.BINARY, Base64.getEncoder().encodeToString((byte[]) data), ScalarStyle.LITERAL);
        }
    }

    protected class RepresentUuid implements RepresentToNode {
        public Node representData(Object data) {
            return representScalar(getTag(data.getClass(), new Tag(UUID.class)), data.toString());
        }
    }

    protected class RepresentOptional implements RepresentToNode {
        public Node representData(Object data) {
            Optional opt = (Optional) data;
            if (opt.isPresent()) {
                Node node = represent(opt.get());
                node.setTag(new Tag(Optional.class));
                return node;
            } else {
                return representScalar(Tag.NULL, "null");
            }
        }
    }
}