/* Copyright (c) 2006 Ola Bini
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* $Id: BaseConstructorImpl.java,v 1.2 2006/09/23 21:43:30 olabini Exp $
*/
package org.jvyaml;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.jvyaml.nodes.Node;
import org.jvyaml.nodes.ScalarNode;
import org.jvyaml.nodes.SequenceNode;
import org.jvyaml.nodes.MappingNode;
/**
* @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
* @version $Revision: 1.2 $
*/
public class BaseConstructorImpl implements Constructor {
private final static Map yamlConstructors = new HashMap();
private final static Map yamlMultiConstructors = new HashMap();
private final static Map yamlMultiRegexps = new HashMap();
public YamlConstructor getYamlConstructor(final Object key) {
return (YamlConstructor)yamlConstructors.get(key);
}
public YamlMultiConstructor getYamlMultiConstructor(final Object key) {
return (YamlMultiConstructor)yamlMultiConstructors.get(key);
}
public Pattern getYamlMultiRegexp(final Object key) {
return (Pattern)yamlMultiRegexps.get(key);
}
public Set getYamlMultiRegexps() {
return yamlMultiRegexps.keySet();
}
public static void addConstructor(final String tag, final YamlConstructor ctor) {
yamlConstructors.put(tag,ctor);
}
public static void addMultiConstructor(final String tagPrefix, final YamlMultiConstructor ctor) {
yamlMultiConstructors.put(tagPrefix,ctor);
yamlMultiRegexps.put(tagPrefix,Pattern.compile("^"+tagPrefix));
}
private Composer composer;
private Map constructedObjects = new HashMap();
private Map recursiveObjects = new HashMap();
public BaseConstructorImpl(final Composer composer) {
this.composer = composer;
}
public boolean checkData() {
return composer.checkNode();
}
public Object getData() {
if(composer.checkNode()) {
Node node = composer.getNode();
if(null != node) {
return constructDocument(node);
}
}
return null;
}
private class DocumentIterator implements Iterator {
public boolean hasNext() {return checkData();}
public Object next() {return getData();}
public void remove() {}
}
public Iterator eachDocument() {
return new DocumentIterator();
}
public Iterator iterator() {
return eachDocument();
}
public Object constructDocument(final Node node) {
final Object data = constructObject(node);
constructedObjects.clear();
recursiveObjects.clear();
return data;
}
public static class YamlMultiAdapter implements YamlConstructor {
private YamlMultiConstructor ctor;
private String prefix;
public YamlMultiAdapter(final YamlMultiConstructor ctor, final String prefix) {
this.ctor = ctor;
this.prefix = prefix;
}
public Object call(final Constructor self, final Node node) {
return ctor.call(self,this.prefix,node);
}
}
public Object constructObject(final Node node) {
if(constructedObjects.containsKey(node)) {
return constructedObjects.get(node);
}
if(recursiveObjects.containsKey(node)) {
throw new ConstructorException(null,"found recursive node",null);
}
recursiveObjects.put(node,null);
YamlConstructor ctor = getYamlConstructor(node.getTag());
if(ctor == null) {
boolean through = true;
for(final Iterator iter = getYamlMultiRegexps().iterator();iter.hasNext();) {
final String tagPrefix = (String)iter.next();
final Pattern reg = getYamlMultiRegexp(tagPrefix);
if(reg.matcher(node.getTag()).find()) {
final String tagSuffix = node.getTag().substring(tagPrefix.length());
ctor = new YamlMultiAdapter(getYamlMultiConstructor(tagPrefix),tagSuffix);
through = false;
break;
}
}
if(through) {
final YamlMultiConstructor xctor = getYamlMultiConstructor(null);
if(null != xctor) {
ctor = new YamlMultiAdapter(xctor,node.getTag());
} else {
ctor = getYamlConstructor(null);
if(ctor == null) {
ctor = CONSTRUCT_PRIMITIVE;
}
}
}
}
final Object data = ctor.call(this,node);
constructedObjects.put(node,data);
recursiveObjects.remove(node);
return data;
}
public Object constructPrimitive(final Node node) {
if(node instanceof ScalarNode) {
return constructScalar(node);
} else if(node instanceof SequenceNode) {
return constructSequence(node);
} else if(node instanceof MappingNode) {
return constructMapping(node);
} else {
System.err.println(node.getTag());
}
return null;
}
public Object constructScalar(final Node node) {
if(!(node instanceof ScalarNode)) {
if(node instanceof MappingNode) {
final Map vals = ((Map)node.getValue());
for(final Iterator iter = vals.keySet().iterator();iter.hasNext();) {
final Node key = (Node)iter.next();
if("tag:yaml.org,2002:value".equals(key.getTag())) {
return constructScalar((Node)vals.get(key));
}
}
}
throw new ConstructorException(null,"expected a scalar node, but found " + node.getClass().getName(),null);
}
return node.getValue();
}
public Object constructPrivateType(final Node node) {
Object val = null;
if(node.getValue() instanceof Map) {
val = constructMapping(node);
} else if(node.getValue() instanceof List) {
val = constructSequence(node);
} else {
val = node.getValue().toString();
}
return new PrivateType(node.getTag(),val);
}
public Object constructSequence(final Node node) {
if(!(node instanceof SequenceNode)) {
throw new ConstructorException(null,"expected a sequence node, but found " + node.getClass().getName(),null);
}
final List internal = (List)node.getValue();
final List val = new ArrayList(internal.size());
for(final Iterator iter = internal.iterator();iter.hasNext();) {
val.add(constructObject((Node)iter.next()));
}
return val;
}
public Object constructMapping(final Node node) {
if(!(node instanceof MappingNode)) {
throw new ConstructorException(null,"expected a mapping node, but found " + node.getClass().getName(),null);
}
Map mapping = new HashMap();
List merge = null;
final Map val = (Map)node.getValue();
for(final Iterator iter = val.keySet().iterator();iter.hasNext();) {
final Node key_v = (Node)iter.next();
final Node value_v = (Node)val.get(key_v);
if(key_v.getTag().equals("tag:yaml.org,2002:merge")) {
if(merge != null) {
throw new ConstructorException("while constructing a mapping", "found duplicate merge key",null);
}
if(value_v instanceof MappingNode) {
merge = new LinkedList();
merge.add(constructMapping(value_v));
} else if(value_v instanceof SequenceNode) {
merge = new LinkedList();
final List vals = (List)value_v.getValue();
for(final Iterator sub = vals.iterator();sub.hasNext();) {
final Node subnode = (Node)sub.next();
if(!(subnode instanceof MappingNode)) {
throw new ConstructorException("while constructing a mapping","expected a mapping for merging, but found " + subnode.getClass().getName(),null);
}
merge.add(0,constructMapping(subnode));
}
} else {
throw new ConstructorException("while constructing a mapping","expected a mapping or list of mappings for merging, but found " + value_v.getClass().getName(),null);
}
} else if(key_v.getTag().equals("tag:yaml.org,2002:value")) {
if(mapping.containsKey("=")) {
throw new ConstructorException("while construction a mapping", "found duplicate value key", null);
}
mapping.put("=",constructObject(value_v));
} else {
mapping.put(constructObject(key_v),constructObject(value_v));
}
}
if(null != merge) {
merge.add(mapping);
mapping = new HashMap();
for(final Iterator iter = merge.iterator();iter.hasNext();) {
mapping.putAll((Map)iter.next());
}
}
return mapping;
}
public Object constructPairs(final Node node) {
if(!(node instanceof MappingNode)) {
throw new ConstructorException(null,"expected a mapping node, but found " + node.getClass().getName(), null);
}
final List value = new LinkedList();
final Map vals = (Map)node.getValue();
for(final Iterator iter = vals.keySet().iterator();iter.hasNext();) {
final Node key = (Node)iter.next();
final Node val = (Node)vals.get(key);
value.add(new Object[]{constructObject(key),constructObject(val)});
}
return value;
}
public final static YamlConstructor CONSTRUCT_PRIMITIVE = new YamlConstructor() {
public Object call(final Constructor self, final Node node) {
return self.constructPrimitive(node);
}};
public final static YamlConstructor CONSTRUCT_SCALAR = new YamlConstructor() {
public Object call(final Constructor self, final Node node) {
return self.constructScalar(node);
}};
public final static YamlConstructor CONSTRUCT_PRIVATE = new YamlConstructor() {
public Object call(final Constructor self, final Node node) {
return self.constructPrivateType(node);
}};
public final static YamlConstructor CONSTRUCT_SEQUENCE = new YamlConstructor() {
public Object call(final Constructor self, final Node node) {
return self.constructSequence(node);
}};
public final static YamlConstructor CONSTRUCT_MAPPING = new YamlConstructor() {
public Object call(final Constructor self, final Node node) {
return self.constructMapping(node);
}};
public static void main(final String[] args) throws Exception {
final String filename = args[0];
System.out.println("Reading of file: \"" + filename + "\"");
final StringBuffer input = new StringBuffer();
final java.io.Reader reader = new java.io.FileReader(filename);
char[] buff = new char[1024];
int read = 0;
while(true) {
read = reader.read(buff);
input.append(buff,0,read);
if(read < 1024) {
break;
}
}
reader.close();
final String str = input.toString();
// final long before = System.currentTimeMillis();
// for(int i=0;i<1;i++) {
final Constructor ctor = new BaseConstructorImpl(new ComposerImpl(new ParserImpl(new ScannerImpl(str)),new ResolverImpl()));
for(final Iterator iter = ctor.eachDocument();iter.hasNext();) {
System.out.println(iter.next());
}
// }
// final long after = System.currentTimeMillis();
// final long time = after-before;
// final double timeS = (after-before)/1000.0;
// System.out.println("Walking through the nodes for the file: " + filename + " took " + time + "ms, or " + timeS + " seconds");
}
}// BaseConstructorImpl