/* 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: SafeConstructorImpl.java,v 1.4 2006/09/30 14:13:35 olabini Exp $ */ package org.jvyaml; import java.util.Calendar; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jvyaml.nodes.Node; import org.jvyaml.util.Base64Coder; /** * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a> * @version $Revision: 1.4 $ */ public class SafeConstructorImpl extends BaseConstructorImpl { 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) { YamlConstructor mine = (YamlConstructor)yamlConstructors.get(key); if(mine == null) { mine = super.getYamlConstructor(key); } return mine; } public YamlMultiConstructor getYamlMultiConstructor(final Object key) { YamlMultiConstructor mine = (YamlMultiConstructor)yamlMultiConstructors.get(key); if(mine == null) { mine = super.getYamlMultiConstructor(key); } return mine; } public Pattern getYamlMultiRegexp(final Object key) { Pattern mine = (Pattern)yamlMultiRegexps.get(key); if(mine == null) { mine = super.getYamlMultiRegexp(key); } return mine; } public Set getYamlMultiRegexps() { final Set all = new HashSet(super.getYamlMultiRegexps()); all.addAll(yamlMultiRegexps.keySet()); return all; } 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)); } public SafeConstructorImpl(final Composer composer) { super(composer); } private final static Map BOOL_VALUES = new HashMap(); static { // BOOL_VALUES.put("y",Boolean.TRUE); // BOOL_VALUES.put("n",Boolean.FALSE); BOOL_VALUES.put("yes",Boolean.TRUE); BOOL_VALUES.put("no",Boolean.FALSE); BOOL_VALUES.put("true",Boolean.TRUE); BOOL_VALUES.put("false",Boolean.FALSE); BOOL_VALUES.put("on",Boolean.TRUE); BOOL_VALUES.put("off",Boolean.FALSE); } public static Object constructYamlNull(final Constructor ctor, final Node node) { return null; } public static Object constructYamlBool(final Constructor ctor, final Node node) { final String val = (String)ctor.constructScalar(node); return BOOL_VALUES.get(val.toLowerCase()); } public static Object constructYamlOmap(final Constructor ctor, final Node node) { return ctor.constructPairs(node); } public static Object constructYamlPairs(final Constructor ctor, final Node node) { return constructYamlOmap(ctor,node); } public static Object constructYamlSet(final Constructor ctor, final Node node) { return ((Map)ctor.constructMapping(node)).keySet(); } public static Object constructYamlStr(final Constructor ctor, final Node node) { final String value = (String)ctor.constructScalar(node); return value.length() == 0 ? (String)null : value; } public static Object constructYamlSeq(final Constructor ctor, final Node node) { return ctor.constructSequence(node); } public static Object constructYamlMap(final Constructor ctor, final Node node) { return ctor.constructMapping(node); } public static Object constructUndefined(final Constructor ctor, final Node node) { throw new ConstructorException(null,"could not determine a constructor for the tag " + node.getTag(),null); } private final static Pattern TIMESTAMP_REGEXP = Pattern.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])?)?))?)?$"); private final static Pattern YMD_REGEXP = Pattern.compile("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)$"); public static Object constructYamlTimestamp(final Constructor ctor, final Node node) { Matcher match = YMD_REGEXP.matcher((String)node.getValue()); if(match.matches()) { final String year_s = match.group(1); final String month_s = match.group(2); final String day_s = match.group(3); final Calendar cal = Calendar.getInstance(); cal.clear(); if(year_s != null) { cal.set(Calendar.YEAR,Integer.parseInt(year_s)); } if(month_s != null) { cal.set(Calendar.MONTH,Integer.parseInt(month_s)-1); // Java's months are zero-based... } if(day_s != null) { cal.set(Calendar.DAY_OF_MONTH,Integer.parseInt(day_s)); } return cal.getTime(); } match = TIMESTAMP_REGEXP.matcher((String)node.getValue()); if(!match.matches()) { return ctor.constructPrivateType(node); } final String year_s = match.group(1); final String month_s = match.group(2); final String day_s = match.group(3); final String hour_s = match.group(4); final String min_s = match.group(5); final String sec_s = match.group(6); final String fract_s = match.group(7); final String timezoneh_s = match.group(8); final String timezonem_s = match.group(9); int usec = 0; if(fract_s != null) { usec = Integer.parseInt(fract_s); if(usec != 0) { while(10*usec < 1000) { usec *= 10; } } } final Calendar cal = Calendar.getInstance(); if(year_s != null) { cal.set(Calendar.YEAR,Integer.parseInt(year_s)); } if(month_s != null) { cal.set(Calendar.MONTH,Integer.parseInt(month_s)-1); // Java's months are zero-based... } if(day_s != null) { cal.set(Calendar.DAY_OF_MONTH,Integer.parseInt(day_s)); } if(hour_s != null) { cal.set(Calendar.HOUR_OF_DAY,Integer.parseInt(hour_s)); } if(min_s != null) { cal.set(Calendar.MINUTE,Integer.parseInt(min_s)); } if(sec_s != null) { cal.set(Calendar.SECOND,Integer.parseInt(sec_s)); } cal.set(Calendar.MILLISECOND,usec); if(timezoneh_s != null || timezonem_s != null) { int zone = 0; int sign = +1; if(timezoneh_s != null) { if(timezoneh_s.startsWith("-")) { sign = -1; } zone += Integer.parseInt(timezoneh_s.substring(1))*3600000; } if(timezonem_s != null) { zone += Integer.parseInt(timezonem_s)*60000; } cal.set(Calendar.ZONE_OFFSET,sign*zone); } return cal.getTime(); } public static Object constructYamlInt(final Constructor ctor, final Node node) { String value = ctor.constructScalar(node).toString().replaceAll("_",""); int sign = +1; char first = value.charAt(0); if(first == '-') { sign = -1; value = value.substring(1); } else if(first == '+') { value = value.substring(1); } int base = 10; if(value.equals("0")) { return new Long(0); } else if(value.startsWith("0b")) { value = value.substring(2); base = 2; } else if(value.startsWith("0x")) { value = value.substring(2); base = 16; } else if(value.startsWith("0")) { value = value.substring(1); base = 8; } else if(value.indexOf(':') != -1) { final String[] digits = value.split(":"); int bes = 1; int val = 0; for(int i=0,j=digits.length;i<j;i++) { val += (Long.parseLong(digits[(j-i)-1])*bes); bes *= 60; } return new Integer(sign*val); } else { return new Long(sign * Long.parseLong(value)); } return new Long(sign * Long.parseLong(value,base)); } private final static Double INF_VALUE_POS = new Double(Double.POSITIVE_INFINITY); private final static Double INF_VALUE_NEG = new Double(Double.NEGATIVE_INFINITY); private final static Double NAN_VALUE = new Double(Double.NaN); public static Object constructYamlFloat(final Constructor ctor, final Node node) { String value = ctor.constructScalar(node).toString().replaceAll("_",""); int sign = +1; char first = value.charAt(0); if(first == '-') { sign = -1; value = value.substring(1); } else if(first == '+') { value = value.substring(1); } final String valLower = value.toLowerCase(); if(valLower.equals(".inf")) { return sign == -1 ? INF_VALUE_NEG : INF_VALUE_POS; } else if(valLower.equals(".nan")) { return NAN_VALUE; } else if(value.indexOf(':') != -1) { final String[] digits = value.split(":"); int bes = 1; double val = 0.0; for(int i=0,j=digits.length;i<j;i++) { val += (Double.parseDouble(digits[(j-i)-1])*bes); bes *= 60; } return new Double(sign*val); } else { return Double.valueOf(value); } } public static Object constructYamlBinary(final Constructor ctor, final Node node) { final String[] values = ctor.constructScalar(node).toString().split("[\n\u0085]|(?:\r[^\n])"); final StringBuffer vals = new StringBuffer(); for(int i=0,j=values.length;i<j;i++) { vals.append(values[i].trim()); } return Base64Coder.decode(vals.toString()); } public static Object constructSpecializedSequence(final Constructor ctor, final String pref, final Node node) { List outp = null; try { final Class seqClass = Class.forName(pref); outp = (List)seqClass.newInstance(); } catch(final Exception e) { throw new YAMLException("Can't construct a sequence from class " + pref + ": " + e.toString()); } outp.addAll((List)ctor.constructSequence(node)); return outp; } public static Object constructSpecializedMap(final Constructor ctor, final String pref, final Node node) { Map outp = null; try { final Class mapClass = Class.forName(pref); outp = (Map)mapClass.newInstance(); } catch(final Exception e) { throw new YAMLException("Can't construct a mapping from class " + pref + ": " + e.toString()); } outp.putAll((Map)ctor.constructMapping(node)); return outp; } private static Object fixValue(final Object inp, final Class outp) { if(inp == null) { return null; } final Class inClass = inp.getClass(); if(outp.isAssignableFrom(inClass)) { return inp; } if(inClass == Long.class && (outp == Integer.class || outp == Integer.TYPE)) { return new Integer(((Long)inp).intValue()); } if(inClass == Long.class && (outp == Short.class || outp == Short.TYPE)) { return new Short((short)((Long)inp).intValue()); } if(inClass == Long.class && (outp == Character.class || outp == Character.TYPE)) { return new Character((char)((Long)inp).intValue()); } if(inClass == Double.class && (outp == Float.class || outp == Float.TYPE)) { return new Float(((Double)inp).floatValue()); } return inp; } public static Object constructJava(final Constructor ctor, final String pref, final Node node) { Object outp = null; try { final Class cl = Class.forName(pref); outp = cl.newInstance(); final Map values = (Map)ctor.constructMapping(node); java.lang.reflect.Method[] ems = cl.getMethods(); for(final Iterator iter = values.keySet().iterator();iter.hasNext();) { final Object key = iter.next(); final Object value = values.get(key); final String keyName = key.toString(); final String mName = "set" + Character.toUpperCase(keyName.charAt(0)) + keyName.substring(1); for(int i=0,j=ems.length;i<j;i++) { if(ems[i].getName().equals(mName) && ems[i].getParameterTypes().length == 1) { ems[i].invoke(outp, new Object[]{fixValue(value, ems[i].getParameterTypes()[0])}); break; } } } } catch(final Exception e) { throw new YAMLException("Can't construct a java object from class " + pref + ": " + e.toString()); } return outp; } static { BaseConstructorImpl.addConstructor("tag:yaml.org,2002:null",new YamlConstructor() { public Object call(final Constructor self, final Node node) { return constructYamlNull(self,node); } }); addConstructor("tag:yaml.org,2002:bool",new YamlConstructor() { public Object call(final Constructor self, final Node node) { return constructYamlBool(self,node); } }); addConstructor("tag:yaml.org,2002:omap",new YamlConstructor() { public Object call(final Constructor self, final Node node) { return constructYamlOmap(self,node); } }); addConstructor("tag:yaml.org,2002:pairs",new YamlConstructor() { public Object call(final Constructor self, final Node node) { return constructYamlPairs(self,node); } }); addConstructor("tag:yaml.org,2002:set",new YamlConstructor() { public Object call(final Constructor self, final Node node) { return constructYamlSet(self,node); } }); addConstructor("tag:yaml.org,2002:int",new YamlConstructor() { public Object call(final Constructor self, final Node node) { return constructYamlInt(self,node); } }); addConstructor("tag:yaml.org,2002:float",new YamlConstructor() { public Object call(final Constructor self, final Node node) { return constructYamlFloat(self,node); } }); addConstructor("tag:yaml.org,2002:timestamp",new YamlConstructor() { public Object call(final Constructor self, final Node node) { return constructYamlTimestamp(self,node); } }); addConstructor("tag:yaml.org,2002:timestamp#ymd",new YamlConstructor() { public Object call(final Constructor self, final Node node) { return constructYamlTimestamp(self,node); } }); addConstructor("tag:yaml.org,2002:str",new YamlConstructor() { public Object call(final Constructor self, final Node node) { return constructYamlStr(self,node); } }); addConstructor("tag:yaml.org,2002:binary",new YamlConstructor() { public Object call(final Constructor self, final Node node) { return constructYamlBinary(self,node); } }); addConstructor("tag:yaml.org,2002:seq",new YamlConstructor() { public Object call(final Constructor self, final Node node) { return constructYamlSeq(self,node); } }); addConstructor("tag:yaml.org,2002:map",new YamlConstructor() { public Object call(final Constructor self, final Node node) { return constructYamlMap(self,node); } }); addConstructor(null,new YamlConstructor() { public Object call(final Constructor self, final Node node) { return self.constructPrivateType(node); } }); addMultiConstructor("tag:yaml.org,2002:seq:",new YamlMultiConstructor() { public Object call(final Constructor self, final String pref, final Node node) { return constructSpecializedSequence(self,pref,node); } }); addMultiConstructor("tag:yaml.org,2002:map:",new YamlMultiConstructor() { public Object call(final Constructor self, final String pref, final Node node) { return constructSpecializedMap(self,pref,node); } }); addMultiConstructor("!java/object:",new YamlMultiConstructor() { public Object call(final Constructor self, final String pref, final Node node) { return constructJava(self,pref,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 SafeConstructorImpl(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"); } }// SafeConstructorImpl