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.io.FileInputStream;
030: import java.io.InputStream;
031:
032: import java.util.Calendar;
033: import java.util.HashMap;
034: import java.util.HashSet;
035: import java.util.Iterator;
036: import java.util.List;
037: import java.util.Map;
038: import java.util.Set;
039:
040: import java.util.regex.Matcher;
041: import java.util.regex.Pattern;
042:
043: import org.jvyamlb.nodes.Node;
044:
045: import org.jvyamlb.util.Base64Coder;
046:
047: import org.jruby.util.ByteList;
048:
049: /**
050: * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
051: */
052: public class SafeConstructorImpl extends BaseConstructorImpl {
053: private final static Map yamlConstructors = new HashMap();
054: private final static Map yamlMultiConstructors = new HashMap();
055: private final static Map yamlMultiRegexps = new HashMap();
056:
057: public YamlConstructor getYamlConstructor(final Object key) {
058: YamlConstructor mine = (YamlConstructor) yamlConstructors
059: .get(key);
060: if (mine == null) {
061: mine = super .getYamlConstructor(key);
062: }
063: return mine;
064: }
065:
066: public YamlMultiConstructor getYamlMultiConstructor(final Object key) {
067: YamlMultiConstructor mine = (YamlMultiConstructor) yamlMultiConstructors
068: .get(key);
069: if (mine == null) {
070: mine = super .getYamlMultiConstructor(key);
071: }
072: return mine;
073: }
074:
075: public Pattern getYamlMultiRegexp(final Object key) {
076: Pattern mine = (Pattern) yamlMultiRegexps.get(key);
077: if (mine == null) {
078: mine = super .getYamlMultiRegexp(key);
079: }
080: return mine;
081: }
082:
083: public Set getYamlMultiRegexps() {
084: final Set all = new HashSet(super .getYamlMultiRegexps());
085: all.addAll(yamlMultiRegexps.keySet());
086: return all;
087: }
088:
089: public static void addConstructor(final String tag,
090: final YamlConstructor ctor) {
091: yamlConstructors.put(tag, ctor);
092: }
093:
094: public static void addMultiConstructor(final String tagPrefix,
095: final YamlMultiConstructor ctor) {
096: yamlMultiConstructors.put(tagPrefix, ctor);
097: yamlMultiRegexps.put(tagPrefix, Pattern
098: .compile("^" + tagPrefix));
099: }
100:
101: public SafeConstructorImpl(final Composer composer) {
102: super (composer);
103: }
104:
105: private static ByteList into(String v) {
106: return new ByteList(v.getBytes(), false);
107: }
108:
109: private final static Map BOOL_VALUES = new HashMap();
110: static {
111: BOOL_VALUES.put(into("yes"), Boolean.TRUE);
112: BOOL_VALUES.put(into("Yes"), Boolean.TRUE);
113: BOOL_VALUES.put(into("YES"), Boolean.TRUE);
114: BOOL_VALUES.put(into("no"), Boolean.FALSE);
115: BOOL_VALUES.put(into("No"), Boolean.FALSE);
116: BOOL_VALUES.put(into("NO"), Boolean.FALSE);
117: BOOL_VALUES.put(into("true"), Boolean.TRUE);
118: BOOL_VALUES.put(into("True"), Boolean.TRUE);
119: BOOL_VALUES.put(into("TRUE"), Boolean.TRUE);
120: BOOL_VALUES.put(into("false"), Boolean.FALSE);
121: BOOL_VALUES.put(into("False"), Boolean.FALSE);
122: BOOL_VALUES.put(into("FALSE"), Boolean.FALSE);
123: BOOL_VALUES.put(into("on"), Boolean.TRUE);
124: BOOL_VALUES.put(into("On"), Boolean.TRUE);
125: BOOL_VALUES.put(into("ON"), Boolean.TRUE);
126: BOOL_VALUES.put(into("off"), Boolean.FALSE);
127: BOOL_VALUES.put(into("Off"), Boolean.FALSE);
128: BOOL_VALUES.put(into("OFF"), Boolean.FALSE);
129: }
130:
131: public static Object constructYamlNull(final Constructor ctor,
132: final Node node) {
133: return null;
134: }
135:
136: public static Object constructYamlBool(final Constructor ctor,
137: final Node node) {
138: final Object val = ctor.constructScalar(node);
139: return BOOL_VALUES.get(val);
140: }
141:
142: public static Object constructYamlOmap(final Constructor ctor,
143: final Node node) {
144: return ctor.constructPairs(node);
145: }
146:
147: public static Object constructYamlPairs(final Constructor ctor,
148: final Node node) {
149: return constructYamlOmap(ctor, node);
150: }
151:
152: public static Object constructYamlSet(final Constructor ctor,
153: final Node node) {
154: return ((Map) ctor.constructMapping(node)).keySet();
155: }
156:
157: public static Object constructYamlStr(final Constructor ctor,
158: final Node node) {
159: final ByteList value = (ByteList) ctor.constructScalar(node);
160: return value.length() == 0 ? (ByteList) null : value;
161: }
162:
163: public static Object constructYamlSeq(final Constructor ctor,
164: final Node node) {
165: return ctor.constructSequence(node);
166: }
167:
168: public static Object constructYamlMap(final Constructor ctor,
169: final Node node) {
170: return ctor.constructMapping(node);
171: }
172:
173: public static Object constructUndefined(final Constructor ctor,
174: final Node node) {
175: throw new ConstructorException(null,
176: "could not determine a constructor for the tag "
177: + node.getTag(), null);
178: }
179:
180: private final static Pattern TIMESTAMP_REGEXP = Pattern
181: .compile("^([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])?)?))?)?$");
182: private final static Pattern YMD_REGEXP = Pattern
183: .compile("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)$");
184:
185: public static Object constructYamlTimestamp(final Constructor ctor,
186: final Node node) {
187: Matcher match = YMD_REGEXP.matcher(node.getValue().toString());
188: if (match.matches()) {
189: final String year_s = match.group(1);
190: final String month_s = match.group(2);
191: final String day_s = match.group(3);
192: final Calendar cal = Calendar.getInstance();
193: cal.clear();
194: if (year_s != null) {
195: cal.set(Calendar.YEAR, Integer.parseInt(year_s));
196: }
197: if (month_s != null) {
198: cal.set(Calendar.MONTH, Integer.parseInt(month_s) - 1); // Java's months are zero-based...
199: }
200: if (day_s != null) {
201: cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day_s));
202: }
203: return cal.getTime();
204: }
205: match = TIMESTAMP_REGEXP.matcher(node.getValue().toString());
206: if (!match.matches()) {
207: return ctor.constructPrivateType(node);
208: }
209: final String year_s = match.group(1);
210: final String month_s = match.group(2);
211: final String day_s = match.group(3);
212: final String hour_s = match.group(4);
213: final String min_s = match.group(5);
214: final String sec_s = match.group(6);
215: final String fract_s = match.group(7);
216: final String utc = match.group(8);
217: final String timezoneh_s = match.group(9);
218: final String timezonem_s = match.group(10);
219:
220: int usec = 0;
221: if (fract_s != null) {
222: usec = Integer.parseInt(fract_s);
223: if (usec != 0) {
224: while (10 * usec < 1000) {
225: usec *= 10;
226: }
227: }
228: }
229: final Calendar cal = Calendar.getInstance();
230: cal.clear();
231: if ("Z".equalsIgnoreCase(utc)) {
232: cal.setTimeZone(java.util.TimeZone.getTimeZone("GMT"));
233: } else {
234: if (timezoneh_s != null || timezonem_s != null) {
235: int zone = 0;
236: int sign = +1;
237: if (timezoneh_s != null) {
238: if (timezoneh_s.startsWith("-")) {
239: sign = -1;
240: }
241: zone += Integer.parseInt(timezoneh_s.substring(1)) * 3600000;
242: }
243: if (timezonem_s != null) {
244: zone += Integer.parseInt(timezonem_s) * 60000;
245: }
246: cal.setTimeZone(java.util.TimeZone.getTimeZone("GMT"));
247: cal.set(Calendar.ZONE_OFFSET, sign * zone);
248: }
249: }
250: if (year_s != null) {
251: cal.set(Calendar.YEAR, Integer.parseInt(year_s));
252: }
253: if (month_s != null) {
254: cal.set(Calendar.MONTH, Integer.parseInt(month_s) - 1); // Java's months are zero-based...
255: }
256: if (day_s != null) {
257: cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day_s));
258: }
259: if (hour_s != null) {
260: cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hour_s));
261: }
262: if (min_s != null) {
263: cal.set(Calendar.MINUTE, Integer.parseInt(min_s));
264: }
265: if (sec_s != null) {
266: cal.set(Calendar.SECOND, Integer.parseInt(sec_s));
267: }
268: cal.set(Calendar.MILLISECOND, usec / 1000);
269: return cal;
270: }
271:
272: public static Object constructYamlInt(final Constructor ctor,
273: final Node node) {
274: String value = ctor.constructScalar(node).toString()
275: .replaceAll("_", "").replaceAll(",", "");
276: ;
277: int sign = +1;
278: char first = value.charAt(0);
279: if (first == '-') {
280: sign = -1;
281: value = value.substring(1);
282: } else if (first == '+') {
283: value = value.substring(1);
284: }
285: int base = 10;
286: if (value.equals("0")) {
287: return new Long(0);
288: } else if (value.startsWith("0b")) {
289: value = value.substring(2);
290: base = 2;
291: } else if (value.startsWith("0x")) {
292: value = value.substring(2);
293: base = 16;
294: } else if (value.startsWith("0")) {
295: value = value.substring(1);
296: base = 8;
297: } else if (value.indexOf(':') != -1) {
298: final String[] digits = value.split(":");
299: int bes = 1;
300: int val = 0;
301: for (int i = 0, j = digits.length; i < j; i++) {
302: val += (Long.parseLong(digits[(j - i) - 1]) * bes);
303: bes *= 60;
304: }
305: return new Integer(sign * val);
306: } else {
307: try {
308: return new Long(sign * Long.parseLong(value));
309: } catch (Exception e) {
310: if (sign < 0) {
311: return new java.math.BigInteger(value).negate();
312: } else {
313: return new java.math.BigInteger(value);
314: }
315: }
316: }
317: try {
318: return new Long(sign * Long.parseLong(value, base));
319: } catch (Exception e) {
320: if (sign < 0) {
321: return new java.math.BigInteger(value, base).negate();
322: } else {
323: return new java.math.BigInteger(value, base);
324: }
325: }
326: }
327:
328: private final static Double INF_VALUE_POS = new Double(
329: Double.POSITIVE_INFINITY);
330: private final static Double INF_VALUE_NEG = new Double(
331: Double.NEGATIVE_INFINITY);
332: private final static Double NAN_VALUE = new Double(Double.NaN);
333:
334: public static Object constructYamlFloat(final Constructor ctor,
335: final Node node) {
336: String value = ctor.constructScalar(node).toString()
337: .replaceAll("_", "").replaceAll(",", "");
338: int sign = +1;
339: char first = value.charAt(0);
340: if (first == '-') {
341: sign = -1;
342: value = value.substring(1);
343: } else if (first == '+') {
344: value = value.substring(1);
345: }
346: final String valLower = value.toLowerCase();
347: if (valLower.equals(".inf")) {
348: return sign == -1 ? INF_VALUE_NEG : INF_VALUE_POS;
349: } else if (valLower.equals(".nan")) {
350: return NAN_VALUE;
351: } else if (value.indexOf(':') != -1) {
352: final String[] digits = value.split(":");
353: int bes = 1;
354: double val = 0.0;
355: for (int i = 0, j = digits.length; i < j; i++) {
356: val += (Double.parseDouble(digits[(j - i) - 1]) * bes);
357: bes *= 60;
358: }
359: return new Double(sign * val);
360: } else {
361: return Double.valueOf(value);
362: }
363: }
364:
365: public static Object constructYamlBinary(final Constructor ctor,
366: final Node node) {
367: final String[] values = ctor.constructScalar(node).toString()
368: .split("[\n\u0085]|(?:\r[^\n])");
369: final StringBuffer vals = new StringBuffer();
370: for (int i = 0, j = values.length; i < j; i++) {
371: vals.append(values[i].trim());
372: }
373: return Base64Coder.decode(ByteList.plain(vals));
374: }
375:
376: public static Object constructSpecializedSequence(
377: final Constructor ctor, final String pref, final Node node) {
378: List outp = null;
379: try {
380: final Class seqClass = Class.forName(pref);
381: outp = (List) seqClass.newInstance();
382: } catch (final Exception e) {
383: throw new YAMLException(
384: "Can't construct a sequence from class " + pref
385: + ": " + e.toString());
386: }
387: outp.addAll((List) ctor.constructSequence(node));
388: return outp;
389: }
390:
391: public static Object constructSpecializedMap(
392: final Constructor ctor, final String pref, final Node node) {
393: Map outp = null;
394: try {
395: final Class mapClass = Class.forName(pref);
396: outp = (Map) mapClass.newInstance();
397: } catch (final Exception e) {
398: throw new YAMLException(
399: "Can't construct a mapping from class " + pref
400: + ": " + e.toString());
401: }
402: outp.putAll((Map) ctor.constructMapping(node));
403: return outp;
404: }
405:
406: private static Object fixValue(final Object inp, final Class outp) {
407: if (inp == null) {
408: return null;
409: }
410: final Class inClass = inp.getClass();
411: if (outp.isAssignableFrom(inClass)) {
412: return inp;
413: }
414: if (inClass == Long.class
415: && (outp == Integer.class || outp == Integer.TYPE)) {
416: return new Integer(((Long) inp).intValue());
417: }
418: if (inClass == Long.class
419: && (outp == Short.class || outp == Short.TYPE)) {
420: return new Short((short) ((Long) inp).intValue());
421: }
422: if (inClass == Long.class
423: && (outp == Character.class || outp == Character.TYPE)) {
424: return new Character((char) ((Long) inp).intValue());
425: }
426: if (inClass == Double.class
427: && (outp == Float.class || outp == Float.TYPE)) {
428: return new Float(((Double) inp).floatValue());
429: }
430: return inp;
431: }
432:
433: public static Object constructJava(final Constructor ctor,
434: final String pref, final Node node) {
435: Object outp = null;
436: try {
437: final Class cl = Class.forName(pref);
438: outp = cl.newInstance();
439: final Map values = (Map) ctor.constructMapping(node);
440: java.lang.reflect.Method[] ems = cl.getMethods();
441: for (final Iterator iter = values.keySet().iterator(); iter
442: .hasNext();) {
443: final Object key = iter.next();
444: final Object value = values.get(key);
445: final String keyName = key.toString();
446: final String mName = "set"
447: + Character.toUpperCase(keyName.charAt(0))
448: + keyName.substring(1);
449: for (int i = 0, j = ems.length; i < j; i++) {
450: if (ems[i].getName().equals(mName)
451: && ems[i].getParameterTypes().length == 1) {
452: ems[i]
453: .invoke(
454: outp,
455: new Object[] { fixValue(
456: value,
457: ems[i]
458: .getParameterTypes()[0]) });
459: break;
460: }
461: }
462: }
463: } catch (final Exception e) {
464: throw new YAMLException(
465: "Can't construct a java object from class " + pref
466: + ": " + e.toString());
467: }
468: return outp;
469: }
470:
471: static {
472: BaseConstructorImpl.addConstructor("tag:yaml.org,2002:null",
473: new YamlConstructor() {
474: public Object call(final Constructor self,
475: final Node node) {
476: return constructYamlNull(self, node);
477: }
478: });
479: addConstructor("tag:yaml.org,2002:bool", new YamlConstructor() {
480: public Object call(final Constructor self, final Node node) {
481: return constructYamlBool(self, node);
482: }
483: });
484: addConstructor("tag:yaml.org,2002:omap", new YamlConstructor() {
485: public Object call(final Constructor self, final Node node) {
486: return constructYamlOmap(self, node);
487: }
488: });
489: addConstructor("tag:yaml.org,2002:pairs",
490: new YamlConstructor() {
491: public Object call(final Constructor self,
492: final Node node) {
493: return constructYamlPairs(self, node);
494: }
495: });
496: addConstructor("tag:yaml.org,2002:set", new YamlConstructor() {
497: public Object call(final Constructor self, final Node node) {
498: return constructYamlSet(self, node);
499: }
500: });
501: addConstructor("tag:yaml.org,2002:int", new YamlConstructor() {
502: public Object call(final Constructor self, final Node node) {
503: return constructYamlInt(self, node);
504: }
505: });
506: addConstructor("tag:yaml.org,2002:float",
507: new YamlConstructor() {
508: public Object call(final Constructor self,
509: final Node node) {
510: return constructYamlFloat(self, node);
511: }
512: });
513: addConstructor("tag:yaml.org,2002:timestamp",
514: new YamlConstructor() {
515: public Object call(final Constructor self,
516: final Node node) {
517: return constructYamlTimestamp(self, node);
518: }
519: });
520: addConstructor("tag:yaml.org,2002:timestamp#ymd",
521: new YamlConstructor() {
522: public Object call(final Constructor self,
523: final Node node) {
524: return constructYamlTimestamp(self, node);
525: }
526: });
527: addConstructor("tag:yaml.org,2002:str", new YamlConstructor() {
528: public Object call(final Constructor self, final Node node) {
529: return constructYamlStr(self, node);
530: }
531: });
532: addConstructor("tag:yaml.org,2002:binary",
533: new YamlConstructor() {
534: public Object call(final Constructor self,
535: final Node node) {
536: return constructYamlBinary(self, node);
537: }
538: });
539: addConstructor("tag:yaml.org,2002:seq", new YamlConstructor() {
540: public Object call(final Constructor self, final Node node) {
541: return constructYamlSeq(self, node);
542: }
543: });
544: addConstructor("tag:yaml.org,2002:map", new YamlConstructor() {
545: public Object call(final Constructor self, final Node node) {
546: return constructYamlMap(self, node);
547: }
548: });
549: addConstructor(null, new YamlConstructor() {
550: public Object call(final Constructor self, final Node node) {
551: return self.constructPrivateType(node);
552: }
553: });
554:
555: addMultiConstructor("tag:yaml.org,2002:seq:",
556: new YamlMultiConstructor() {
557: public Object call(final Constructor self,
558: final String pref, final Node node) {
559: return constructSpecializedSequence(self, pref,
560: node);
561: }
562: });
563: addMultiConstructor("tag:yaml.org,2002:map:",
564: new YamlMultiConstructor() {
565: public Object call(final Constructor self,
566: final String pref, final Node node) {
567: return constructSpecializedMap(self, pref, node);
568: }
569: });
570: addMultiConstructor("!java/object:",
571: new YamlMultiConstructor() {
572: public Object call(final Constructor self,
573: final String pref, final Node node) {
574: return constructJava(self, pref, node);
575: }
576: });
577: addMultiConstructor("tag:java.yaml.org,2002:object:",
578: new YamlMultiConstructor() {
579: public Object call(final Constructor self,
580: final String pref, final Node node) {
581: return constructJava(self, pref, node);
582: }
583: });
584: }
585:
586: public static void main(final String[] args) throws Exception {
587: final String filename = args[0];
588: System.out.println("Reading of file: \"" + filename + "\"");
589:
590: final ByteList input = new ByteList(1024);
591: final InputStream reader = new FileInputStream(filename);
592: byte[] buff = new byte[1024];
593: int read = 0;
594: while (true) {
595: read = reader.read(buff);
596: input.append(buff, 0, read);
597: if (read < 1024) {
598: break;
599: }
600: }
601: reader.close();
602: final long before = System.currentTimeMillis();
603: for (int i = 0; i < 1; i++) {
604: final Constructor ctor = new SafeConstructorImpl(
605: new ComposerImpl(new ParserImpl(new ScannerImpl(
606: input)), new ResolverImpl()));
607: for (final Iterator iter = ctor.eachDocument(); iter
608: .hasNext();) {
609: iter.next();
610: //System.out.println(iter.next());
611: }
612: }
613: final long after = System.currentTimeMillis();
614: final long time = after - before;
615: final double timeS = (after - before) / 1000.0;
616: System.out.println("Walking through the nodes for the file: "
617: + filename + " took " + time + "ms, or " + timeS
618: + " seconds");
619: }
620: }// SafeConstructorImpl
|