/******************************************************************************* * Copyright (c) 2009 MATERNA Information & Communications. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html. For further * project-related information visit http://www.ws4d.org. The most recent * version of the JMEDS framework can be obtained from * http://sourceforge.net/projects/ws4d-javame. ******************************************************************************/ package org.ws4d.java.schema; import java.io.IOException; import java.io.InputStream; import org.ws4d.java.DPWSFramework; import org.ws4d.java.communication.TimeoutException; import org.ws4d.java.communication.monitor.ResourceLoader; import org.ws4d.java.constants.SchemaConstants; import org.ws4d.java.io.xml.ElementParser; import org.ws4d.java.service.Fault; import org.ws4d.java.service.OperationCommons; import org.ws4d.java.service.OperationDescription; import org.ws4d.java.service.Service; import org.ws4d.java.structures.HashMap; import org.ws4d.java.structures.Iterator; import org.ws4d.java.types.QName; import org.ws4d.java.types.URI; import org.ws4d.java.util.Log; import org.ws4d.java.util.StringUtil; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; /** * Utility class for XML Schema. */ public final class SchemaUtil implements SchemaConstants { public static final String TYPE_ANYTYPE = SCHEMA_STYPES[44]; public static final String TYPE_ANYSIMPLETYPE = SCHEMA_STYPES[45]; public static final String TYPE_STRING = SCHEMA_STYPES[0]; public static final String TYPE_NORMALIZED_STRING = SCHEMA_STYPES[1]; public static final String TYPE_TOKEN = SCHEMA_STYPES[2]; public static final String TYPE_BASE64_BINARY = SCHEMA_STYPES[3]; public static final String TYPE_HEX_BINARY = SCHEMA_STYPES[4]; public static final String TYPE_INTEGER = SCHEMA_STYPES[5]; public static final String TYPE_POSITIVE_INTEGER = SCHEMA_STYPES[6]; public static final String TYPE_NEGATIVE_INTEGER = SCHEMA_STYPES[7]; public static final String TYPE_NON_NEGATIVE_INTEGER = SCHEMA_STYPES[8]; public static final String TYPE_NON_POSITIVE_INTEGER = SCHEMA_STYPES[9]; public static final String TYPE_LONG = SCHEMA_STYPES[10]; public static final String TYPE_UNSIGNED_LONG = SCHEMA_STYPES[11]; public static final String TYPE_INT = SCHEMA_STYPES[12]; public static final String TYPE_UNSIGNED_INT = SCHEMA_STYPES[13]; public static final String TYPE_SHORT = SCHEMA_STYPES[14]; public static final String TYPE_UNSIGNED_SHORT = SCHEMA_STYPES[15]; public static final String TYPE_BYTE = SCHEMA_STYPES[16]; public static final String TYPE_UNSIGNED_BYTE = SCHEMA_STYPES[17]; public static final String TYPE_DECIMAL = SCHEMA_STYPES[18]; public static final String TYPE_FLOAT = SCHEMA_STYPES[19]; public static final String TYPE_DOUBLE = SCHEMA_STYPES[20]; public static final String TYPE_BOOLEAN = SCHEMA_STYPES[21]; public static final String TYPE_DURATION = SCHEMA_STYPES[22]; public static final String TYPE_DATE_TIME = SCHEMA_STYPES[23]; public static final String TYPE_DATE = SCHEMA_STYPES[24]; public static final String TYPE_TIME = SCHEMA_STYPES[25]; public static final String TYPE_G_YEAR = SCHEMA_STYPES[26]; public static final String TYPE_G_YEARMONTH = SCHEMA_STYPES[27]; public static final String TYPE_G_MONTH = SCHEMA_STYPES[28]; public static final String TYPE_G_MONTH_DAY = SCHEMA_STYPES[29]; public static final String TYPE_G_DAY = SCHEMA_STYPES[30]; public static final String TYPE_NAME = SCHEMA_STYPES[31]; public static final String TYPE_QNAME = SCHEMA_STYPES[32]; public static final String TYPE_NCNAME = SCHEMA_STYPES[33]; public static final String TYPE_ANYURI = SCHEMA_STYPES[34]; public static final String TYPE_LANGUAGE = SCHEMA_STYPES[35]; public static final String TYPE_ID = SCHEMA_STYPES[36]; public static final String TYPE_IDREF = SCHEMA_STYPES[37]; public static final String TYPE_IDREFS = SCHEMA_STYPES[38]; public static final String TYPE_ENTITY = SCHEMA_STYPES[39]; public static final String TYPE_ENTITIES = SCHEMA_STYPES[40]; public static final String TYPE_NOTATION = SCHEMA_STYPES[41]; public static final String TYPE_NMTOKEN = SCHEMA_STYPES[42]; public static final String TYPE_NMTOKENS = SCHEMA_STYPES[43]; private static final String[] NATIVES = { TYPE_ANYTYPE, TYPE_ANYSIMPLETYPE, TYPE_STRING, TYPE_NORMALIZED_STRING, TYPE_TOKEN, TYPE_BYTE, TYPE_UNSIGNED_BYTE, TYPE_BASE64_BINARY, TYPE_HEX_BINARY, TYPE_INTEGER, TYPE_POSITIVE_INTEGER, TYPE_NEGATIVE_INTEGER, TYPE_NON_NEGATIVE_INTEGER, TYPE_NON_POSITIVE_INTEGER, TYPE_INT, TYPE_UNSIGNED_INT, TYPE_LONG, TYPE_UNSIGNED_LONG, TYPE_SHORT, TYPE_UNSIGNED_SHORT, TYPE_DECIMAL, TYPE_FLOAT, TYPE_DOUBLE, TYPE_BOOLEAN, TYPE_TIME, TYPE_DATE_TIME, TYPE_DURATION, TYPE_DATE, TYPE_G_MONTH, TYPE_G_YEAR, TYPE_G_YEARMONTH, TYPE_G_DAY, TYPE_G_MONTH_DAY, TYPE_NAME, TYPE_QNAME, TYPE_NCNAME, TYPE_ANYURI, TYPE_LANGUAGE, TYPE_ID, TYPE_IDREF, TYPE_IDREFS, TYPE_ENTITY, TYPE_ENTITIES, TYPE_NOTATION, TYPE_NMTOKEN, TYPE_NMTOKENS }; private static final String[] BINARY = { TYPE_HEX_BINARY, TYPE_BASE64_BINARY }; public static final long MILLIS_PER_SECOND = 1000L; public static final long MILLIS_PER_MINUTE = 60L * MILLIS_PER_SECOND; public static final long MILLIS_PER_HOUR = 60L * MILLIS_PER_MINUTE; public static final long MILLIS_PER_DAY = 24L * MILLIS_PER_HOUR; public static final long MILLIS_PER_MONTH = 30L * MILLIS_PER_DAY; public static final long MILLIS_PER_YEAR = 365L * MILLIS_PER_DAY; private static HashMap nativeTypes = null; private SchemaUtil() { } /** * Parses duration strings specified in schema. * * @param duration Duration to parse * @return Millis since 1rst of January since 1970 */ public static long parseDuration(String duration) { if (duration == null) { return 0L; } // PnYnMnDTnHnMnS long result = 0L; // we don't support negative durations // long sign = 1L; long multiplier = 1L; boolean time = false; String number = ""; int len = duration.length(); for (int i = 0; i < len; i++) { char c = duration.charAt(i); switch (c) { // we don't support negative durations // case ('-'): { // sign = -1L; // continue; // } case ('P'): { continue; } case ('Y'): { multiplier = MILLIS_PER_YEAR; break; } case ('M'): { multiplier = time ? MILLIS_PER_MINUTE : MILLIS_PER_MONTH; break; } case ('D'): { multiplier = MILLIS_PER_DAY; break; } case ('T'): { time = true; continue; } case ('H'): { multiplier = MILLIS_PER_HOUR; break; } case ('S'): { multiplier = MILLIS_PER_SECOND; break; } default: { // must be a number number += c; continue; } } try { result += Long.parseLong(number.trim()) * multiplier; } catch (NumberFormatException e) { return -1L; } number = ""; } return /* sign * */result; } /** * Creates duration string by millis since 1rst of January 1970. * * @param millis Millis to * @return Duration string specified in schema */ public static String createDuration(long millis) { StringBuffer sb = new StringBuffer(); if (millis < 0L) { sb.append('-'); } sb.append('P'); if (millis > MILLIS_PER_YEAR) { sb.append(millis / MILLIS_PER_YEAR).append('Y'); millis = millis % MILLIS_PER_YEAR; } if (millis > MILLIS_PER_MONTH) { sb.append(millis / MILLIS_PER_MONTH).append('M'); millis = millis % MILLIS_PER_MONTH; } if (millis > MILLIS_PER_DAY) { sb.append(millis / MILLIS_PER_DAY).append('D'); millis = millis % MILLIS_PER_DAY; } if (millis > 0L) { sb.append('T'); } if (millis > MILLIS_PER_HOUR) { sb.append(millis / MILLIS_PER_HOUR).append('H'); millis = millis % MILLIS_PER_HOUR; } if (millis > MILLIS_PER_MINUTE) { sb.append(millis / MILLIS_PER_MINUTE).append('M'); millis = millis % MILLIS_PER_MINUTE; } if (millis >= MILLIS_PER_SECOND) { sb.append(millis / MILLIS_PER_SECOND).append('S'); // we don't support fractions of a second // millis = millis % MILLIS_PER_SECOND; } return sb.toString(); } /** * Returns the XML schema type for the given qualified name. * <p> * This method will return types from the XML schema namespace ( * {@link Schema#XMLSCHEMA_NAMESPACE} ) only! * </p> * * @param name the qualified name of the XML schema type. * @return the type. */ public static synchronized Type getType(QName name) { if (nativeTypes == null) { initNatives(); } return (Type) nativeTypes.get(name); } /** * Returns the XML schema type for the given name. * <p> * This method will return types from the XML schema namespace ( * {@link Schema#XMLSCHEMA_NAMESPACE} ) only! * </p> * <p> * For instance this method will return a object representing the XML string * (xs:string) type if the name is "string". * </p> * * @param name the name of the XML schema type. * @return the type. */ public static Type getSchemaType(String name) { return getType(new QName(name, XMLSCHEMA_NAMESPACE)); } /** * Returns <code>true</code> if the given name matches a binary type from * the XML schema, <code>false</code> otherwise. * * @param name the type name to check. * @return <code>true</code> if the given name matches a binary type from * the XML schema, <code>false</code> otherwise. */ public static boolean isBinaryType(String name) { for (int i = 0; i < BINARY.length; i++) if (BINARY[i].equals(name)) return true; return false; } public static String getPrefix(String prefixedString) { int p = prefixedString.indexOf(":"); if (p == -1) return null; return prefixedString.substring(0, p); } public static String getPrefixedName(XmlSerializer serializer, QName name) { if (name == null) return ""; String prefix = serializer.getPrefix(name.getNamespace(), false); return prefix + ":" + name.getLocalPart(); } public static String getName(String prefixedString) { int p = prefixedString.indexOf(":"); if (p == -1) return prefixedString; return prefixedString.substring(p + 1, prefixedString.length()); } private static void addElementMap(Element elem, HashMap map) { if (elem != null) { Schema schema = null; String namespace = elem.getName().getNamespace(); if (map.containsKey(namespace)) { schema = (Schema) map.get(namespace); } else { schema = new Schema(namespace); map.put(elem.getName().getNamespace(), schema); } schema.addElement(elem); } } private static void addTypeMap(Type type, HashMap map) { if (type != null) { Schema schema = null; String namespace = type.getName().getNamespace(); if (map.containsKey(namespace)) { schema = (Schema) map.get(namespace); } else { schema = new Schema(namespace); map.put(type.getName().getNamespace(), schema); } schema.addType(type); } } public static void addToSchemaMap(Iterator iterator, HashMap map) { while (iterator.hasNext()) { OperationCommons op = (OperationCommons) iterator.next(); Element input = op.getInput(); addElementMap(input, map); Element output = op.getOutput(); addElementMap(output, map); for (Iterator it2 = op.getFaults(); it2.hasNext();) { Fault fault = (Fault) it2.next(); Element faultElement = fault.getElement(); addElementMap(faultElement, map); } for (Iterator it3 = op.getCustomComplexTypes(); it3.hasNext();) { ComplexType customType = (ComplexType) it3.next(); addTypeMap(customType, map); } } } public static HashMap createSchema(Service service, String targetNamespace) { HashMap map = new HashMap(); addToSchemaMap(service.getOperations(), map); addToSchemaMap(service.getEventSources(), map); return map; } public static Schema createSchema(Service service) { Schema schema = new Schema(); String namespace = null; try { if (service.getParentDeviceReference() != null) { namespace = service.getParentDeviceReference().getDevice().getDefaultNamespace(); } } catch (TimeoutException e1) {} addToSchema(service.getOperations(), schema, namespace); addToSchema(service.getEventSources(), schema, namespace); try { schema.resolveSchema(); } catch (SchemaException e) { Log.printStackTrace(e); } return schema; } static Type getAnyType() { return getType(new QName(TYPE_ANYTYPE, XMLSCHEMA_NAMESPACE)); } static Type getAnySimpleType() { return getType(new QName(TYPE_ANYSIMPLETYPE, XMLSCHEMA_NAMESPACE)); } static Schema includeOrImportSchema(ElementParser parser, URI location, boolean loadReferencedFiles) throws XmlPullParserException, IOException, SchemaException { Schema s = null; ResourceLoader rl = DPWSFramework.getResourceAsStream(location); InputStream in = rl.getInputStream(); if (in == null) throw new IOException("Cannot include. Unable to access location " + location); s = Schema.parse(in, location, loadReferencedFiles); in.close(); int d = parser.getDepth(); while (parser.nextTag() != XmlPullParser.END_TAG && parser.getDepth() == d + 1) { handleUnkownTags(parser); } return s; } static void handleUnkownTags(ElementParser parser) throws XmlPullParserException, IOException { /* * eat every unknown tag, to move the parser to next nice one. ;) */ int i = parser.getDepth(); int e = parser.getEventType(); while (e != XmlPullParser.END_TAG && e != XmlPullParser.END_DOCUMENT && parser.getDepth() >= i) { e = parser.nextTag(); handleUnkownTags(parser); } } private static void addToSchema(Iterator operationDescs, Schema schema, String defaultNamespace) { while (operationDescs.hasNext()) { OperationDescription op = (OperationDescription) operationDescs.next(); Element input = op.getInput(); if (input != null) { input.globalScope = true; if (input.name == null) { // input.name = new QName(input.getClass().getSimpleName(), // defaultNamespace); input.name = new QName(StringUtil.simpleClassName(input.getClass()), defaultNamespace); } schema.addElement(input); } Element output = op.getOutput(); if (output != null) { output.globalScope = true; schema.addElement(output); } for (Iterator it2 = op.getFaults(); it2.hasNext();) { Fault fault = (Fault) it2.next(); Element faultElement = fault.getElement(); if (faultElement != null) { faultElement.globalScope = true; schema.addElement(faultElement); } } } } private static void initNatives() { nativeTypes = new HashMap(); for (int i = 0; i < NATIVES.length; i++) { QName name = new QName(NATIVES[i], XMLSCHEMA_NAMESPACE); Type nativeType = new SimpleType(name); nativeTypes.put(name, nativeType); } // add anyType and anySimpleType QName typeName = new QName(TYPE_ANYTYPE, XMLSCHEMA_NAMESPACE); nativeTypes.put(typeName, new ComplexType(typeName)); typeName = new QName(TYPE_ANYSIMPLETYPE, XMLSCHEMA_NAMESPACE); // Changed SSch 2011-01-13 nativeTypes.put(typeName, new AnySimpleType(typeName)); } }