001: /***** BEGIN LICENSE BLOCK *****
002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
003: *
004: * The contents of this file are subject to the Common Public
005: * License Version 1.0 (the "License"); you may not use this file
006: * except in compliance with the License. You may obtain a copy of
007: * the License at http://www.eclipse.org/legal/cpl-v10.html
008: *
009: * Software distributed under the License is distributed on an "AS
010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
011: * implied. See the License for the specific language governing
012: * rights and limitations under the License.
013: *
014: * Copyright (C) 2007 Ola Bini <ola@ologix.com>
015: *
016: * Alternatively, the contents of this file may be used under the terms of
017: * either of the GNU General Public License Version 2 or later (the "GPL"),
018: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
019: * in which case the provisions of the GPL or the LGPL are applicable instead
020: * of those above. If you wish to allow use of your version of this file only
021: * under the terms of either the GPL or the LGPL, and not to allow others to
022: * use your version of this file under the terms of the CPL, indicate your
023: * decision by deleting the provisions above and replace them with the notice
024: * and other provisions required by the GPL or the LGPL. If you do not delete
025: * the provisions above, a recipient may use your version of this file under
026: * the terms of any one of the CPL, the GPL or the LGPL.
027: ***** END LICENSE BLOCK *****/package org.jvyamlb;
028:
029: import java.util.ArrayList;
030: import java.util.Iterator;
031: import java.util.List;
032: import java.util.LinkedList;
033: import java.util.HashMap;
034: import java.util.Map;
035:
036: import org.jvyamlb.nodes.MappingNode;
037: import org.jvyamlb.nodes.Node;
038: import org.jvyamlb.nodes.ScalarNode;
039: import org.jvyamlb.nodes.SequenceNode;
040:
041: import org.jruby.util.ByteList;
042:
043: /**
044: * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
045: */
046: public class ResolverImpl implements Resolver {
047: private final static Map yamlPathResolvers = new HashMap();
048:
049: private final static ResolverScanner SCANNER = new ResolverScanner();
050:
051: private List resolverExactPaths = new LinkedList();
052: private List resolverPrefixPaths = new LinkedList();
053:
054: public static void addPathResolver(final String tag,
055: final List path, final Class kind) {
056: final List newPath = new LinkedList();
057: Object nodeCheck = null;
058: Object indexCheck = null;
059: for (final Iterator iter = path.iterator(); iter.hasNext();) {
060: final Object element = iter.next();
061: if (element instanceof List) {
062: final List eList = (List) element;
063: if (eList.size() == 2) {
064: nodeCheck = eList.get(0);
065: indexCheck = eList.get(1);
066: } else if (eList.size() == 1) {
067: nodeCheck = eList.get(0);
068: indexCheck = Boolean.TRUE;
069: } else {
070: throw new ResolverException(
071: "Invalid path element: " + element);
072: }
073: } else {
074: nodeCheck = null;
075: indexCheck = element;
076: }
077:
078: if (nodeCheck instanceof String
079: || nodeCheck instanceof ByteList) {
080: nodeCheck = ScalarNode.class;
081: } else if (nodeCheck instanceof List) {
082: nodeCheck = SequenceNode.class;
083: } else if (nodeCheck instanceof Map) {
084: nodeCheck = MappingNode.class;
085: } else if (null != nodeCheck
086: && !ScalarNode.class.equals(nodeCheck)
087: && !SequenceNode.class.equals(nodeCheck)
088: && !MappingNode.class.equals(nodeCheck)) {
089: throw new ResolverException("Invalid node checker: "
090: + nodeCheck);
091: }
092: if (!(indexCheck instanceof String
093: || nodeCheck instanceof ByteList || indexCheck instanceof Integer)
094: && null != indexCheck) {
095: throw new ResolverException("Invalid index checker: "
096: + indexCheck);
097: }
098: newPath.add(new Object[] { nodeCheck, indexCheck });
099: }
100: Class newKind = null;
101: if (String.class.equals(kind) || ByteList.class.equals(kind)) {
102: newKind = ScalarNode.class;
103: } else if (List.class.equals(kind)) {
104: newKind = SequenceNode.class;
105: } else if (Map.class.equals(kind)) {
106: newKind = MappingNode.class;
107: } else if (kind != null && !ScalarNode.class.equals(kind)
108: && !SequenceNode.class.equals(kind)
109: && !MappingNode.class.equals(kind)) {
110: throw new ResolverException("Invalid node kind: " + kind);
111: } else {
112: newKind = kind;
113: }
114: final List x = new ArrayList(1);
115: x.add(newPath);
116: final List y = new ArrayList(2);
117: y.add(x);
118: y.add(kind);
119: yamlPathResolvers.put(y, tag);
120: }
121:
122: public void descendResolver(final Node currentNode,
123: final Object currentIndex) {
124: final Map exactPaths = new HashMap();
125: final List prefixPaths = new LinkedList();
126: if (null != currentNode) {
127: final int depth = resolverPrefixPaths.size();
128: for (final Iterator iter = ((List) resolverPrefixPaths
129: .get(0)).iterator(); iter.hasNext();) {
130: final Object[] obj = (Object[]) iter.next();
131: final List path = (List) obj[0];
132: if (checkResolverPrefix(depth, path, (Class) obj[1],
133: currentNode, currentIndex)) {
134: if (path.size() > depth) {
135: prefixPaths.add(new Object[] { path, obj[1] });
136: } else {
137: final List resPath = new ArrayList(2);
138: resPath.add(path);
139: resPath.add(obj[1]);
140: exactPaths.put(obj[1], yamlPathResolvers
141: .get(resPath));
142: }
143: }
144: }
145: } else {
146: for (final Iterator iter = yamlPathResolvers.keySet()
147: .iterator(); iter.hasNext();) {
148: final List key = (List) iter.next();
149: final List path = (List) key.get(0);
150: final Class kind = (Class) key.get(1);
151: if (null == path) {
152: exactPaths.put(kind, yamlPathResolvers.get(key));
153: } else {
154: prefixPaths.add(key);
155: }
156: }
157: }
158: resolverExactPaths.add(0, exactPaths);
159: resolverPrefixPaths.add(0, prefixPaths);
160: }
161:
162: public void ascendResolver() {
163: resolverExactPaths.remove(0);
164: resolverPrefixPaths.remove(0);
165: }
166:
167: public boolean checkResolverPrefix(final int depth,
168: final List path, final Class kind, final Node currentNode,
169: final Object currentIndex) {
170: final Object[] check = (Object[]) path.get(depth - 1);
171: final Object nodeCheck = check[0];
172: final Object indexCheck = check[1];
173: if (nodeCheck instanceof String) {
174: if (!currentNode.getTag().equals(nodeCheck)) {
175: return false;
176: }
177: } else if (null != nodeCheck) {
178: if (!((Class) nodeCheck).isInstance(currentNode)) {
179: return false;
180: }
181: }
182: if (indexCheck == Boolean.TRUE && currentIndex != null) {
183: return false;
184: }
185: if (indexCheck == Boolean.FALSE && currentIndex == null) {
186: return false;
187: }
188: if (indexCheck instanceof String) {
189: if (!(currentIndex instanceof ScalarNode && indexCheck
190: .equals(((ScalarNode) currentIndex).getValue()))) {
191: return false;
192: }
193: } else if (indexCheck instanceof ByteList) {
194: if (!(currentIndex instanceof ScalarNode && indexCheck
195: .equals(((ScalarNode) currentIndex).getValue()))) {
196: return false;
197: }
198: } else if (indexCheck instanceof Integer) {
199: if (!currentIndex.equals(indexCheck)) {
200: return false;
201: }
202: }
203: return true;
204: }
205:
206: public String resolve(final Class kind, final ByteList value,
207: final boolean[] implicit) {
208: List resolvers = null;
209: if (kind.equals(ScalarNode.class) && implicit[0]) {
210: String resolv = SCANNER.recognize(value);
211: if (resolv != null) {
212: return resolv;
213: }
214: }
215: final Map exactPaths = (Map) resolverExactPaths.get(0);
216: if (exactPaths.containsKey(kind)) {
217: return (String) exactPaths.get(kind);
218: }
219: if (exactPaths.containsKey(null)) {
220: return (String) exactPaths.get(null);
221: }
222: if (kind.equals(ScalarNode.class)) {
223: return YAML.DEFAULT_SCALAR_TAG;
224: } else if (kind.equals(SequenceNode.class)) {
225: return YAML.DEFAULT_SEQUENCE_TAG;
226: } else if (kind.equals(MappingNode.class)) {
227: return YAML.DEFAULT_MAPPING_TAG;
228: }
229: return null;
230: }
231:
232: private static ByteList s(String se) {
233: return new ByteList(se.getBytes());
234: }
235:
236: public static void main(String[] args) {
237: ByteList[] strings = {
238: s("yes"),
239: s("NO"),
240: s("booooooooooooooooooooooooooooooooooooooooooooooool"),
241: s("false"), s(""), s("~"), s("~a"), s("<<"), s("10.1"),
242: s("10000000000003435345.2324E+13"), s(".23"),
243: s(".nan"), s("null"), s("124233333333333333"),
244: s("0b030323"), s("+0b0111111011010101"),
245: s("0xaafffdf"), s("2005-05-03"), s("2005-05-03a"),
246: s(".nana"), s("2005-03-05T05:23:22"), s("="), s("= "),
247: s("=a") };
248: boolean[] implicit = new boolean[] { true, true };
249: Resolver res = new ResolverImpl();
250: res.descendResolver(null, null);
251: Class s = ScalarNode.class;
252: final long before = System.currentTimeMillis();
253: final int NUM = 100000;
254: for (int j = 0; j < NUM; j++) {
255: for (int i = 0; i < strings.length; i++) {
256: res.resolve(s, strings[i % (strings.length)], implicit);
257: }
258: }
259: final long after = System.currentTimeMillis();
260: final long time = after - before;
261: final double timeS = (after - before) / 1000.0;
262: System.out.println("Resolving " + NUM * strings.length
263: + " nodes took " + time + "ms, or " + timeS
264: + " seconds");
265: }
266: }// ResolverImpl
|