/*
* 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.parse;
import org.geotools.ysld.YamlMap;
import org.geotools.ysld.YamlObject;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Holds the context of a call to {@link YamlParser#parse(YamlParseHandler, Map)} during its descent into the {@link YamlObject} being parsed.
*
*/
public class YamlParseContext {
/*
* Handlers may handle a YAML object as a whole, or they may handle some specific subset of properties for one. The former delegates the the
* latter by pushing the latter on to the stack with the current object. Example: TextHandler handling X pushes a FontHandler with X as the
* current node.
*
* Odd design resulted from initially planning to do stream parsing then recycling the code for a in memory parser.
*/
Deque<Entry> stack;
Entry curr;
Map<String, Object> docHints = new HashMap<>();
public YamlParseContext() {
stack = new ArrayDeque<Entry>();
}
/**
* Parse a child of the current object if present
*
* @param key key of the child entry
* @param handler handler to use
* @return self
*/
public YamlParseContext push(String key, YamlParseHandler handler) {
return push(curr.obj, key, handler);
}
/**
* Parse a child of the specified object if present
*
* @param scope object to start from
* @param key key of the child entry
* @param handler handler to use
* @return self
*/
public YamlParseContext push(YamlObject scope, String key, YamlParseHandler handler) {
YamlMap map = scope.map();
if (map.has(key)) {
return doPush(scope.map().obj(key), handler);
}
return this;
}
/**
* Add a handler to the stack handling the current object. Used for "inlined"/common properties.
*
* @param handler handler to use
* @return self
*/
public YamlParseContext push(YamlParseHandler handler) {
return doPush(curr.obj, handler);
}
/**
* Add a handler to the stack handling the specified object
*
* @param obj the object to parse
* @param handler handler to use
* @return self
*/
public YamlParseContext push(YamlObject obj, YamlParseHandler handler) {
return doPush(obj, handler);
}
YamlParseContext doPush(YamlObject obj, YamlParseHandler handler) {
stack.push(new Entry(obj, handler));
return this;
}
public YamlParseContext pop() {
stack.pop();
return this;
}
/**
*
* Pop a {@link YamlParseHandler} from the handler stack and execute its {@link YamlParseHandler#handle(YamlObject, YamlParseContext)} method on
* the {@link YamlObject} with which it was pushed.
*
* @return True if more handlers remain on handler stack; false if the handler stack is empty.
*/
public boolean next() {
curr = stack.pop();
curr.handler.handle(curr.obj, this);
return !stack.isEmpty();
}
public @Nullable Object getDocHint(String key) {
return docHints.get(key);
}
public void setDocHint(String key, Object value) {
docHints.put(key, value);
}
public void mergeDocHints(Map<String, Object> hints) {
docHints.putAll(hints);
}
/**
* Container object for a {@link YamlParseHandler} and the {@link YamlObject} it should handle. Instances of this class are added to the stack in
* the {@link YamlParseContext} by a {@link YamlParseHandler} as it descends into the {@link YamlObject} it is parsing.
*
*/
static class Entry {
YamlObject obj;
YamlParseHandler handler;
Entry(YamlObject obj, YamlParseHandler handler) {
this.obj = obj;
this.handler = handler;
}
}
}