/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2016 Open Source Geospatial Foundation (OSGeo) * (C) 2014-2016 Boundless Spatial * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.ysld; import org.geotools.util.Converters; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; import java.io.StringWriter; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; /** * Base class for Yaml object wrappers. * * Yaml is represented as atomic types (literals, expressions) and YamlObjects (for wrapping lists and maps). * The factory method {@link #create(Object)} will sort out the details. * These YamlObjects are used to stage data when parsing a Yaml document. */ public class YamlObject<T> { /** * Convert raw object to Yaml wrapper. * <p> * <ul> * <li>Map wrapped as {@link YamlMap}</li> * <li>List wrapped as {@link YamlSeq}</li> * </ul> * Other data (Literals, Expressions) are considered atomic and do not provide a YamlObject representation. * * @param raw * @return Yaml object wrapper, or null if the provided raw object is null. */ @SuppressWarnings("unchecked") public static <W> YamlObject<W> create(W raw) { if (raw == null) { return null; } if (raw instanceof YamlObject) { return (YamlObject<W>) raw; } if (raw instanceof Map) { return (YamlObject<W>) new YamlMap(raw); } if (raw instanceof List) { return (YamlObject<W>) new YamlSeq(raw); } throw new IllegalArgumentException("Unable to create yaml object from: " + raw); } /** * underlying raw object */ protected T raw; protected YamlObject(T raw) { this.raw = raw; } /** * Casts this object to a {@link YamlMap}. */ public YamlMap map() { if (this instanceof YamlMap) { return (YamlMap) this; } throw new IllegalArgumentException("Object " + this + " is not a mapping"); } /** * Casts this object to a {@link YamlSeq}. */ public YamlSeq seq() { if (this instanceof YamlSeq) { return (YamlSeq) this; } throw new IllegalArgumentException("Object " + this + " is not a sequence"); } static Object yamlize(Object o) { if (o instanceof Map) { o = new YamlMap(o); } if (o instanceof List) { o = new YamlSeq(o); } if (o.getClass().isArray()) { o = new YamlSeq(o); } return o; } /** * Raw value access via path. * <p> * Indended for quick value lookup during test cases. * <ul> * <li>raw("1"): first value in a sequence</li> * <li>raw("x"): value for "x" in a map</li> * <li>raw("rules/2/symbolizers/3"): third symbolizer in second rule</li> * * @param path * @return raw value, or null if not available */ public Object lookup(String path) { Object here = this; for (String key : path.split("/")) { // Step 1: cast to wrapper if required for further navigation here = yamlize(here); // Step 2: navigate into data structure int index; try { index = Integer.parseInt(key); } catch (NumberFormatException nan) { index = -1; } if (here instanceof YamlMap) { YamlMap map = (YamlMap) here; if (index != -1) { String tempKey = map.key(index); here = map.get(tempKey); } else if (map.has(key)) { here = map.get(key); } else { throw new NoSuchElementException("Key: " + key + ", Keys: " + map.raw.keySet()); } } else if (here instanceof YamlSeq) { YamlSeq list = (YamlSeq) here; if (index != -1) { here = list.get(index); } else { throw new IndexOutOfBoundsException( "Index: " + key + ", Size: " + list.raw.size()); } } else { throw new NoSuchElementException( "Key: " + key + ", For:" + here.getClass().getSimpleName()); } } return here; } /** * Returns the raw object. */ public T raw() { return raw; } /** * See {@link #lookup}. Ensures that the result is wrapped as a YamlObject if it is a map, list, or array. * * @param path */ public Object lookupY(String path) { return yamlize(lookup(path)); } /** * Converts an object to the specified class. */ protected <C> C convert(Object obj, Class<C> clazz) { if (!clazz.isInstance(obj)) { C converted = Converters.convert(obj, clazz); if (converted != null) { obj = converted; } } try { return clazz.cast(obj); } catch (ClassCastException e) { throw new IllegalStateException( String.format("Unable to retrieve %s as %s", obj, clazz.getSimpleName()), e); } } /** * Yaml representation. * * @return Yaml representation */ @Override public String toString() { StringWriter w = new StringWriter(); DumperOptions dumperOpts = new DumperOptions(); dumperOpts.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); dumperOpts.setDefaultScalarStyle(DumperOptions.ScalarStyle.PLAIN); new Yaml(dumperOpts).dump(raw, w); return w.toString(); } }