/* 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: ResolverImpl.java,v 1.3 2006/09/30 14:13:35 olabini Exp $
*/
package org.jvyaml;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.LinkedList;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import org.jvyaml.nodes.MappingNode;
import org.jvyaml.nodes.Node;
import org.jvyaml.nodes.ScalarNode;
import org.jvyaml.nodes.SequenceNode;
/**
* @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
* @version $Revision: 1.3 $
*/
public class ResolverImpl implements Resolver {
private final static Map yamlImplicitResolvers = new HashMap();
private final static Map yamlPathResolvers = new HashMap();
private List resolverExactPaths = new LinkedList();
private List resolverPrefixPaths = new LinkedList();
public static void addImplicitResolver(final String tag, final Pattern regexp, final String first) {
final String firstVal = first;
if(firstVal == null || "".equals("firstVal")) {
List curr = (List)yamlImplicitResolvers.get("");
if(curr == null) {
curr = new LinkedList();
yamlImplicitResolvers.put("",curr);
}
curr.add(new Object[]{tag,regexp});
} else {
final char[] chrs = firstVal.toCharArray();
for(int i=0,j=chrs.length;i<j;i++) {
final Character theC = new Character(chrs[i]);
List curr = (List)yamlImplicitResolvers.get(theC);
if(curr == null) {
curr = new LinkedList();
yamlImplicitResolvers.put(theC,curr);
}
curr.add(new Object[]{tag,regexp});
}
}
}
public static void addPathResolver(final String tag, final List path, final Class kind) {
final List newPath = new LinkedList();
Object nodeCheck=null;
Object indexCheck=null;
for(final Iterator iter = path.iterator();iter.hasNext();) {
final Object element = iter.next();
if(element instanceof List) {
final List eList = (List)element;
if(eList.size() == 2) {
nodeCheck = eList.get(0);
indexCheck = eList.get(1);
} else if(eList.size() == 1) {
nodeCheck = eList.get(0);
indexCheck = Boolean.TRUE;
} else {
throw new ResolverException("Invalid path element: " + element);
}
} else {
nodeCheck = null;
indexCheck = element;
}
if(nodeCheck instanceof String) {
nodeCheck = ScalarNode.class;
} else if(nodeCheck instanceof List) {
nodeCheck = SequenceNode.class;
} else if(nodeCheck instanceof Map) {
nodeCheck = MappingNode.class;
} else if(null != nodeCheck && !ScalarNode.class.equals(nodeCheck) && !SequenceNode.class.equals(nodeCheck) && !MappingNode.class.equals(nodeCheck)) {
throw new ResolverException("Invalid node checker: " + nodeCheck);
}
if(!(indexCheck instanceof String || indexCheck instanceof Integer) && null != indexCheck) {
throw new ResolverException("Invalid index checker: " + indexCheck);
}
newPath.add(new Object[]{nodeCheck,indexCheck});
}
Class newKind = null;
if(String.class.equals(kind)) {
newKind = ScalarNode.class;
} else if(List.class.equals(kind)) {
newKind = SequenceNode.class;
} else if(Map.class.equals(kind)) {
newKind = MappingNode.class;
} else if(kind != null && !ScalarNode.class.equals(kind) && !SequenceNode.class.equals(kind) && !MappingNode.class.equals(kind)) {
throw new ResolverException("Invalid node kind: " + kind);
} else {
newKind = kind;
}
final List x = new ArrayList(1);
x.add(newPath);
final List y = new ArrayList(2);
y.add(x);
y.add(kind);
yamlPathResolvers.put(y,tag);
}
public void descendResolver(final Node currentNode, final Object currentIndex) {
final Map exactPaths = new HashMap();
final List prefixPaths = new LinkedList();
if(null != currentNode) {
final int depth = resolverPrefixPaths.size();
for(final Iterator iter = ((List)resolverPrefixPaths.get(0)).iterator();iter.hasNext();) {
final Object[] obj = (Object[])iter.next();
final List path = (List)obj[0];
if(checkResolverPrefix(depth,path,(Class)obj[1],currentNode,currentIndex)) {
if(path.size() > depth) {
prefixPaths.add(new Object[] {path,obj[1]});
} else {
final List resPath = new ArrayList(2);
resPath.add(path);
resPath.add(obj[1]);
exactPaths.put(obj[1],yamlPathResolvers.get(resPath));
}
}
}
} else {
for(final Iterator iter = yamlPathResolvers.keySet().iterator();iter.hasNext();) {
final List key = (List)iter.next();
final List path = (List)key.get(0);
final Class kind = (Class)key.get(1);
if(null == path) {
exactPaths.put(kind,yamlPathResolvers.get(key));
} else {
prefixPaths.add(key);
}
}
}
resolverExactPaths.add(0,exactPaths);
resolverPrefixPaths.add(0,prefixPaths);
}
public void ascendResolver() {
resolverExactPaths.remove(0);
resolverPrefixPaths.remove(0);
}
public boolean checkResolverPrefix(final int depth, final List path, final Class kind, final Node currentNode, final Object currentIndex) {
final Object[] check = (Object[])path.get(depth-1);
final Object nodeCheck = check[0];
final Object indexCheck = check[1];
if(nodeCheck instanceof String) {
if(!currentNode.getTag().equals(nodeCheck)) {
return false;
}
} else if(null != nodeCheck) {
if(!((Class)nodeCheck).isInstance(currentNode)) {
return false;
}
}
if(indexCheck == Boolean.TRUE && currentIndex != null) {
return false;
}
if(indexCheck == Boolean.FALSE && currentIndex == null) {
return false;
}
if(indexCheck instanceof String) {
if(!(currentIndex instanceof ScalarNode && indexCheck.equals(((ScalarNode)currentIndex).getValue()))) {
return false;
}
} else if(indexCheck instanceof Integer) {
if(!currentIndex.equals(indexCheck)) {
return false;
}
}
return true;
}
public String resolve(final Class kind, final String value, final boolean[] implicit) {
List resolvers = null;
if(kind.equals(ScalarNode.class) && implicit[0]) {
if("".equals(value)) {
resolvers = (List)yamlImplicitResolvers.get("");
} else {
resolvers = (List)yamlImplicitResolvers.get(new Character(value.charAt(0)));
}
if(resolvers == null) {
resolvers = new LinkedList();
}
if(yamlImplicitResolvers.containsKey(null)) {
resolvers.addAll((List)yamlImplicitResolvers.get(null));
}
for(final Iterator iter = resolvers.iterator();iter.hasNext();) {
final Object[] val = (Object[])iter.next();
if(((Pattern)val[1]).matcher(value).matches()) {
return (String)val[0];
}
}
}
final Map exactPaths = (Map)resolverExactPaths.get(0);
if(exactPaths.containsKey(kind)) {
return (String)exactPaths.get(kind);
}
if(exactPaths.containsKey(null)) {
return (String)exactPaths.get(null);
}
if(kind.equals(ScalarNode.class)) {
return YAML.DEFAULT_SCALAR_TAG;
} else if(kind.equals(SequenceNode.class)) {
return YAML.DEFAULT_SEQUENCE_TAG;
} else if(kind.equals(MappingNode.class)) {
return YAML.DEFAULT_MAPPING_TAG;
}
return null;
}
static {
addImplicitResolver("tag:yaml.org,2002:bool",Pattern.compile("^(?:yes|Yes|YES|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)$"),"yYnNtTfFoO");
addImplicitResolver("tag:yaml.org,2002:float",Pattern.compile("^(?:[-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+][0-9]+)?|[-+]?(?:[0-9][0-9_]*)?\\.[0-9_]+(?:[eE][-+][0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"),"-+0123456789.");
addImplicitResolver("tag:yaml.org,2002:int",Pattern.compile("^(?:[-+]?0b[0-1_]+|[-+]?0[0-7_]+|[-+]?(?:0|[1-9][0-9_]*)|[-+]?0x[0-9a-fA-F_]+|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$"),"-+0123456789");
addImplicitResolver("tag:yaml.org,2002:merge",Pattern.compile("^(?:<<)$"),"<");
addImplicitResolver("tag:yaml.org,2002:null",Pattern.compile("^(?:~|null|Null|NULL| )$"),"~nN\0");
addImplicitResolver("tag:yaml.org,2002:null",Pattern.compile("^$"),null);
addImplicitResolver("tag:yaml.org,2002:timestamp",Pattern.compile("^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]|[0-9][0-9][0-9][0-9]-[0-9][0-9]?-[0-9][0-9]?(?:[Tt]|[ \t]+)[0-9][0-9]?:[0-9][0-9]:[0-9][0-9](?:\\.[0-9]*)?(?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$"),"0123456789");
addImplicitResolver("tag:yaml.org,2002:value",Pattern.compile("^(?:=)$"),"=");
// The following implicit resolver is only for documentation purposes. It cannot work
// because plain scalars cannot start with '!', '&', or '*'.
addImplicitResolver("tag:yaml.org,2002:yaml",Pattern.compile("^(?:!|&|\\*)$"),"!&*");
}
}// ResolverImpl