/* * Copyright (c) 2003-2006, KNOPFLERFISH project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * - Neither the name of the KNOPFLERFISH project nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @author Erik Wistrand * @author Philippe Laporte */ //TODO use only one parser... package org.knopflerfish.util.metatype; import org.osgi.framework.*; import org.osgi.service.cm.*; import org.osgi.service.metatype.*; import org.osgi.util.tracker.ServiceTracker; import net.n3.nanoxml.*; import java.net.URL; import java.io.*; import java.util.*; import java.lang.reflect.Array; import org.knopflerfish.util.Text; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.kxml2.io.KXmlParser; /** * Helper class which loads (and saves) KF Metatype XML, * as well as the R4 Metatype XML * *

* This implementaion uses the nanoxml package for KF Metatype XML, and * kXML for R4 Metatype XML *

*

* NanoXML is distributed under the zlib/libpng license.
* See http://nanoxml.sourceforge.net/orig/copyright.html * for details.
* The full license text is also include in the kf_metatype bundle jar *

* Nanoxml is Copyrighted 2000-2002 Marc De Scheemaecker, All Rights * Reserved. * * kXML notice goes here *

*/ public class Loader { static final String METATYPE = "metatype"; static final String SERVICES = "services"; static final String FACTORIES = "factories"; static final String VALUES = "values"; static final String SCHEMA = "schema"; static final String SERVICE_PID = "service.pid"; static final String FACTORY_PID = "factory.pid"; static final String ITEM = "item"; static final String ATTR_PID = "pid"; static final String ATTR_DECS = "description"; static final String ATTR_TYPE = "type"; static final String ATTR_NAME = "name"; static final String ATTR_BASE = "base"; static final String ATTR_VALUE = "value"; static final String ATTR_ICONURL = "iconURL"; static final String ATTR_MINOCCURS = "minOccurs"; static final String ATTR_MAXOCCURS = "maxOccurs"; static final String ATTR_ARRAY = "array"; static final String XSD_NS = "http://www.w3.org/2001/XMLSchema"; static final String METATYPE_NS = "http://www.knopflerfish.org/XMLMetatype"; static final String TAG_ANNOTATION = "annotation"; static final String TAG_SIMPLETYPE = "simpleType"; static final String TAG_COMPLEXTYPE = "complexType"; static final String TAG_ELEMENT = "element"; static final String TAG_RESTRICTION = "restriction"; static final String TAG_ENUMERATION = "enumeration"; static final String TAG_DOCUMENTATION = "documentation"; static final String TAG_APPINFO = "appInfo"; static final String TAG_SEQUENCE = "sequence"; static final String BUNDLE_PROTO = "bundle://"; /** * Load a MetaTypeProvider from an XML file. */ public static MTP loadMTPFromURL(Bundle bundle, URL url) throws IOException { InputStream in = null; try { in = url.openStream(); IXMLParser parser = XMLParserFactory.createDefaultXMLParser(); IXMLReader reader = new StdXMLReader(in); parser.setReader(reader); XMLElement el = (XMLElement) parser.parse(); return loadMTP(bundle, url, el); } catch (Exception e) { e.printStackTrace(); throw new IOException("Failed to load " + url + " " + e); } finally { try { in.close(); } catch (Exception ignored) { } } } /** * load defaults from an XML file into an MTP */ public static List loadDefaultsFromURL(MTP mtp, URL url) throws IOException { InputStream in = null; try { in = url.openStream(); IXMLParser parser = XMLParserFactory.createDefaultXMLParser(); IXMLReader reader = new StdXMLReader(in); parser.setReader(reader); XMLElement el = (XMLElement) parser.parse(); if(isName(el, METATYPE_NS, VALUES)) { List propList = loadValues(mtp, el); setDefaultValues(mtp, propList); return propList; } else { for(Enumeration e = el.enumerateChildren(); e.hasMoreElements(); ) { XMLElement childEl = (XMLElement)e.nextElement(); if(isName(childEl, METATYPE_NS, VALUES)) { List propList = loadValues(mtp, childEl); setDefaultValues(mtp, propList); return propList; } } } throw new XMLException("No values tag in " + url, el); } catch (Exception e) { e.printStackTrace(); throw new IOException("Failed to load " + url + " " + e); } finally { try { in.close(); } catch (Exception ignored) { } } } /** * Load a MetaTypeProvider from an XML "config" element. * *
    *
  1. Load all service and factory definitions into * a MetaTypeProvider instance. *
  2. Load any default data *
  3. Insert default data into definitions in MetaTypeProvider * using the setDefaultValues method *
* */ public static MTP loadMTP(Bundle bundle, URL sourceURL, XMLElement el) { assertTagName(el, METATYPE_NS, METATYPE); CMConfig[] services = null; CMConfig[] factories = null; boolean bHasDefValues = false; for(Enumeration e = el.enumerateChildren(); e.hasMoreElements(); ) { XMLElement childEl = (XMLElement)e.nextElement(); if(isName(childEl, METATYPE_NS, SERVICES)) { services = parseServices(childEl, false); } else if(isName(childEl, METATYPE_NS, FACTORIES)) { factories = parseServices(childEl, true); } else if(isName(childEl, METATYPE_NS, VALUES)) { bHasDefValues = true; } else if(isName(childEl, XSD_NS, SCHEMA)) { CMConfig[] any = parseSchema(childEl); List sa = new ArrayList(); List fa = new ArrayList(); for(int i = 0; i < any.length; i++) { if(any[i].maxInstances > 1) { fa.add(any[i]); } else { sa.add(any[i]); } } services = new CMConfig[sa.size()]; sa.toArray(services); factories = new CMConfig[fa.size()]; fa.toArray(factories); } else { throw new XMLException("Unexpected element", el); } } MTP mtp = new MTP(sourceURL.toString()); mtp.setBundle(bundle); // Insert services and factory definition into MTP // default values will be default values defined by AD for(int i = 0; services != null && i < services.length; i++) { OCD ocd = new OCD(services[i].pid, services[i].pid, services[i].desc, sourceURL); ocd.maxInstances = 1; String iconURL = services[i].iconURL; if(iconURL != null) { try { if(bundle != null) { if(iconURL.startsWith("/")) { iconURL = BUNDLE_PROTO + "$(BID)" + iconURL; } iconURL = Text.replace(iconURL, "$(BID)", Long.toString(bundle.getBundleId())); } ocd.setIconURL(iconURL); } catch (Exception e) { System.err.println("Failed to set icon url: " + e); } } for(int j = 0; j < services[i].ads.length; j++) { ocd.add(services[i].ads[j], services[i].ads[j].isOptional() ? ObjectClassDefinition.OPTIONAL : ObjectClassDefinition.REQUIRED); } mtp.addService(services[i].pid, ocd); } for(int i = 0; factories != null && i < factories.length; i++) { OCD ocd = new OCD(factories[i].pid, factories[i].pid, factories[i].desc, sourceURL); ocd.maxInstances = factories[i].maxInstances; if(factories[i].iconURL != null) { try { ocd.setIconURL(factories[i].iconURL); } catch (Exception e) { System.err.println("Failed to set icon url: "+ e); } } for(int j = 0; j < factories[i].ads.length; j++) { ocd.add(factories[i].ads[j], ObjectClassDefinition.REQUIRED); } mtp.addFactory(factories[i].pid, ocd); } // Overwrite MTP default values with values found in // DEFAULTVALUES section in source XML if(bHasDefValues) { for(Enumeration e = el.enumerateChildren(); e.hasMoreElements(); ) { XMLElement childEl = (XMLElement)e.nextElement(); if(isName(childEl, METATYPE_NS, VALUES)) { List propList = loadValues(mtp, childEl); setDefaultValues(mtp, propList); } } } return mtp; } /** * Overwrite default values in MTP using a set of dictionaries. * * @param mtp MetaTypeProvider containing instances of AD * @param propList List of Dictionary */ public static void setDefaultValues(MetaTypeProvider mtp, List propList) { for(Iterator it = propList.iterator(); it.hasNext();) { Dictionary props = (Dictionary)it.next(); String pid = (String)props.get(SERVICE_PID); if(pid == null) { pid = (String)props.get("factory.pid"); } ObjectClassDefinition ocd = null; try { ocd = mtp.getObjectClassDefinition(pid, null); } catch (Exception ignored) { } if(ocd == null) { throw new IllegalArgumentException("No definition for pid '" + pid + "'"); } else { AttributeDefinition[] ads = ocd.getAttributeDefinitions(ObjectClassDefinition.ALL); for(int i = 0; ads != null && i < ads.length; i++) { Object val = props.get(ads[i].getID()); if(!(ads[i] instanceof AD)) { throw new IllegalArgumentException("AttributeDefinitions must be instances of AD, otherwise default values cannot be set"); } AD ad = (AD)ads[i]; if(val instanceof Vector) { ad.setDefaultValue(toStringArray((Vector)val)); } else if(val.getClass().isArray()) { ad.setDefaultValue(toStringArray((Object[])val)); } else { ad.setDefaultValue(new String[] { val.toString() }); } } } } } /** * @return String (pid) -> Dictionary */ public static List loadValues(MetaTypeProvider mtp, XMLElement el) { // assertTagName(el, DEFAULTVALUES); List propList = new ArrayList(); // String (pid) -> Integer (count) Map countMap = new HashMap(); for(Enumeration e = el.enumerateChildren(); e.hasMoreElements(); ) { XMLElement childEl = (XMLElement)e.nextElement(); String pid = childEl.getName(); ObjectClassDefinition ocd = null; try { ocd = mtp.getObjectClassDefinition(pid, null); } catch (Exception ignored) { } if(ocd == null) { throw new XMLException("Undefined pid '" + pid + "'", childEl); } Dictionary props = loadValues(ocd.getAttributeDefinitions(ObjectClassDefinition.ALL), childEl); int maxInstances = 1; if(ocd instanceof OCD) { maxInstances = ((OCD)ocd).maxInstances; } Integer count = (Integer)countMap.get(pid); if(count == null) { count = new Integer(0); } count = new Integer(count.intValue() + 1); if(count.intValue() > maxInstances) { throw new XMLException("PID " + pid + " can only have " + maxInstances + " instance(s), found " + count, el); } countMap.put(pid, count); props.put(maxInstances > 1 ? "factory.pid" : SERVICE_PID, pid); propList.add(props); } return propList; } public static Dictionary loadValues(AttributeDefinition[] attrs, XMLElement el) { if(attrs == null) { throw new NullPointerException("attrs array cannot be null"); } Hashtable props = new Hashtable(); for(Enumeration e = el.enumerateChildren(); e.hasMoreElements(); ) { XMLElement childEl = (XMLElement)e.nextElement(); String id = childEl.getFullName(); AttributeDefinition attr = null; // System.out.println("load id=" + id); for(int i = 0; attr == null && i < attrs.length; i++) { // System.out.println(i + ": " + attrs[i]); if(id.equals(attrs[i].getID())) { attr = attrs[i]; } } if(attr == null) { throw new XMLException("Undefined id '" + id + "'", childEl); } Object val = loadValue(attr, childEl); props.put(id, val); } // Verify that all attributes are found for(int i = 0; i < attrs.length; i++) { if(!props.containsKey(attrs[i].getID())) { throw new XMLException("Missing attribute id '" + attrs[i].getID() + "'", el); } } return props; } /** * Load a java object from an XML element using type info in the * specified definition. * */ public static Object loadValue(AttributeDefinition attr, XMLElement el) { assertTagName(el, attr.getID()); if(attr.getCardinality() < 0) { return loadSequence(attr, el, -attr.getCardinality(), ITEM); } if(attr.getCardinality() > 0) { Vector v = loadSequence(attr, el, -attr.getCardinality(), ITEM); Object[] array = (Object[])Array.newInstance(AD.getClass(attr.getType()), v.size()); v.copyInto(array); return array; } return loadContent(attr, el); } public static Vector loadSequence(AttributeDefinition attr, XMLElement el, int max, String tagName) { Vector v = new Vector(); for(Enumeration e = el.enumerateChildren(); e.hasMoreElements(); ) { XMLElement childEl = (XMLElement)e.nextElement(); assertTagName(childEl, tagName); v.addElement(loadContent(attr, childEl)); } return v; } /** * Load the contents of a tag into a java object. * * @param el element which content should be converted to a java object. * @param attr definition defining type. */ public static Object loadContent(AttributeDefinition attr, XMLElement el) { String content = el.getContent(); if(content == null) { content = ""; } content = content.trim(); String msg = attr.validate(content); if(msg != null && !"".equals(msg)) { throw new XMLException("Validation error: '" + msg + "'", el); } return AD.parseSingle(content, attr.getType()); } /** * Parse a services or factories tag info an array of wrapper * objects. * *

* Children to this tag must all be "xsd:schema" *

*/ static CMConfig[] parseServices(XMLElement el, boolean bFactory) { assertTagName(el, METATYPE_NS, bFactory ? FACTORIES : SERVICES); List list = new ArrayList(); for(Enumeration e = el.enumerateChildren(); e.hasMoreElements(); ) { XMLElement childEl = (XMLElement)e.nextElement(); CMConfig[] conf = parseSchema(childEl); if(conf.length == 0) { throw new XMLException("No elements in schema", childEl); } conf[0].maxInstances = bFactory ? Integer.MAX_VALUE : 1; list.add(conf[0]); } CMConfig[] ads = new CMConfig[list.size()]; list.toArray(ads); return ads; } /** * Parse an XSD schema into a wrapper object for services and factories. * *

* Each schema element must contain exacly one child "xsd:complexType" *

*/ private static CMConfig[] parseSchema(XMLElement el) { assertTagName(el, XSD_NS, "schema"); /* if(el.getChildrenCount() != 1) { throw new XMLException("service/factory schema must contain exacly one xsd.complexType", el); } */ List v = new ArrayList(); for(Enumeration e = el.enumerateChildren(); e.hasMoreElements(); ) { XMLElement childEl = (XMLElement)e.nextElement(); AD[] ads = parseComplexType(childEl); Annotation an = loadAnnotationFromAny(childEl); String iconURL = childEl.getAttribute(ATTR_ICONURL); if("".equals(iconURL)) { iconURL = null; } int maxOccurs = getInteger(childEl, ATTR_MAXOCCURS, 1); String name = childEl.getAttribute(ATTR_NAME).toString(); // System.out.println("load " + name + ", maxOccurs=" + maxOccurs); v.add(new CMConfig(name, ads, an != null ? an.doc : "", iconURL, maxOccurs)); } CMConfig[] r = new CMConfig[v.size()]; v.toArray(r); return r; } static final String UNBOUNDED = "unbounded"; static int getInteger(XMLElement el, String attr, int def) { String s = el.getAttribute(attr, Integer.toString(def)); if(UNBOUNDED.equals(s)) { return Integer.MAX_VALUE; } return Integer.parseInt(s); } /** * Parse an XSD complexType info an array of AttributeDefinition, * definining the metadata for a PID. * *

* The name of the complexTyp specifies the metadata PID. *

* *

* The following child elements are supported: *

* * The names of the elements definines the ID's of the definitions. Each * ID must only be present once. *

* * @throws XMLException if the el tag is not an "xsd:complexType" */ static AD[] parseComplexType(XMLElement el) { assertTagName(el, XSD_NS, TAG_COMPLEXTYPE); Set list = new HashSet(); Annotation annotation = null; for(Enumeration e = el.enumerateChildren(); e.hasMoreElements(); ) { XMLElement childEl = (XMLElement)e.nextElement(); if(isName(childEl, XSD_NS, TAG_ANNOTATION)) { annotation = loadAnnotation(childEl); } else { try { AD ad = parseAttributeDefinition(childEl); if(list.contains(ad)) { throw new XMLException("Multiple definitions of id '" + ad.getID() + "'", childEl); } if(ad == null) { throw new XMLException("Null ad", childEl); } list.add(ad); } catch (XMLException ex) { System.out.println("Failed in " + el.getFullName() + ", name=" + el.getAttribute(ATTR_NAME) + ", line=" + el.getLineNr() + ", " + ex); throw ex; } } } AD[] ads = new AD[list.size()]; list.toArray(ads); return ads; } /** * Parse an XSD sequence into an AttributeDefinition of either * vector or array type. * *

* Only one child name "element" is allowed, and this child specifies * the element type of the vector/array. *

* * @throws XMLException of element is not an "xsd:sequence" */ static AD parseComplexTypeAttr(XMLElement el) { assertTagName(el, XSD_NS, TAG_COMPLEXTYPE); AD attr = null; for(Enumeration e = el.enumerateChildren(); e.hasMoreElements(); ) { XMLElement childEl = (XMLElement)e.nextElement(); if(isName(childEl, XSD_NS, TAG_SEQUENCE)) { if(attr != null) { throw new XMLException("Only one sequence is allowed in complexType", childEl); } attr = parseSequence(childEl, el.getAttribute(ATTR_NAME)); } else if(isName(childEl, XSD_NS, TAG_RESTRICTION)) { System.out.println("skip restriction"); } else if(isName(childEl, XSD_NS, TAG_ANNOTATION)) { // parse later } } if(attr == null) { throw new XMLException("No sequence found in complexType", el); } return addAnnotation(attr, el); } static AD parseSimpleTypeAttr(XMLElement el) { assertTagName(el, XSD_NS, TAG_SIMPLETYPE); AD attr = null; String id = el.getAttribute(ATTR_NAME).toString(); for(Enumeration e = el.enumerateChildren(); e.hasMoreElements(); ) { XMLElement childEl = (XMLElement)e.nextElement(); if(isName(childEl, XSD_NS, TAG_RESTRICTION)) { int type = getType(childEl); int card = 0; String name = id; String[] defValue = null; attr = new AD(id, type, card, name, defValue); addEnumeration(childEl, attr); } else if(isName(childEl, XSD_NS, TAG_ANNOTATION)) { // accept and parse later; } } return addAnnotation(attr, el); } static void addEnumeration(XMLElement el, AD ad) { assertTagName(el, XSD_NS, TAG_RESTRICTION); Vector v = new Vector(); for(Enumeration e = el.enumerateChildren(); e.hasMoreElements(); ) { XMLElement childEl = (XMLElement)e.nextElement(); // System.out.println(" addEnum " + childEl.getName()); if(isName(childEl, XSD_NS, TAG_ENUMERATION)) { String val = childEl.getAttribute(ATTR_VALUE); if(val == null) { throw new XMLException("No value specified in enum", childEl); } String label = val; Annotation annotation = loadAnnotationFromAny(childEl); if(annotation != null && annotation.doc != null) { label = annotation.doc; } v.addElement(new String[] { val, label }); } } // System.out.println("optvalues=" + v); if(v.size() > 0) { String[] optValues = new String[v.size()]; String[] optLabels = new String[v.size()]; for(int i = 0; i < v.size(); i++) { String[] row = (String[])v.elementAt(i); optValues[i] = row[0]; optLabels[i] = row[1]; } ad.setOptions(optValues, optLabels); } } static AD addAnnotation(AD attr, XMLElement el) { Annotation a = loadAnnotationFromAny(el); if(a != null) { if(a.doc != null) { attr.setDescription(a.doc); } } int minOccurs = 1; try { minOccurs = Integer.parseInt(el.getAttribute(ATTR_MINOCCURS, "1")); } catch (Exception e) { throw new XMLException("minOccurs must be a valid integer: " + e, el); } if(minOccurs > 1) { throw new XMLException("minOccurs cannot be > 1, is " + minOccurs, el); } attr.bOptional = minOccurs == 0; return attr; } static Annotation loadAnnotationFromAny(XMLElement el) { for(Enumeration e = el.enumerateChildren(); e.hasMoreElements(); ) { XMLElement childEl = (XMLElement)e.nextElement(); if(isName(childEl, XSD_NS, TAG_ANNOTATION)) { return loadAnnotation(childEl); } } return null; } static Annotation loadAnnotation(XMLElement el) { assertTagName(el, XSD_NS, TAG_ANNOTATION); Annotation a = null; for(Enumeration e = el.enumerateChildren(); e.hasMoreElements(); ) { XMLElement childEl = (XMLElement)e.nextElement(); if(isName(childEl, XSD_NS, TAG_DOCUMENTATION)) { if(a == null) { a = new Annotation(); } a.doc = "" + childEl.getContent(); } else if(isName(childEl, XSD_NS, TAG_APPINFO)) { if(a == null) { a = new Annotation(); } a.appinfo = "" + childEl.getContent(); } } return a; } static AD parseSequence(XMLElement el, String name) { // System.out.println("parseSequence " + el.getAttribute(ATTR_NAME)); assertTagName(el, XSD_NS, TAG_SEQUENCE); boolean bArray = "true".equals(el.getAttribute(ATTR_ARRAY, "false").toLowerCase()); int maxOccurs = getInteger(el, ATTR_MAXOCCURS, Integer.MAX_VALUE); if(el.getChildrenCount() != 1) { throw new XMLException("sequence children count must be " + "exactly one", el); } else { String id = name; int type = -1; for(Enumeration e = el.enumerateChildren(); e.hasMoreElements(); ) { XMLElement childEl = (XMLElement)e.nextElement(); String childName = childEl.getAttribute(ATTR_NAME).toString(); int card = -1; if(!ITEM.equals(childName)) { throw new XMLException("Only '" + ITEM + "'" + " names are allowed in sequences, found " + childName, childEl); } if(bArray) { card = maxOccurs; } else { if(maxOccurs == Integer.MAX_VALUE) { card = Integer.MIN_VALUE; } else { card = -maxOccurs; } } AD ad = parseAttributeDefinition(childEl); type = ad.getType(); String[] defValue = null; return new AD(id, type, card, id, defValue); } throw new XMLException("parseSequence failed", el); } } /** * Parse an XSD element into an AttributeDefinition * *

* The type of the definition is derived using the getType * method. *

*

* If the XSD element is a sequence, parse using the parseSequence * method. *

*/ static AD parseAttributeDefinition(XMLElement el) { // System.out.println("parseAttributeDefinition " + el.getFullName() + ", name=" + el.getAttribute(ATTR_NAME)); AD ad = null; if(isName(el, XSD_NS, TAG_SIMPLETYPE)) { ad = parseSimpleTypeAttr(el); } else if(isName(el, XSD_NS, TAG_COMPLEXTYPE)) { ad = parseComplexTypeAttr(el); } else if(isName(el, XSD_NS, TAG_ELEMENT)) { String id = el.getAttribute(ATTR_NAME); if(id == null) { throw new XMLException("No id specified in element", el); } String strType = el.getAttribute(ATTR_TYPE); if(strType == null) { throw new XMLException("No type specified in " + id, el); } int type = getType(el); int card = 0; String name = id; String[] defValue = null; ad = new AD(id, type, card, name, defValue); } else if(isName(el, XSD_NS, TAG_ANNOTATION)) { // } else { throw new XMLException("Unsupported tag " + el.getName() + ", ns=" + el.getNamespace() + ", name=" + el.getAttribute(ATTR_NAME), el); } return addAnnotation(ad, el); } /** * Get AttributeDefinition type from XSD element type. * * @return One of the AttributeDefinition.STRING...BOOLEAN types. * @throws XMLException if the XSD type is unsupported. */ static int getType(XMLElement el) { int type = -1; String strType = null; if(isName(el, XSD_NS, TAG_ELEMENT)) { strType = el.getAttribute(ATTR_TYPE); } else if(isName(el, XSD_NS, TAG_RESTRICTION)) { strType = el.getAttribute(ATTR_BASE); } else { throw new XMLException("Type is only supported in element and restriction tags", el); } if(strType == null) { throw new XMLException("No type in tag", el); } if("xsd:int".equals(strType)) { type = AttributeDefinition.INTEGER; } else if("xsd:string".equals(strType)) { type = AttributeDefinition.STRING; } else if("xsd:boolean".equals(strType)) { type = AttributeDefinition.BOOLEAN; } else if("xsd:float".equals(strType)) { type = AttributeDefinition.FLOAT; } else if("xsd:long".equals(strType)) { type = AttributeDefinition.LONG; } else if("xsd:short".equals(strType)) { type = AttributeDefinition.SHORT; } else if("xsd:char".equals(strType)) { type = AttributeDefinition.CHARACTER; } else if("xsd:double".equals(strType)) { type = AttributeDefinition.DOUBLE; } else { throw new XMLException("Unsupported type '" + strType + "'", el); } return type; } /** * Print sets of definitions to an XML file. */ public static void printMetatypeXML(MetaTypeProvider mtp, String[] servicePIDs, String[] factoryPIDs, boolean bXMLHeader, boolean bMetatypeTag, List propList, PrintWriter out) { if(bXMLHeader) { out.println(""); } if(bMetatypeTag) { out.println(""); } out.println(""); out.println(" \n"); printOCDXML(mtp, servicePIDs, 1, out); out.println(""); printOCDXML(mtp, factoryPIDs, Integer.MAX_VALUE, out); out.println(""); out.println(" \n"); if(propList != null) { printValuesXML(propList, false, out); } if(bMetatypeTag) { out.println(""); } } /** * Print a set of ObjectClassDefinitions as XML. * * @param mtp Metatype provider * @param pids Set of String (PIDs) * @param out writer to print to. */ public static void printOCDXML(MetaTypeProvider mtp, String[] pids, int maxOccurs, PrintWriter out) { for(int i = 0; i < pids.length; i++) { String pid = pids[i]; ObjectClassDefinition ocd = mtp.getObjectClassDefinition(pid, null); if(ocd instanceof OCD) { maxOccurs = ((OCD)ocd).maxInstances; } AttributeDefinition[] ads = ocd.getAttributeDefinitions(ObjectClassDefinition.ALL); out.println(""); out.println(" "); // out.println(" "); out.print (" "); printAnnotation(ocd.getDescription(), " ", out); for(int j = 0; j < ads.length; j++) { printXML(out, ads[j]); } out.println(" "); // out.println(" \n"); } } static void printXMLSequence(PrintWriter out, AttributeDefinition ad, boolean bArray) { out.println(" "); out.println(" "); out.println(" "); out.println(" "); out.println(" "); } /** * Print an attribute definition as XML. */ public static void printXML(PrintWriter out, AttributeDefinition ad) { if(ad.getCardinality() > 0) { printXMLSequence(out, ad, false); } else if(ad.getCardinality() < 0) { printXMLSequence(out, ad, true); } else { printXMLSingle(out, ad); } } static void printXMLSingle(PrintWriter out, AttributeDefinition ad) { String tag = getXSDType(ad.getType()); String[] optValues = ad.getOptionValues(); String[] optLabels = ad.getOptionLabels(); String desc = ad.getDescription(); if(optValues != null) { out.println(" "); out.println(" "); for(int i = 0; i < optValues.length; i++) { out.println(" "); if(optLabels != null) { printAnnotation(optLabels[i], " ", out); } out.println(" "); } out.println(" "); out.println(" "); } else { if("".equals(desc)) { out.println(" "); } else { out.println(" "); printAnnotation(desc, " ", out); out.println(" "); } } } public static void printValuesXML(List propList, boolean bXMLHeader, PrintWriter out) { if(bXMLHeader) { out.println(""); } out.println(" " ); out.println(""); for(Iterator it = propList.iterator(); it.hasNext();) { Dictionary props = (Dictionary)it.next(); String pid = (String)props.get(SERVICE_PID); if(pid == null) { pid = (String)props.get("factory.pid"); } out.println(""); out.println(" "); out.println(" <" + pid + ">"); printPropertiesXML(props, out); out.println(" "); } out.println(" "); } static void printPropertiesXML(Dictionary props, PrintWriter out) { for(Enumeration e = props.keys(); e.hasMoreElements(); ) { String key = (String)e.nextElement(); Object val = props.get(key); if(val instanceof Vector) { out.println(" <" + key + ">"); Vector v = (Vector)val; for(int i = 0; i < v.size(); i++) { out.println(" " + v.elementAt(i) + ""); } out.println(" "); } else if(val.getClass().isArray()) { out.println(" <" + key + ">"); for(int i = 0; i < Array.getLength(val); i++) { out.println(" " + Array.get(val, i) + ""); } out.println(" "); } else { out.println(" <" + key + ">" + val.toString() + ""); } } } static String getXSDType(int type) { String tag = ""; switch(type) { case AttributeDefinition.STRING: return "xsd:string"; case AttributeDefinition.INTEGER: return "xsd:int"; case AttributeDefinition.LONG: return "xsd:long"; case AttributeDefinition.SHORT: return "xsd:short"; case AttributeDefinition.DOUBLE: return "xsd:double"; case AttributeDefinition.CHARACTER: return "xsd:char"; case AttributeDefinition.FLOAT: return "xsd:float"; case AttributeDefinition.BOOLEAN: return "xsd:boolean"; case AttributeDefinition.BIGINTEGER: return "xsd:integer"; case AttributeDefinition.BIGDECIMAL: return "xsd:decimal"; default: throw new IllegalArgumentException("Cannot print " + type); } } static void printAnnotation(String s, String prefix, PrintWriter out) { out.println(prefix + ""); out.println(prefix + " " + s + ""); out.println(prefix + ""); } static void assertTagName(XMLElement el, String name) { assertTagName(el, null, name); } static void assertTagName(XMLElement el, String namespace, String name) { if(!isName(el, namespace, name)) { throw new XMLException("Excepted tag '" + namespace + ":" + name + "', found '" + el.getFullName() + "'", el); } } static boolean isName(XMLElement el, String namespace, String name) { boolean b = el.getName().equals(name) && (namespace == null || el.getNamespace() == null || namespace.equals(el.getNamespace())); return b; } private static String[] toStringArray(Object[] val) { String[] r = new String[val.length]; for(int i = 0; i < val.length; i++) { r[i] = AD.escape(val[i].toString()); } return r; } private static String[] toStringArray(Vector val) { String[] r = new String[val.size()]; for(int i = 0; i < val.size(); i++) { r[i] = AD.escape(val.elementAt(i).toString()); } return r; } //----------------- R4 ---------------------------------------------------------- //TODO finish the impl static final String METADATA = "MetaData"; static final String OCD = "OCD"; static final String AD_E = "AD"; static final String OBJECT = "Object"; static final String ATTRIBUTE = "Attribute"; static final String DESIGNATE = "Designate"; static final String OPTION = "Option"; static final String ICON = "Icon"; static final String VALUE = "Value"; static final String CONTENT = "Content"; static final String ATTR_LOCALIZATION = "localization"; static final String ATTR_ID = "id"; static final String ATTR_DESCRIPTION = "description"; static final String ATTR_CARDINALITY = "cardinality"; static final String ATTR_MIN = "min"; static final String ATTR_MAX = "max"; static final String ATTR_DEFAULT = "default"; static final String ATTR_REQUIRED = "required"; static final String ATTR_OCDREF = "ocdref"; static final String ATTR_ADREF = "adref"; static final String ATTR_CONTENT = "content"; static final String ATTR_FACTORYPID = "factoryPid"; static final String ATTR_BUNDLE = "bundle"; static final String ATTR_OPTIONAL = "optional"; static final String ATTR_MERGE = "merge"; static final String ATTR_LABEL = "label"; static final String ATTR_RESOURCE = "resource"; static final String ATTR_SIZE = "size"; private static final String CHARACTER_ENCODING = "UTF8"; private static XmlPullParser xml_parser/* = null*/; private static String content/* = null*/; private static BundleMetaTypeResource currentBMTR; private static MetaData currentMetaData; private static OCD currentOCD; private static AD currentAD; private static Vector currentOptionLabels = new Vector(); private static Vector currentOptionValues = new Vector(); private static String currentDesignatePid; private static String currentDesignateFactoryPid; private static String currentObjectOCDref; private static ServiceTracker confAdminTracker; private static Configuration currentConf; private static Vector currentAttributes; private static AE currentAE; private static Bundle currentBundle; public static BundleMetaTypeResource loadBMTIfromUrl(BundleContext bc, Bundle b, URL url) throws IOException { InputStream in = null; if(xml_parser == null){ xml_parser = new KXmlParser(); confAdminTracker = new ServiceTracker(bc, ConfigurationAdmin.class.getName(), null); confAdminTracker.open(); } currentBMTR = new BundleMetaTypeResource(b); currentBundle = b; try { processDocument(xml_parser, url); return currentBMTR; } catch (Exception e) { throw new IOException("Failed to load " + url + " " + e); } } //method private static void processDocument(XmlPullParser xpp, URL url) throws XmlPullParserException, IOException { InputStream in = null; try { in = url.openStream(); xpp.setInput(in, CHARACTER_ENCODING); int eventType = xpp.getEventType(); do { if(eventType == XmlPullParser.START_DOCUMENT) { // System.out.println("Start document"); } else if(eventType == XmlPullParser.END_DOCUMENT) { // System.out.println("End document"); } else if(eventType == XmlPullParser.START_TAG) { try{ startElement(xpp.getName(), url); } catch(Exception e){ //System.out.println("Got exception:" + e); //e.printStackTrace(System.err); } // System.out.println("Start element: " + name); } else if(eventType == XmlPullParser.END_TAG) { try{ endElement(xpp.getName(), content); } catch(Exception e){ // System.out.println("Got exception"); } // System.out.println("End element: " + name); } else if(eventType == XmlPullParser.TEXT) { content = xpp.getText().trim(); // System.out.println("Text: " + content); } else{ // System.out.println("Got something else"); } try{ eventType = xpp.next(); } catch(java.io.IOException ex){ //System.out.println(ex); //stream closed for example return; //TODO proper handling } catch(XmlPullParserException e){ //catch also initial call upstairs //System.out.println(e); return; //TODO proper handling } } while (eventType != XmlPullParser.END_DOCUMENT); // System.out.println("End document"); // System.out.flush(); } finally { if (in != null) { try { in.close(); } catch (IOException _ignore) { } } } } //method //any missing attribute gets the element ignored protected static void startElement(String element, URL sourceURL) throws Exception { int n_attrs = xml_parser.getAttributeCount(); HashMap attrs = new HashMap(); for(int i = 0; i < n_attrs; i++){ attrs.put(xml_parser.getAttributeName(i), xml_parser.getAttributeValue(i)); } if (METADATA.equals(element) || element.endsWith(METADATA)) { String localization = (String) attrs.get(ATTR_LOCALIZATION); if(localization != null){ currentMetaData = new MetaData(localization, currentBundle); } else{ currentMetaData = new MetaData(currentBundle); } } else if (OCD.equals(element)) { String id = (String) attrs.get(ATTR_ID); if(id == null){ return;//TODO not valid: required attribute is missing } String name = (String) attrs.get(ATTR_NAME); if(name == null){ //TODO not valid: required attribute is missing return; } String desc = (String) attrs.get(ATTR_DESCRIPTION); currentOCD = new OCD(id, name, desc, sourceURL); } else if (AD_E.equals(element)) { String id = (String) attrs.get(ATTR_ID); if(id == null){ //TODO not valid: required attribute is missing return; } String name = (String) attrs.get(ATTR_NAME); String desc = (String) attrs.get(ATTR_DESCRIPTION); String typeS = (String) attrs.get(ATTR_TYPE); int type; if(typeS != null){ type = getType(typeS); } else{ //TODO not valid: required attribute is missing return; } String card = (String) attrs.get(ATTR_CARDINALITY); int cardinality; if(card != null){ cardinality = Integer.parseInt(card); } else{ cardinality = 0; } String min = (String) attrs.get(ATTR_MIN); String max = (String) attrs.get(ATTR_MAX); String default_attr = (String) attrs.get(ATTR_DEFAULT); String[] defaults = null; if(default_attr != null){ StringTokenizer st = new StringTokenizer(default_attr, ","); int number = st.countTokens(); if(number > 0) { defaults = new String[number]; for(int i = 0; i < defaults.length; i++){ defaults[i] = st.nextToken(); } } } String requiredS = (String) attrs.get(ATTR_REQUIRED); boolean required; if(requiredS != null){ required = Boolean.valueOf(requiredS).booleanValue(); } else{ required = true; } currentAD = new AD(id, type, cardinality, name, desc, defaults, min, max, required); } else if (OBJECT.equals(element)) { String ocdref = (String) attrs.get(ATTR_OCDREF); if(ocdref != null){ currentObjectOCDref = ocdref; } else{ //TODO not valid: required attribute is missing return; } } else if (ATTRIBUTE.equals(element)) { String adref = (String) attrs.get(ATTR_ADREF); if(adref != null){ currentAE = new AE(adref); } else{ //TODO not valid: required attribute is missing return; } String content = (String) attrs.get(ATTR_CONTENT); if(content != null){ StringTokenizer st = new StringTokenizer(content, ","); while(st.hasMoreTokens()){ currentAE.addValue(st.nextToken()); } } currentAttributes.add(currentAE); } else if (DESIGNATE.equals(element)) { //SPCES what do we mean by optional exactly? boolean optionalB; String optional = (String) attrs.get(ATTR_OPTIONAL); if(optional != null){ optionalB = Boolean.valueOf(optional).booleanValue(); } else{ optionalB = false; } String pid = (String) attrs.get(ATTR_PID); if(pid != null){ currentDesignatePid = pid; } else{ //TODO not valid: required attribute is missing if(!optionalB){ return; } } String factoryPid = (String) attrs.get(ATTR_FACTORYPID); if(factoryPid != null && !factoryPid.equals("")){ currentDesignateFactoryPid = factoryPid; currentDesignatePid = null; } String bundle_location = (String) attrs.get(ATTR_BUNDLE); if(currentDesignatePid != null){ ConfigurationAdmin ca = (ConfigurationAdmin) confAdminTracker.getService(); if(ca != null){ currentConf = ca.getConfiguration(currentDesignatePid, bundle_location); String merge = (String) attrs.get(ATTR_MERGE); if(merge == null || !Boolean.valueOf(merge).booleanValue()){ currentConf.delete(); currentConf = ca.getConfiguration(currentDesignatePid, bundle_location); } String location = currentConf.getBundleLocation(); if(location != null && !location.equals(bundle_location)){ //currentConf = null; //will prevent processing currentConf.setBundleLocation(bundle_location); } } } else if (currentDesignateFactoryPid != null){ ConfigurationAdmin ca = (ConfigurationAdmin) confAdminTracker.getService(); if(ca != null){ currentConf = ca.createFactoryConfiguration(currentDesignateFactoryPid, bundle_location); //merge is meaningless } } currentAttributes = new Vector(); } else if (OPTION.equals(element)) { String label = (String) attrs.get(ATTR_LABEL); if(label != null){ currentOptionLabels.add(label); } else{ //TODO not valid: required attribute is missing return; } String value = (String) attrs.get(ATTR_VALUE); if(value != null){ currentOptionValues.add(value); } else{ //TODO not valid: required attribute is missing return; } } else if (ICON.equals(element)) { String resource = (String) attrs.get(ATTR_RESOURCE); if(resource == null){ //TODO not valid: required attribute is missing return; } String sizeS = (String) attrs.get(ATTR_SIZE); int size; if(sizeS != null){ size = Integer.parseInt(sizeS); } else{ //TODO not valid: required attribute is missing return; } currentOCD.addIcon(size, resource); } } protected static void endElement(String element, String content) throws Exception { if (METADATA.equals(element) || element.endsWith(METADATA)) { currentBMTR.addMetaData(currentMetaData); currentMetaData.prepare(); currentMetaData = null; } else if (OCD.equals(element)) { currentMetaData.addOCD(currentOCD); currentOCD = null; } else if (AD_E.equals(element)) { currentOCD.add(currentAD, currentAD.getRequired()); String[] optionValues = null; String[] optionLabels = null; int number; if((number = currentOptionValues.size()) > 0){ optionValues = (String[]) currentOptionValues.toArray(new String[number]); //same number optionLabels = (String[]) currentOptionLabels.toArray(new String[number]); } currentAD.setOptions(optionValues, optionLabels); currentOptionValues.removeAllElements(); currentOptionLabels.removeAllElements(); currentAD = null; } else if (DESIGNATE.equals(element)) { //MetaInfo currentMetaData.designate(currentDesignateFactoryPid, currentDesignatePid, currentObjectOCDref, currentConf, currentAttributes); currentDesignatePid = null; currentDesignateFactoryPid = null; currentAttributes = null; currentObjectOCDref = null; currentConf = null; } //seems like not sure yet see: http://membercvs.osgi.org/bugs/show_bug.cgi?id=129 else if (VALUE.equals(element) || CONTENT.equals(element)) { currentAE.addValue(content); } } static int getType(String strType) { int type = -1; if("Integer".equals(strType)) { type = AttributeDefinition.INTEGER; } else if("String".equals(strType)) { type = AttributeDefinition.STRING; } else if("Boolean".equals(strType)) { type = AttributeDefinition.BOOLEAN; } else if("Float".equals(strType)) { type = AttributeDefinition.FLOAT; } else if("Long".equals(strType)) { type = AttributeDefinition.LONG; } else if("Short".equals(strType)) { type = AttributeDefinition.SHORT; } else if("Char".equals(strType)) { type = AttributeDefinition.CHARACTER; } else if("Double".equals(strType)) { type = AttributeDefinition.DOUBLE; } else { throw new IllegalArgumentException("Unsupported type '" + strType); } return type; } } //class class XMLException extends IllegalArgumentException { XMLElement el; private XMLException() { } public XMLException(XMLElement el) { this("", el); } public XMLException(String msg, XMLElement el) { super(msg + ", line=" + el.getLineNr()); this.el = el; } public XMLElement getXMLElement() { return el; } } class CMConfig { public String pid; public int maxInstances = 1; public AD[] ads; public String desc; public String iconURL; public CMConfig(String pid, AD[] ads, String desc, String iconURL, int maxInstances) { this.pid = pid; this.ads = ads; this.desc = desc != null ? desc : ""; this.iconURL = iconURL; this.maxInstances = maxInstances; } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("CMConfig["); sb.append("pid=" + pid); sb.append(", desc=" + desc); sb.append(", iconURL=" + iconURL); sb.append(", maxInstances=" + maxInstances); sb.append(", attribs="); for(int i = 0; i < ads.length; i++) { sb.append(ads[i]); if(i < ads.length - 1) { sb.append(", "); } } return sb.toString(); } } class Annotation { String appinfo; String doc; public Annotation() { } }