/* * Constellation - An open source and standard compliant SDI * http://www.constellation-sdi.org * * Copyright 2014 Geomatys. * * 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 org.constellation.json.metadata; import java.util.Date; import java.util.Locale; import java.util.Arrays; import java.util.ArrayList; import java.util.MissingResourceException; import java.io.IOException; import java.nio.charset.Charset; import org.opengis.util.ControlledVocabulary; import org.apache.sis.measure.Angle; import org.apache.sis.util.iso.Types; import org.apache.sis.util.CharSequences; import static org.constellation.json.JsonMetadataConstants.*; /** * A node containing both a {@link TemplateNode} and its associated value. * This node extends {@code ArrayList} for opportunist reasons only. * The list elements are children. * * @author Martin Desruisseaux (Geomatys) */ @SuppressWarnings("serial") final class ValueNode extends ArrayList<ValueNode> { /** * The template for which this node contains a value. */ final TemplateNode template; /** * The indices of each path element. */ final int[] indices; /** * The value associated to this node, or {@code null}. */ final Object value; /** * Creates a new node for the given metadata. * * @param template The template to apply. * @param indices The indices of each path element. * @param value The value associated to this node, or {@code null}. */ ValueNode(final TemplateNode template, final int[] indices, final Object value) { this.template = template; this.value = value; this.indices = (template.path != null) ? Arrays.copyOfRange(indices, 0, template.path.length) : null; } /** * Returns {@code true} if the path is equals to the given numeroted path. */ final boolean pathEquals(final NumerotedPath np) { return Arrays.equals(template.path, np.path) && Arrays.equals(indices, np.indices); } /** * Formats the path. Callers must ensure that {@link TemplateNode#path} is non-null * before to invoke this method. */ final void formatPath(final Appendable out, int pathOffset) throws IOException { out.append('"'); NumerotedPath.formatPath(out, template.path, pathOffset, indices); out.append('"'); } /** * Formats the value. */ final void formatValue(final Appendable out) throws IOException { final String p; if (value == null) { p = null; } else if (value instanceof Number) { p = value.toString(); } else if (value instanceof Angle) { p = Double.toString(((Angle) value).degrees()); } else { /* * Above were unquoted cases. Below are texts to quote. */ out.append('"'); if (value instanceof ControlledVocabulary) { out.append(Types.getStandardName(value.getClass())).append('.') .append(Types.getCodeName((ControlledVocabulary) value)); } else if (value instanceof Date) { if (DATE_READ_ONLY.equals(template.render)) { synchronized (DATE_HOUR_FORMAT) { p = DATE_HOUR_FORMAT.format(value); } } else { synchronized (DATE_FORMAT) { p = DATE_FORMAT.format(value); } } out.append(p); } else if (value instanceof Locale) { String language; try { language = ((Locale) value).getISO3Language(); } catch (MissingResourceException e) { language = ((Locale) value).getLanguage(); } out.append("LanguageCode.").append(language); } else if (value instanceof Charset) { out.append(((Charset) value).name()); } else { CharSequence cs = value.toString(); cs = CharSequences.replace(cs, "\"", "\\\""); cs = CharSequences.replace(cs, "\t", "\\t"); cs = CharSequences.replace(cs, "\n", "\\n"); out.append(cs); } out.append('"'); return; } out.append(p); } /** * Returns a string representation for debugging purpose only. */ @Override public String toString() { final StringBuilder buffer = new StringBuilder(60); toString(buffer, 0, 0); return buffer.toString(); } /** * Implementation of {@link #toString()} to be invoked recursively by children. */ private void toString(final StringBuilder buffer, int indentation, int pathOffset) { boolean hasValue = template.isField(); buffer.append(CharSequences.spaces(indentation)).append(hasValue ? "Field" : "Node").append('['); hasValue &= (value != null); if (template.path != null) { buffer.append("path:"); try { formatPath(buffer, pathOffset); } catch (IOException e) { throw new AssertionError(e); // Should never happen, since we are writting to a StringBuilder. } if (hasValue) { buffer.append(", "); } pathOffset += template.path.length; // For the iteration over children. } if (hasValue) { buffer.append("value:\"").append(value).append('"'); } buffer.append("]\n"); indentation += 4; for (final ValueNode child : this) { child.toString(buffer, indentation, pathOffset); } } }