Package org.knopflerfish.util.metatype

Source Code of org.knopflerfish.util.metatype.Annotation

/*
* Copyright (c) 2003-2010, 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.
*
* <p> This implementaion uses the nanoxml package for KF Metatype
* XML, and kXML for R4 Metatype XML.</p>
*
* <p>
* NanoXML is distributed under the zlib/libpng license.<br>
* See <a href="http://nanoxml.sourceforge.net/orig/copyright.html">http://nanoxml.sourceforge.net/orig/copyright.html</a>
* for details.<br>
* The full license text is also include in the kf_metatype bundle jar.
* </p>
*
* <p>Nanoxml is Copyrighted 2000-2002 Marc De Scheemaecker, All
* Rights Reserved.  </p>
*/
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.
   *
   * <ol>
   <li>Load all service and factory definitions into
   *      a MetaTypeProvider instance.
   *  <li>Load any default data.
   *  <li>Insert default data into definitions in MetaTypeProvider
   *      using the <tt>setDefaultValues</tt> method.
   * </ol>
   *
   */
  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 <tt>AD</tt>
   * @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.
   *
   *<p>
   * Children to this tag must all be "xsd:schema"
   *</p>
   */
  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.
   *
   * <p>
   * Each schema element must contain exacly one child "xsd:complexType"
   * </p>
   */
  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 <tt>AttributeDefinition</tt>,
   * definining the metadata for a PID.
   *
   * <p>
   * The name of the complexTyp specifies the metadata PID.
   * </p>
   *
   * <p>
   * The following child elements are supported:
   * <ul>
   * <li><b>xsd:element</b> - defines a singleton definition
   * <li><b>xsd:sequence</b> - defines a vector or array definition
   * </ul>
   *
   * The names of the elements definines the ID's of the definitions. Each
   * ID must only be present once.
   * </p>
   *
   * @throws XMLException if the <tt>el</tt> 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.err.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 <tt>AttributeDefinition</tt> of either
   * vector or array type.
   *
   * <p>
   * Only one child name "element" is allowed, and this child specifies
   * the element type of the vector/array.
   * </p>
   *
   * @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 <tt>AttributeDefinition</tt>.
   *
   * <p>The type of the definition is derived using the
   * <tt>getType</tt> method.</p>
   *
   * <p>If the XSD element is a sequence, parse using the
   * <tt>parseSequence</tt> method.</p>
   */
  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 <tt>AttributeDefinition</tt> type from XSD element type.
   *
   * @return One of the <tt>AttributeDefinition.STRING...BOOLEAN</tt> 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("<?xml version=\"1.0\"?>");
    }

    if(bMetatypeTag) {
      out.println("<metatype:metatype\n" +
                  "  xmlns:metatype=\"http://www.knopflerfish.org/XMLMetatype\"\n" +
                  "  xmlns:xsd     = \"http://www.w3.org/2001/XMLSchema\">");
    }
    out.println("");

    out.println("   <xsd:schema>\n");
    printOCDXML(mtp, servicePIDs, 1, out);
    out.println("");

    printOCDXML(mtp, factoryPIDs, Integer.MAX_VALUE, out);
    out.println("");

    out.println("   </xsd:schema>\n");

    if(propList != null) {
      printValuesXML(propList, false, out);
    }

    if(bMetatypeTag) {
      out.println("</metatype:metatype>");
    }
  }

  /**
   * 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("    <!-- " +
                  (maxOccurs > 1 ? "Factory " : "Service ") +
                  pid + " -->");
      //      out.println("   <xsd:schema>");
      out.print ("    <xsd:complexType " + ATTR_NAME + "=\"" + pid + "\"");
      out.print (" " + ATTR_MAXOCCURS + "=\"" +
                 (maxOccurs == Integer.MAX_VALUE
                  ? UNBOUNDED
                  : Integer.toString(maxOccurs))
                 + "\"");

      if(ocd instanceof OCD) {
        OCD o2 = (OCD)ocd;
        String urlStr = o2.getIconURL(0);
        if(urlStr != null) {
          out.print(" " + ATTR_ICONURL + "=\"" + urlStr + "\"");
        }
      }
      out.println(">");
      printAnnotation(ocd.getDescription(), "     ", out);
      for(int j = 0; j < ads.length; j++) {
        printXML(out, ads[j]);
      }
      out.println("    </xsd:complexType>");
      //      out.println("   </xsd:schema>\n");
    }
  }


  static void printXMLSequence(PrintWriter out,
                               AttributeDefinition ad,
                               boolean bArray) {
    out.println("     <xsd:complexType " + ATTR_NAME + " = \"" + ad.getID() + "\">");
    out.println("      <xsd:sequence " + ATTR_ARRAY + "=\"" + bArray + "\">");
    out.println("       <xsd:element " + ATTR_NAME + " = \"" + ITEM +
                "\" " + ATTR_TYPE + "= \"" +
                getXSDType(ad.getType()) + "\"/>");
    out.println("      </xsd:sequence>");
    out.println("     </xsd:complexType>");

  }

  /**
   * 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("      <xsd:simpleType name = \"" + ad.getID() + "\">");
      out.println("       <xsd:restriction base=\"" + tag + "\">");
      for(int i = 0; i < optValues.length; i++) {
        out.println("       <xsd:enumeration value=\"" + optValues[i] + "\">");
        if(optLabels != null) {
          printAnnotation(optLabels[i], "        ", out);
        }
        out.println("       </xsd:enumeration>");
      }
      out.println("       </xsd:restriction>");
      out.println("      </xsd:simpleType>");
    } else {
      if("".equals(desc)) {
        out.println("     <xsd:element name=\"" + ad.getID() + "\"" +
                    " type=\"" + tag + "\"/>");
      } else {
        out.println("     <xsd:element name=\"" + ad.getID() + "\"" +
                    " type=\"" + tag + "\">");
        printAnnotation(desc, "      ", out);
        out.println("     </xsd:element>");
      }
    }
  }

  public static void printValuesXML(List propList,
                                    boolean bXMLHeader,
                                    PrintWriter out) {

    if(bXMLHeader) {
      out.println("<?xml version=\"1.0\"?>");
    }

    out.println(" <metatype:values\n" +         "  xmlns:metatype=\"http://www.knopflerfish.org/XMLMetatype\">" );
    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("  <!-- pid " + pid + " -->");
      out.println("  <" + pid + ">");
      printPropertiesXML(props, out);
      out.println("  </" + pid + ">");
    }

    out.println(" </metatype:values>");
  }

  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("    <item>" + v.elementAt(i) + "</item>");
        }
        out.println("   </" + key + ">");
      } else if(val.getClass().isArray()) {
        out.println("   <" + key + ">");
        for(int i = 0; i < Array.getLength(val); i++) {
          out.println("    <item>" + Array.get(val, i) + "</item>");
        }
        out.println("   </" + key + ">");
      } else {
        out.println("   <" + key + ">" + val.toString() + "</" + key + ">");
      }
    }
  }


  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 + "<xsd:annotation>");
    out.println(prefix + " <xsd:documentation>" + s + "</xsd:documentation>");
    out.println(prefix + "</xsd:annotation>");
  }

  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() {
  }
}
TOP

Related Classes of org.knopflerfish.util.metatype.Annotation

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.