You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ma...@apache.org on 2009/06/27 17:53:26 UTC

svn commit: r788992 [17/25] - in /incubator/ace/trunk: gateway/ gateway/src/ gateway/src/net/ gateway/src/net/luminis/ gateway/src/net/luminis/liq/ gateway/src/net/luminis/liq/bootstrap/ gateway/src/net/luminis/liq/bootstrap/multigateway/ gateway/src/n...

Added: incubator/ace/trunk/server/src/org/apache/felix/metatype/AD.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/org/apache/felix/metatype/AD.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/org/apache/felix/metatype/AD.java (added)
+++ incubator/ace/trunk/server/src/org/apache/felix/metatype/AD.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,477 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.metatype;
+
+
+import java.util.*;
+
+import org.apache.felix.metatype.internal.Activator;
+import org.osgi.service.log.LogService;
+import org.osgi.service.metatype.AttributeDefinition;
+
+
+/**
+ * The <code>AD</code> class represents the <code>AD</code> element of the
+ * meta type descriptor.
+ *
+ * @author fmeschbe
+ */
+public class AD
+{
+
+    /**
+     * The message returned from the {@link #validate(String)} method if the
+     * value is not any of the specified {@link #getOptionValues() option values}
+     * (value is "%not a valid option").
+     */
+    public static final String VALIDATE_NOT_A_VALID_OPTION = "%not a valid option";
+
+    /**
+     * The message returned from the {@link #validate(String)} method if the
+     * value is greater than the specified {@link #getMax() maximum value}
+     * (value is "%greater than maximum").
+     */
+    public static final String VALIDATE_GREATER_THAN_MAXIMUM = "%greater than maximum";
+
+    /**
+     * The message returned from the {@link #validate(String)} method if the
+     * value is less than the specified {@link #getMin() minimum value}
+     * (value is "%less than minimum").
+     */
+    public static final String VALIDATE_LESS_THAN_MINIMUM = "%less than minimum";
+
+    private String id;
+    private String name;
+    private String description;
+    private int type;
+    private int cardinality = 0;
+    private String[] optionLabels;
+    private String[] optionValues;
+    private String[] defaultValue;
+    private String min;
+    private String max;
+    private boolean isRequired = true;
+
+
+    public String getID()
+    {
+        return id;
+    }
+
+
+    public String getName()
+    {
+        return name;
+    }
+
+
+    public String getDescription()
+    {
+        return description;
+    }
+
+
+    public int getType()
+    {
+        return type;
+    }
+
+
+    public int getCardinality()
+    {
+        return cardinality;
+    }
+
+
+    public String[] getOptionLabels()
+    {
+        return optionLabels;
+    }
+
+
+    public String[] getOptionValues()
+    {
+        return optionValues;
+    }
+
+
+    public String[] getDefaultValue()
+    {
+        return defaultValue;
+    }
+
+
+    public String getMin()
+    {
+        return min;
+    }
+
+
+    public String getMax()
+    {
+        return max;
+    }
+
+
+    public boolean isRequired()
+    {
+        return isRequired;
+    }
+
+
+    /**
+     * Implements validation of the <code>valueString</code> and returns an
+     * indication of the success:
+     * <dl>
+     * <dt><code>null</code>
+     * <dd>If neither a {@link #getMin() minimal value} nor a
+     *      {@link #getMax() maximal value} nor any
+     *      {@link #getOptionValues() optional values} are defined in this
+     *      instance, validation cannot be performed.
+     * <dt>Empty String
+     * <dd>If validation succeeds. This value is also returned if the
+     *      <code>valueString</code> is empty or <code>null</code> or cannot be
+     *      converted into a numeric type.
+     * <dt><b>%</b>message
+     * <dd>If the value falls below the minimum, higher than the maximum or is
+     *      not any of the option values, an explanatory message, which may be
+     *      localized is returned. If any of the minimum, maximum or option
+     *      values is <code>null</code>, the respective value is not checked.
+     * </dl>
+     *
+     * @param valueString The string representation of the value to validate.
+     *
+     * @return As explained above.
+     *
+     * @see #VALIDATE_GREATER_THAN_MAXIMUM
+     * @see #VALIDATE_LESS_THAN_MINIMUM
+     * @see #VALIDATE_NOT_A_VALID_OPTION
+     */
+    public String validate( String valueString )
+    {
+        // no validation if no min and max
+        if ( getMin() == null && getMax() == null && getOptionValues() == null )
+        {
+            return null;
+        }
+
+        Comparable value = convertToType( valueString );
+        if ( value == null )
+        {
+            return ""; // accept null value
+        }
+
+        Comparable other = convertToType( getMin() );
+        if ( other != null )
+        {
+            if ( value.compareTo( other ) < 0 )
+            {
+                return VALIDATE_LESS_THAN_MINIMUM;
+            }
+        }
+
+        other = convertToType( getMax() );
+        if ( other != null )
+        {
+            if ( value.compareTo( other ) > 0 )
+            {
+                return VALIDATE_GREATER_THAN_MAXIMUM;
+            }
+        }
+
+        String[] optionValues = getOptionValues();
+        if ( optionValues != null )
+        {
+            for ( int i = 0; i < optionValues.length; i++ )
+            {
+                other = convertToType( optionValues[i] );
+                if ( value.compareTo( other ) == 0 )
+                {
+                    // one of the option values
+                    return "";
+                }
+            }
+
+            // not any of the option values, fail
+            return VALIDATE_NOT_A_VALID_OPTION;
+        }
+
+        // finally, we accept the value
+        return "";
+    }
+
+
+    //--------- Setters for setting up this instance --------------------------
+
+    /**
+     * @param id the id to set
+     */
+    public void setID( String id )
+    {
+        this.id = id;
+    }
+
+
+    /**
+     * @param name the name to set
+     */
+    public void setName( String name )
+    {
+        this.name = name;
+    }
+
+
+    /**
+     * @param description the description to set
+     */
+    public void setDescription( String description )
+    {
+        this.description = description;
+    }
+
+
+    /**
+     * @param typeString the type to set
+     */
+    public void setType( String typeString )
+    {
+        this.type = toType( typeString );
+    }
+
+
+    /**
+     * @param cardinality the cardinality to set
+     */
+    public void setCardinality( int cardinality )
+    {
+        this.cardinality = cardinality;
+    }
+
+
+    /**
+     * @param options the options to set
+     */
+    public void setOptions( Map options )
+    {
+        optionLabels = new String[options.size()];
+        optionValues = new String[options.size()];
+        int i = 0;
+        for ( Iterator oi = options.entrySet().iterator(); oi.hasNext(); i++ )
+        {
+            Map.Entry entry = ( Map.Entry ) oi.next();
+            optionValues[i] = String.valueOf( entry.getKey() );
+            optionLabels[i] = String.valueOf( entry.getValue() );
+        }
+    }
+
+
+    /**
+     * @param defaultValue the defaultValue to set
+     */
+    public void setDefaultValue( String defaultValue )
+    {
+        this.defaultValue = splitList( defaultValue );
+    }
+
+
+    /**
+     * @param min the min to set
+     */
+    public void setMin( String min )
+    {
+        this.min = min;
+    }
+
+
+    /**
+     * @param max the max to set
+     */
+    public void setMax( String max )
+    {
+        this.max = max;
+    }
+
+
+    /**
+     * @param defaultValue the defaultValue to set
+     */
+    public void setDefaultValue( String[] defaultValue )
+    {
+        this.defaultValue = ( String[] ) defaultValue.clone();
+    }
+
+
+    /**
+     * @param isRequired the isRequired to set
+     */
+    public void setRequired( boolean isRequired )
+    {
+        this.isRequired = isRequired;
+    }
+
+
+    public static int toType( String typeString )
+    {
+        if ( "String".equals( typeString ) )
+        {
+            return AttributeDefinition.STRING;
+        }
+        else if ( "Long".equals( typeString ) )
+        {
+            return AttributeDefinition.LONG;
+        }
+        else if ( "Double".equals( typeString ) )
+        {
+            return AttributeDefinition.DOUBLE;
+        }
+        else if ( "Float".equals( typeString ) )
+        {
+            return AttributeDefinition.FLOAT;
+        }
+        else if ( "Integer".equals( typeString ) )
+        {
+            return AttributeDefinition.INTEGER;
+        }
+        else if ( "Byte".equals( typeString ) )
+        {
+            return AttributeDefinition.BYTE;
+        }
+        else if ( "Char".equals( typeString ) )
+        {
+            return AttributeDefinition.CHARACTER;
+        }
+        else if ( "Boolean".equals( typeString ) )
+        {
+            return AttributeDefinition.BOOLEAN;
+        }
+        else if ( "Short".equals( typeString ) )
+        {
+            return AttributeDefinition.SHORT;
+        }
+
+        // finally fall back to string for illegal values
+        return AttributeDefinition.STRING;
+    }
+
+
+    public static String[] splitList( String listString )
+    {
+        // return nothing ...
+        if ( listString == null )
+        {
+            return null;
+        }
+
+        List values = new ArrayList();
+        boolean escape = false;
+        StringBuffer buf = new StringBuffer();
+        for ( int i = 0; i < listString.length(); i++ )
+        {
+            char c = listString.charAt( i );
+
+            if ( escape )
+            {
+                // just go ahead
+                escape = false;
+            }
+            else if ( c == ',' )
+            {
+                String value = buf.toString().trim();
+                if ( value.length() > 0 )
+                {
+                    values.add( value );
+                }
+                buf.delete( 0, buf.length() );
+                continue;
+            }
+            else if ( c == '\\' )
+            {
+                escape = true;
+                continue;
+            }
+
+            buf.append( c );
+        }
+
+        // add last string
+        if ( buf.length() > 0 )
+        {
+            String value = buf.toString().trim();
+            if ( value.length() > 0 )
+            {
+                values.add( value );
+            }
+        }
+
+        return values.isEmpty() ? null : ( String[] ) values.toArray( new String[values.size()] );
+    }
+
+
+    protected Comparable convertToType( final String value )
+    {
+        if ( value != null && value.length() > 0 )
+        {
+            try
+            {
+                switch ( getType() )
+                {
+                    case AttributeDefinition.BOOLEAN:
+                        // Boolean is only Comparable starting with Java 5
+                        return new ComparableBoolean(value);
+                    case AttributeDefinition.CHARACTER:
+                        return new Character( value.charAt( 0 ) );
+                    case AttributeDefinition.BYTE:
+                        return Byte.valueOf( value );
+                    case AttributeDefinition.SHORT:
+                        return Short.valueOf( value );
+                    case AttributeDefinition.INTEGER:
+                        return Integer.valueOf( value );
+                    case AttributeDefinition.LONG:
+                        return Long.valueOf( value );
+                    case AttributeDefinition.FLOAT:
+                        return Float.valueOf( value );
+                    case AttributeDefinition.DOUBLE:
+                        return Double.valueOf( value );
+                    case AttributeDefinition.STRING:
+                    default:
+                        return value;
+                }
+            }
+            catch ( NumberFormatException nfe )
+            {
+                Activator.log( LogService.LOG_INFO, "Cannot convert value '" + value + "'", nfe );
+            }
+        }
+
+        return null;
+    }
+
+    private static class ComparableBoolean implements Comparable {
+        private boolean value;
+
+        ComparableBoolean(String boolValue) {
+            value = Boolean.valueOf(boolValue).booleanValue();
+        }
+
+        public int compareTo(Object obj) {
+            ComparableBoolean cb = (ComparableBoolean) obj;
+            return (cb.value == value ? 0 : (value ? 1 : -1));
+        }
+    }
+}

Added: incubator/ace/trunk/server/src/org/apache/felix/metatype/Attribute.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/org/apache/felix/metatype/Attribute.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/org/apache/felix/metatype/Attribute.java (added)
+++ incubator/ace/trunk/server/src/org/apache/felix/metatype/Attribute.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,78 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.metatype;
+
+
+/**
+ * The <code>Attribute</code> TODO
+ *
+ * @author fmeschbe
+ * @version $Rev:$, $Date: 2007-04-11 20:27:12 +0200 (Wed, 11 Apr 2007) $
+ */
+public class Attribute
+{
+
+    private String adRef;
+    private String[] content;
+
+
+    public String getAdRef()
+    {
+        return adRef;
+    }
+
+
+    public void setAdRef( String adRef )
+    {
+        this.adRef = adRef;
+    }
+
+
+    public String[] getContent()
+    {
+        return ( String[] ) content.clone();
+    }
+
+
+    public void addContent( String[] added )
+    {
+        if ( added != null && added.length > 0 )
+        {
+            if ( content == null )
+            {
+                content = ( String[] ) added.clone();
+            }
+            else
+            {
+                String[] newContent = new String[content.length + added.length];
+                System.arraycopy( content, 0, newContent, 0, content.length );
+                System.arraycopy( added, 0, newContent, content.length, added.length );
+            }
+        }
+    }
+
+
+    public void addContent( String content )
+    {
+        if ( content != null )
+        {
+            addContent( AD.splitList( content ) );
+        }
+    }
+}

Added: incubator/ace/trunk/server/src/org/apache/felix/metatype/DefaultMetaTypeProvider.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/org/apache/felix/metatype/DefaultMetaTypeProvider.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/org/apache/felix/metatype/DefaultMetaTypeProvider.java (added)
+++ incubator/ace/trunk/server/src/org/apache/felix/metatype/DefaultMetaTypeProvider.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,197 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.metatype;
+
+
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.felix.metatype.internal.LocalizedObjectClassDefinition;
+import org.apache.felix.metatype.internal.l10n.BundleResources;
+import org.apache.felix.metatype.internal.l10n.Resources;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.service.metatype.MetaTypeProvider;
+import org.osgi.service.metatype.MetaTypeService;
+import org.osgi.service.metatype.ObjectClassDefinition;
+
+
+/**
+ * The <code>DefaultMetaTypeProvider</code> class is an implementation of the
+ * <code>MetaTypeProvider</code> interface whichis configured for a given
+ * bundle using a {@link MetaData} object.
+ * <p>
+ * This class may be used by clients, e.g. <code>ManagedService</code> or
+ * <code>ManagedServiceFactory</code> implementations to easily also implement
+ * the <code>MetaTypeProvider</code> interface. 
+ *
+ * @author fmeschbe
+ */
+public class DefaultMetaTypeProvider implements MetaTypeProvider
+{
+
+    private final Bundle bundle;
+
+    private String localePrefix;
+    private Map objectClassDefinitions;
+    private Map designates;
+
+    private Map locales;
+
+
+    public DefaultMetaTypeProvider( Bundle bundle, MetaData metadata )
+    {
+        this.bundle = bundle;
+
+        // copy from holder
+        if ( metadata.getObjectClassDefinitions() == null )
+        {
+            objectClassDefinitions = Collections.EMPTY_MAP;
+        }
+        else
+        {
+            Map copy = new HashMap( metadata.getObjectClassDefinitions() );
+            objectClassDefinitions = Collections.unmodifiableMap( copy );
+        }
+        if ( metadata.getDesignates() == null )
+        {
+            designates = Collections.EMPTY_MAP;
+        }
+        else
+        {
+            Map copy = new HashMap( metadata.getDesignates() );
+            designates = Collections.unmodifiableMap( copy );
+        }
+
+        localePrefix = metadata.getLocalePrefix();
+        if ( localePrefix == null )
+        {
+            localePrefix = ( String ) bundle.getHeaders().get( Constants.BUNDLE_LOCALIZATION );
+            if ( localePrefix == null )
+            {
+                localePrefix = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
+            }
+        }
+        else
+        {
+            localePrefix = MetaTypeService.METATYPE_DOCUMENTS_LOCATION + "/" + localePrefix;
+        }
+    }
+
+
+    /**
+     * Returns the <code>Bundle</code> to which this instance belongs.
+     */
+    public Bundle getBundle()
+    {
+        return bundle;
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.osgi.service.metatype.MetaTypeProvider#getLocales()
+     */
+    public String[] getLocales()
+    {
+        if ( locales == null )
+        {
+            String path;
+            String pattern;
+            int lastSlash = localePrefix.lastIndexOf( '/' );
+            if ( lastSlash < 0 )
+            {
+                path = "/";
+                pattern = localePrefix;
+            }
+            else
+            {
+                path = localePrefix.substring( 0, lastSlash );
+                pattern = localePrefix.substring( lastSlash + 1 );
+            }
+
+            Enumeration entries = getBundle().findEntries( path, pattern + "*.properties", false );
+            locales = new TreeMap();
+            while ( entries.hasMoreElements() )
+            {
+                URL url = ( URL ) entries.nextElement();
+                String name = url.getPath();
+                name = name.substring( name.lastIndexOf( '/' ) + 1 + pattern.length(), name.length()
+                    - ".properties".length() );
+                if ( name.startsWith( "_" ) )
+                {
+                    name = name.substring( 1 );
+                }
+                locales.put( name, url );
+            }
+        }
+
+        // no locales found
+        if ( locales.isEmpty() )
+        {
+            return null;
+        }
+
+        return ( String[] ) locales.keySet().toArray( new String[locales.size()] );
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.osgi.service.metatype.MetaTypeProvider#getObjectClassDefinition(java.lang.String, java.lang.String)
+     */
+    public ObjectClassDefinition getObjectClassDefinition( String id, String locale )
+    {
+        Designate designate = getDesignate( id );
+        if ( designate == null || designate.getObject() == null )
+        {
+            return null;
+        }
+
+        String ocdRef = designate.getObject().getOcdRef();
+        OCD ocd = ( OCD ) objectClassDefinitions.get( ocdRef );
+        if ( ocd == null )
+        {
+            return null;
+        }
+
+        Resources resources = BundleResources.getResources( bundle, localePrefix, locale );
+        return new LocalizedObjectClassDefinition( bundle, ocd, resources );
+    }
+
+
+    public Designate getDesignate( String pid )
+    {
+        return ( Designate ) designates.get( pid );
+    }
+    
+    protected Map getObjectClassDefinitions()
+    {
+        return objectClassDefinitions;
+    }
+
+
+    protected Map getDesignates()
+    {
+        return designates;
+    }
+
+}

Added: incubator/ace/trunk/server/src/org/apache/felix/metatype/Designate.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/org/apache/felix/metatype/Designate.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/org/apache/felix/metatype/Designate.java (added)
+++ incubator/ace/trunk/server/src/org/apache/felix/metatype/Designate.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,150 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.metatype;
+
+
+/**
+ * The <code>Designate</code> class represents the <code>Designate</code>
+ * element of the meta type descriptor.
+ * 
+ * @author fmeschbe
+ */
+public class Designate
+{
+
+    private String pid;
+
+    private String factoryPid;
+
+    private String bundleLocation;
+
+    private boolean optional;
+
+    private boolean merge;
+
+    private DesignateObject object;
+
+
+    /**
+     * @return the bundleLocation
+     */
+    public String getBundleLocation()
+    {
+        return bundleLocation;
+    }
+
+
+    /**
+     * @return the factoryPid
+     */
+    public String getFactoryPid()
+    {
+        return factoryPid;
+    }
+
+
+    /**
+     * @return the merge
+     */
+    public boolean isMerge()
+    {
+        return merge;
+    }
+
+
+    /**
+     * @return the optional
+     */
+    public boolean isOptional()
+    {
+        return optional;
+    }
+
+
+    /**
+     * @return the pid
+     */
+    public String getPid()
+    {
+        return pid;
+    }
+
+
+    /**
+     * @return the object
+     */
+    public DesignateObject getObject()
+    {
+        return object;
+    }
+
+
+    /**
+     * @param bundleLocation the bundleLocation to set
+     */
+    public void setBundleLocation( String bundleLocation )
+    {
+        this.bundleLocation = bundleLocation;
+    }
+
+
+    /**
+     * @param factoryPid the factoryPid to set
+     */
+    public void setFactoryPid( String factoryPid )
+    {
+        this.factoryPid = factoryPid;
+    }
+
+
+    /**
+     * @param merge the merge to set
+     */
+    public void setMerge( boolean merge )
+    {
+        this.merge = merge;
+    }
+
+
+    /**
+     * @param optional the optional to set
+     */
+    public void setOptional( boolean optional )
+    {
+        this.optional = optional;
+    }
+
+
+    /**
+     * @param pid the pid to set
+     */
+    public void setPid( String pid )
+    {
+        this.pid = pid;
+    }
+
+
+    /**
+     * @param object the object to set
+     */
+    public void setObject( DesignateObject object )
+    {
+        this.object = object;
+    }
+}

Added: incubator/ace/trunk/server/src/org/apache/felix/metatype/DesignateObject.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/org/apache/felix/metatype/DesignateObject.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/org/apache/felix/metatype/DesignateObject.java (added)
+++ incubator/ace/trunk/server/src/org/apache/felix/metatype/DesignateObject.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,68 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.metatype;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * The <code>DesignateObject</code> class represents the <code>Object</code> element of
+ * the meta type descriptor.
+ * 
+ * @author fmeschbe
+ */
+public class DesignateObject
+{
+
+    private String ocdRef;
+    private List attributes;
+
+
+    public String getOcdRef()
+    {
+        return ocdRef;
+    }
+
+
+    public void setOcdRef( String ocdRef )
+    {
+        this.ocdRef = ocdRef;
+    }
+
+
+    public List getAttributes()
+    {
+        return attributes;
+    }
+
+
+    public void addAttribute( Attribute attribute )
+    {
+        if ( attribute != null )
+        {
+            if ( attributes == null )
+            {
+                attributes = new ArrayList();
+            }
+            attributes.add( attribute );
+        }
+    }
+}

Added: incubator/ace/trunk/server/src/org/apache/felix/metatype/MetaData.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/org/apache/felix/metatype/MetaData.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/org/apache/felix/metatype/MetaData.java (added)
+++ incubator/ace/trunk/server/src/org/apache/felix/metatype/MetaData.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,91 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.metatype;
+
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+
+/**
+ * The <code>MetaData</code> class represents the <code>MetaData</code>
+ * element of the meta type descriptor.
+ * 
+ * @author fmeschbe
+ */
+public class MetaData
+{
+
+    private String localePrefix;
+    private Map objectClassDefinitions;
+    private Map designates;
+
+
+    public String getLocalePrefix()
+    {
+        return localePrefix;
+    }
+
+
+    public void setLocalePrefix( String localePrefix )
+    {
+        this.localePrefix = localePrefix;
+    }
+
+
+    public Map getObjectClassDefinitions()
+    {
+        return objectClassDefinitions;
+    }
+
+
+    public void addObjectClassDefinition( OCD objectClassDefinition )
+    {
+        if ( objectClassDefinition != null )
+        {
+            if ( objectClassDefinitions == null )
+            {
+                objectClassDefinitions = new LinkedHashMap();
+            }
+
+            objectClassDefinitions.put( objectClassDefinition.getID(), objectClassDefinition );
+        }
+    }
+
+
+    public Map getDesignates()
+    {
+        return designates;
+    }
+
+
+    public void addDesignate( Designate designate )
+    {
+        if ( designate != null )
+        {
+            if ( designates == null )
+            {
+                designates = new HashMap();
+            }
+
+            designates.put( designate.getPid(), designate );
+        }
+    }
+}

Added: incubator/ace/trunk/server/src/org/apache/felix/metatype/MetaDataReader.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/org/apache/felix/metatype/MetaDataReader.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/org/apache/felix/metatype/MetaDataReader.java (added)
+++ incubator/ace/trunk/server/src/org/apache/felix/metatype/MetaDataReader.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,577 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.metatype;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.felix.metatype.internal.Activator;
+import org.kxml2.io.KXmlParser;
+import org.osgi.service.log.LogService;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+
+/**
+ * The <code>MetaDataReader</code> provides two methods to read meta type
+ * documents according to the MetaType schema (105.8 XML Schema). The
+ * {@link #parse(URL)} and {@link #parse(InputStream)} methods may be called
+ * multiple times to parse such documents.
+ * <p>
+ * While reading the XML document java objects are created to hold the data.
+ * These objects are created by factory methods. Users of this may extend this
+ * class by overwriting the the factory methods to create specialized versions.
+ * One notable use of this is the extension of the {@link AD} class to overwrite
+ * the {@link AD#validate(String)} method. In this case, the {@link #createAD()}
+ * method would be overwritten to return an instance of the extending class.
+ * <p>
+ * This class is not thread safe. Using instances of this class in multiple
+ * threads concurrently is not supported and will fail.
+ *
+ * @author fmeschbe
+ */
+public class MetaDataReader
+{
+
+    /** The XML parser used to read the XML documents */
+    private KXmlParser parser = new KXmlParser();
+
+
+    /**
+     * Parses the XML document provided by the <code>url</code>. The XML document
+     * must be at the beginning of the stream contents.
+     * <p>
+     * This method is almost identical to
+     * <code>return parse(url.openStream());</code> but also sets the string
+     * representation of the URL as a location helper for error messages.
+     *
+     * @param url The <code>URL</code> providing access to the XML document.
+     *
+     * @return A {@link MetaData} providing access to the
+     *      raw contents of the XML document.
+     *
+     * @throws IOException If an I/O error occurrs accessing the stream.
+     * @throws XmlPullParserException If an error occurrs parsing the XML
+     *      document.
+     */
+    public MetaData parse( URL url ) throws IOException, XmlPullParserException
+    {
+        InputStream ins = null;
+        try
+        {
+            ins = url.openStream();
+            this.parser.setProperty( "http://xmlpull.org/v1/doc/properties.html#location", url.toString() );
+            this.parser.setFeature(KXmlParser.FEATURE_PROCESS_NAMESPACES, true);
+            return this.parse( ins );
+        }
+        finally
+        {
+            if ( ins != null )
+            {
+                try
+                {
+                    ins.close();
+                }
+                catch ( IOException ioe )
+                {
+                    // ignore
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Parses the XML document in the given input stream.
+     * <p>
+     * This method starts reading at the current position of the input stream
+     * and returns immediately after completely reading a single meta type
+     * document. The stream is not closed by this method.
+     *
+     * @param ins The <code>InputStream</code> providing the XML document
+     *
+     * @return A {@link MetaData} providing access to the
+     *      raw contents of the XML document.
+     *
+     * @throws IOException If an I/O error occurrs accessing the stream.
+     * @throws XmlPullParserException If an error occurrs parsing the XML
+     *      document.
+     */
+    public MetaData parse( InputStream ins ) throws IOException, XmlPullParserException
+    {
+        // set the parser input, use null encoding to force detection with <?xml?>
+        this.parser.setInput( ins, null );
+
+        MetaData mti = null;
+
+        int eventType = this.parser.getEventType();
+        while ( eventType != XmlPullParser.END_DOCUMENT )
+        {
+            if ( eventType == XmlPullParser.START_TAG )
+            {
+                if ( "MetaData".equals( this.parser.getName() ) )
+                {
+                    mti = this.readMetaData();
+                }
+                else
+                {
+                    this.ignoreElement();
+                }
+            }
+            eventType = this.parser.next();
+        }
+
+        return mti;
+    }
+
+
+    private MetaData readMetaData() throws IOException, XmlPullParserException
+    {
+        MetaData mti = this.createMetaData();
+        mti.setLocalePrefix( this.getOptionalAttribute( "localization" ) );
+
+        int eventType = this.parser.next();
+        while ( eventType != XmlPullParser.END_DOCUMENT )
+        {
+            if ( eventType == XmlPullParser.START_TAG )
+            {
+                if ( "OCD".equals( this.parser.getName() ) )
+                {
+                    mti.addObjectClassDefinition( this.readOCD() );
+                }
+                else if ( "Designate".equals( this.parser.getName() ) )
+                {
+                    mti.addDesignate( this.readDesignate() );
+                }
+                else
+                {
+                    this.ignoreElement();
+                }
+            }
+            else if ( eventType == XmlPullParser.END_TAG )
+            {
+                if ( "MetaData".equals( this.parser.getName() ) )
+                {
+                    break;
+                }
+
+                throw this.unexpectedElement();
+            }
+            eventType = this.parser.next();
+        }
+
+        return mti;
+    }
+
+
+    private OCD readOCD() throws IOException, XmlPullParserException
+    {
+        OCD ocd = this.createOCD();
+        ocd.setId( this.getRequiredAttribute( "id" ) );
+        ocd.setName( this.getRequiredAttribute( "name" ) );
+        ocd.setDescription( this.getOptionalAttribute( "description" ) );
+
+        int eventType = this.parser.next();
+        while ( eventType != XmlPullParser.END_DOCUMENT )
+        {
+            if ( eventType == XmlPullParser.START_TAG )
+            {
+                if ( "AD".equals( this.parser.getName() ) )
+                {
+                    ocd.addAttributeDefinition( this.readAD() );
+                }
+                else if ( "Icon".equals( this.parser.getName() ) )
+                {
+                    String res = this.getRequiredAttribute( "resource" );
+                    String sizeString = this.getRequiredAttribute( "size" );
+                    try
+                    {
+                        Integer size = Integer.decode( sizeString );
+                        ocd.addIcon( size, res );
+                    }
+                    catch ( NumberFormatException nfe )
+                    {
+                        Activator.log( LogService.LOG_DEBUG, "readOCD: Icon size '" + sizeString
+                            + "' is not a valid number" );
+                    }
+                }
+                else
+                {
+                    this.ignoreElement();
+                }
+            }
+            else if ( eventType == XmlPullParser.END_TAG )
+            {
+                if ( "OCD".equals( this.parser.getName() ) )
+                {
+                    break;
+                }
+                else if ( !"Icon".equals( this.parser.getName() ) )
+                {
+                    throw this.unexpectedElement();
+                }
+            }
+            eventType = this.parser.next();
+        }
+
+        return ocd;
+    }
+
+
+    private Designate readDesignate() throws IOException, XmlPullParserException
+    {
+        Designate designate = this.createDesignate();
+        designate.setPid( this.getRequiredAttribute( "pid" ) );
+        designate.setFactoryPid( this.getOptionalAttribute( "factoryPid" ) );
+        designate.setBundleLocation( this.getOptionalAttribute( "bundle" ) );
+        designate.setOptional( this.getOptionalAttribute( "optional", false ) );
+        designate.setMerge( this.getOptionalAttribute( "merge", false ) );
+
+        int eventType = this.parser.next();
+        while ( eventType != XmlPullParser.END_DOCUMENT )
+        {
+            if ( eventType == XmlPullParser.START_TAG )
+            {
+                if ( "Object".equals( this.parser.getName() ) )
+                {
+                    designate.setObject( this.readObject() );
+                }
+                else
+                {
+                    this.ignoreElement();
+                }
+            }
+            else if ( eventType == XmlPullParser.END_TAG )
+            {
+                if ( "Designate".equals( this.parser.getName() ) )
+                {
+                    break;
+                }
+
+                throw this.unexpectedElement();
+            }
+            eventType = this.parser.next();
+        }
+
+        return designate;
+    }
+
+
+    private AD readAD() throws IOException, XmlPullParserException
+    {
+        AD ad = this.createAD();
+        ad.setID( this.getRequiredAttribute( "id" ) );
+        ad.setName( this.getOptionalAttribute( "name" ) );
+        ad.setDescription( this.getOptionalAttribute( "description" ) );
+        ad.setType( this.getRequiredAttribute( "type" ) );
+        ad.setCardinality( this.getOptionalAttribute( "cardinality", 0 ) );
+        ad.setMin( this.getOptionalAttribute( "min" ) );
+        ad.setMax( this.getOptionalAttribute( "min" ) );
+        ad.setDefaultValue( this.getOptionalAttribute( "default" ) );
+        ad.setRequired( this.getOptionalAttribute( "required", true ) );
+
+        Map options = new LinkedHashMap();
+        int eventType = this.parser.next();
+        while ( eventType != XmlPullParser.END_DOCUMENT )
+        {
+            if ( eventType == XmlPullParser.START_TAG )
+            {
+                if ( "Option".equals( this.parser.getName() ) )
+                {
+                    String value = this.getRequiredAttribute( "value" );
+                    String label = this.getRequiredAttribute( "label" );
+                    options.put( value, label );
+                }
+                else
+                {
+                    this.ignoreElement();
+                }
+            }
+            else if ( eventType == XmlPullParser.END_TAG )
+            {
+                if ( "AD".equals( this.parser.getName() ) )
+                {
+                    break;
+                }
+                else if ( !"Option".equals( this.parser.getName() ) )
+                {
+                    throw this.unexpectedElement();
+                }
+            }
+            eventType = this.parser.next();
+        }
+
+        ad.setOptions( options );
+
+        return ad;
+    }
+
+
+    private DesignateObject readObject() throws IOException, XmlPullParserException
+    {
+        DesignateObject oh = this.createDesignateObject();
+        oh.setOcdRef( this.getRequiredAttribute( "ocdref" ) );
+
+        int eventType = this.parser.next();
+        while ( eventType != XmlPullParser.END_DOCUMENT )
+        {
+            if ( eventType == XmlPullParser.START_TAG )
+            {
+                if ( "Attribute".equals( this.parser.getName() ) )
+                {
+                    oh.addAttribute( this.readAttribute() );
+                }
+                else
+                {
+                    this.ignoreElement();
+                }
+            }
+            else if ( eventType == XmlPullParser.END_TAG )
+            {
+                if ( "Object".equals( this.parser.getName() ) )
+                {
+                    break;
+                }
+                throw this.unexpectedElement();
+            }
+            eventType = this.parser.next();
+        }
+
+        return oh;
+    }
+
+
+    private Attribute readAttribute() throws IOException, XmlPullParserException
+    {
+        Attribute ah = this.createAttribute();
+        ah.setAdRef( this.getRequiredAttribute( "adref" ) );
+        ah.addContent( this.getOptionalAttribute( "content" ) );
+
+        int eventType = this.parser.next();
+        while ( eventType != XmlPullParser.END_DOCUMENT )
+        {
+            if ( eventType == XmlPullParser.START_TAG )
+            {
+                if ( "Value".equals( this.parser.getName() ) )
+                {
+                    ah.addContent( this.parser.nextText() );
+                    eventType = this.parser.getEventType();
+                    continue;
+                }
+                this.ignoreElement();
+            }
+            else if ( eventType == XmlPullParser.END_TAG )
+            {
+                if ( "Attribute".equals( this.parser.getName() ) )
+                {
+                    break;
+                }
+                else if ( !"Value".equals( this.parser.getName() ) )
+                {
+                    throw this.unexpectedElement();
+                }
+            }
+            eventType = this.parser.next();
+        }
+
+        return ah;
+    }
+
+
+    //---------- Attribute access helper --------------------------------------
+
+    private String getRequiredAttribute( String attrName ) throws XmlPullParserException
+    {
+        String attrVal = this.parser.getAttributeValue( null, attrName );
+        if ( attrVal != null )
+        {
+            return attrVal;
+        }
+
+        // fail if value is missing
+        throw this.missingAttribute( attrName );
+    }
+
+
+    private String getOptionalAttribute( String attrName )
+    {
+        return this.getOptionalAttribute( attrName, ( String ) null );
+    }
+
+
+    private String getOptionalAttribute( String attrName, String defaultValue )
+    {
+        String attrVal = this.parser.getAttributeValue( null, attrName );
+        return ( attrVal != null ) ? attrVal : defaultValue;
+    }
+
+
+    private boolean getOptionalAttribute( String attrName, boolean defaultValue )
+    {
+        String attrVal = this.parser.getAttributeValue( null, attrName );
+        return ( attrVal != null ) ? "true".equalsIgnoreCase( attrVal ) : defaultValue;
+    }
+
+
+    private int getOptionalAttribute( String attrName, int defaultValue )
+    {
+        String attrVal = this.parser.getAttributeValue( null, attrName );
+        if ( attrVal != null && attrVal.length() > 0 )
+        {
+            try
+            {
+                return Integer.decode( attrVal ).intValue();
+            }
+            catch ( NumberFormatException nfe )
+            {
+                Activator.log( LogService.LOG_DEBUG, "getOptionalAttribute: Value '" + attrVal + "' of attribute "
+                    + attrName + " is not a valid number. Using default value " + defaultValue );
+            }
+        }
+
+        // fallback to default
+        return defaultValue;
+    }
+
+
+    //---------- Error Handling support ---------------------------------------
+
+    private void ignoreElement() throws IOException, XmlPullParserException
+    {
+        String ignoredElement = this.parser.getName();
+
+        int depth = 0; // enable nested ignored elements
+        int eventType = this.parser.next();
+        while ( eventType != XmlPullParser.END_DOCUMENT )
+        {
+            if ( eventType == XmlPullParser.START_TAG )
+            {
+                if ( ignoredElement.equals( this.parser.getName() ) )
+                {
+                    depth++;
+                }
+            }
+            else if ( eventType == XmlPullParser.END_TAG )
+            {
+                if ( ignoredElement.equals( this.parser.getName() ) )
+                {
+                    if ( depth <= 0 )
+                    {
+                        return;
+                    }
+
+                    depth--;
+                }
+            }
+            eventType = this.parser.next();
+        }
+    }
+
+
+    private XmlPullParserException missingAttribute( String attrName )
+    {
+        String message = "Missing Attribute " + attrName + " in element " + this.parser.getName();
+        return new XmlPullParserException( message, this.parser, null );
+    }
+
+
+    private XmlPullParserException unexpectedElement()
+    {
+        String message = "Illegal Element " + this.parser.getName();
+        return new XmlPullParserException( message, this.parser, null );
+    }
+
+
+    //---------- Factory methods ----------------------------------------------
+
+    /**
+     * Creates a new {@link MetaData} object to hold the contents of the
+     * <code>MetaData</code> element.
+     * <p>
+     * This method may be overwritten to return a customized extension.
+     */
+    protected MetaData createMetaData()
+    {
+        return new MetaData();
+    }
+
+
+    /**
+     * Creates a new {@link OCD} object to hold the contents of the
+     * <code>OCD</code> element.
+     * <p>
+     * This method may be overwritten to return a customized extension.
+     */
+    protected OCD createOCD()
+    {
+        return new OCD();
+    }
+
+
+    /**
+     * Creates a new {@link AD} object to hold the contents of the
+     * <code>AD</code> element.
+     * <p>
+     * This method may be overwritten to return a customized extension.
+     */
+    protected AD createAD()
+    {
+        return new AD();
+    }
+
+
+    /**
+     * Creates a new {@link DesignateObject} object to hold the contents of the
+     * <code>Object</code> element.
+     * <p>
+     * This method may be overwritten to return a customized extension.
+     */
+    protected DesignateObject createDesignateObject()
+    {
+        return new DesignateObject();
+    }
+
+
+    /**
+     * Creates a new {@link Attribute} object to hold the contents of the
+     * <code>Attribute</code> element.
+     * <p>
+     * This method may be overwritten to return a customized extension.
+     */
+    protected Attribute createAttribute()
+    {
+        return new Attribute();
+    }
+
+
+    /**
+     * Creates a new {@link Designate} object to hold the contents of the
+     * <code>Designate</code> element.
+     * <p>
+     * This method may be overwritten to return a customized extension.
+     */
+    protected Designate createDesignate()
+    {
+        return new Designate();
+    }
+}

Added: incubator/ace/trunk/server/src/org/apache/felix/metatype/OCD.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/org/apache/felix/metatype/OCD.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/org/apache/felix/metatype/OCD.java (added)
+++ incubator/ace/trunk/server/src/org/apache/felix/metatype/OCD.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,123 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.metatype;
+
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+
+/**
+ * The <code>OCD</code> class represents the <code>OCD</code> element of the
+ * meta type descriptor.
+ *
+ * @author fmeschbe
+ */
+public class OCD
+{
+
+    private String id;
+    private String name;
+    private String description;
+    private Map attributes;
+    private Map icons;
+
+
+    public String getID()
+    {
+        return id;
+    }
+
+
+    public void setId( String id )
+    {
+        this.id = id;
+    }
+
+
+    public String getName()
+    {
+        return name;
+    }
+
+
+    public void setName( String name )
+    {
+        this.name = name;
+    }
+
+
+    public String getDescription()
+    {
+        return description;
+    }
+
+
+    public void setDescription( String description )
+    {
+        this.description = description;
+    }
+
+
+    public Map getIcons()
+    {
+        return icons;
+    }
+
+
+    /**
+     * 
+     * @param size
+     * @param icon The icon, either an URL or a string designating a resource
+     *      which may be a localized string
+     */
+    public void addIcon( Integer size, String icon )
+    {
+        if ( icon != null )
+        {
+            if ( icons == null )
+            {
+                icons = new HashMap();
+            }
+
+            icons.put( size, icon );
+        }
+    }
+
+
+    public Map getAttributeDefinitions()
+    {
+        return attributes;
+    }
+
+
+    public void addAttributeDefinition( AD attribute )
+    {
+        if ( attribute != null )
+        {
+            if ( attributes == null )
+            {
+                attributes = new LinkedHashMap();
+            }
+
+            attributes.put( attribute.getID(), attribute );
+        }
+    }
+}

Added: incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/Activator.java (added)
+++ incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.metatype.internal;
+
+
+import java.io.PrintStream;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+import org.apache.felix.metatype.internal.l10n.BundleResources;
+import org.osgi.framework.*;
+import org.osgi.service.log.LogService;
+import org.osgi.service.metatype.MetaTypeService;
+import org.osgi.util.tracker.ServiceTracker;
+
+
+/**
+ * The <code>Activator</code> class is the <code>BundleActivator</code> of
+ * this bundle and provides abstract logging functionality: If a
+ * <code>LogService</code> is available, that service is used, otherwise
+ * logging goes to standard output or standard error (in case of level ERROR
+ * messages).
+ *
+ * @author fmeschbe
+ */
+public class Activator implements BundleActivator
+{
+
+    /** The name of the log service. */
+    private static final String NAME_LOG_SERVICE = LogService.class.getName();
+
+    /**
+     * A <code>SimpleDateFormat</code> object to format the a log message time
+     * stamp in case of logging to standard output/error (value of format
+     * pattern is "dd.MM.yyyy HH:mm:ss").
+     */
+    private static final SimpleDateFormat FORMAT = new SimpleDateFormat( "dd.MM.yyyy HH:mm:ss" );
+
+    /**
+     * The (singleton) instance of this activator. Used by the log methods to
+     * access the {@link #logService} field.
+     */
+    private static Activator INSTANCE;
+
+    /**
+     * The <code>LogService</code> used to log messages. If a log service is
+     * not available in the framework, this field is <code>null</code>.
+     *
+     * @see #start(BundleContext)
+     * @see #serviceChanged(ServiceEvent)
+     */
+    private ServiceTracker logService;
+
+    /*
+     * Set the static INSTANCE field to this new instance
+     */
+    {
+        INSTANCE = this;
+    }
+
+
+    /**
+     * Starts this bundle doing the following:
+     * <ol>
+     * <li>Register as listener for service events concerning the
+     *      <code>LogService</code>
+     * <li>Try to get the <code>LogService</code>
+     * <li>Registers the <code>MetaTypeService</code> implementation provided
+     *      by this bundle.
+     * </ol>
+     *
+     * @param context The <code>BundleContext</code> of this activator's bundle
+     */
+    public void start( BundleContext context )
+    {
+        // register for log service events
+        logService = new ServiceTracker( context, NAME_LOG_SERVICE, null );
+        logService.open();
+
+        // register the MetaTypeService now, that we are ready
+        Dictionary props = new Hashtable();
+        props.put( Constants.SERVICE_PID, "org.apache.felix.metatype.MetaTypeService" );
+        props.put( Constants.SERVICE_DESCRIPTION, "MetaTypeService Specification 1.1 Implementation" );
+        props.put( Constants.SERVICE_VENDOR, "Apache Software Foundation" );
+        MetaTypeService metaTypeService = new MetaTypeServiceImpl( context );
+        context.registerService( MetaTypeService.class.getName(), metaTypeService, props );
+    }
+
+
+    /**
+     * Stops this bundle by just unregistering as a service listener.
+     * <p>
+     * The framework will take care of ungetting the <code>LogService</code> and
+     * unregistering the <code>MetaTypeService</code> registered by the
+     * {@link #start(BundleContext)} method.
+     *
+     * @param context The <code>BundleContext</code> of this activator's bundle
+     */
+    public void stop( BundleContext context )
+    {
+        logService.close();
+
+        // make sure the static BundleResources cache does not block the class laoder
+        BundleResources.clearResourcesCache();
+    }
+
+    //---------- Logging Support ----------------------------------------------
+    // log to stdout or use LogService
+
+    public static void log( int level, String message )
+    {
+        LogService log = ( LogService ) INSTANCE.logService.getService();
+        if ( log == null )
+        {
+            _log( null, level, message, null );
+        }
+        else
+        {
+            log.log( level, message );
+        }
+    }
+
+
+    public static void log( int level, String message, Throwable exception )
+    {
+        LogService log = ( LogService ) INSTANCE.logService.getService();
+        if ( log == null )
+        {
+            _log( null, level, message, exception );
+        }
+        else
+        {
+            log.log( level, message, exception );
+        }
+    }
+
+
+    public static void log( ServiceReference sr, int level, String message )
+    {
+        LogService log = ( LogService ) INSTANCE.logService.getService();
+        if ( log == null )
+        {
+            _log( sr, level, message, null );
+        }
+        else
+        {
+            log.log( sr, level, message );
+        }
+    }
+
+
+    public static void log( ServiceReference sr, int level, String message, Throwable exception )
+    {
+        LogService log = ( LogService ) INSTANCE.logService.getService();
+        if ( log == null )
+        {
+            _log( sr, level, message, exception );
+        }
+        else
+        {
+            log.log( sr, level, message, exception );
+        }
+    }
+
+
+    //---------- Helper Methods -----------------------------------------------
+
+    private static void _log( ServiceReference sr, int level, String message, Throwable exception )
+    {
+        String time = getTimeStamp();
+
+        StringBuffer buf = new StringBuffer( time );
+        buf.append( ' ' ).append( toLevelString( level ) ).append( ' ' );
+        buf.append( message );
+
+        if ( sr != null )
+        {
+            String name = ( String ) sr.getProperty( Constants.SERVICE_PID );
+            if ( name == null )
+            {
+                name = ( ( String[] ) sr.getProperty( Constants.OBJECTCLASS ) )[0];
+            }
+            buf.append( " (" ).append( name ).append( ", service.id=" ).append( sr.getProperty( Constants.SERVICE_ID ) )
+                .append( ')' );
+        }
+
+        PrintStream dst = ( level == LogService.LOG_ERROR ) ? System.err : System.out;
+        dst.println( buf );
+
+        if ( exception != null )
+        {
+            buf = new StringBuffer( time );
+            buf.append( ' ' ).append( toLevelString( level ) ).append( ' ' );
+            dst.print( buf );
+            exception.printStackTrace( dst );
+        }
+    }
+
+
+    private static String getTimeStamp()
+    {
+        synchronized ( FORMAT )
+        {
+            return FORMAT.format( new Date() );
+        }
+    }
+
+
+    private static String toLevelString( int level )
+    {
+        switch ( level )
+        {
+            case LogService.LOG_DEBUG:
+                return "*DEBUG*";
+            case LogService.LOG_INFO:
+                return "*INFO *";
+            case LogService.LOG_WARNING:
+                return "*WARN *";
+            case LogService.LOG_ERROR:
+                return "*ERROR*";
+            default:
+                return "*" + level + "*";
+        }
+
+    }
+}

Added: incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/LocalizedAttributeDefinition.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/LocalizedAttributeDefinition.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/LocalizedAttributeDefinition.java (added)
+++ incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/LocalizedAttributeDefinition.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.metatype.internal;
+
+
+import org.apache.felix.metatype.AD;
+import org.apache.felix.metatype.internal.l10n.Resources;
+import org.osgi.service.metatype.AttributeDefinition;
+
+
+/**
+ * The <code>LocalizedAttributeDefinition</code> class is the implementation
+ * of the <code>AttributeDefinition</code> interface. This class delegates
+ * calls to the underlying {@link AD} localizing the results of the following
+ * methods: {@link #getName()}, {@link #getDescription()},
+ * {@link #getOptionLabels()}, and {@link #validate(String)}.
+ *
+ * @author fmeschbe
+ */
+class LocalizedAttributeDefinition extends LocalizedBase implements AttributeDefinition
+{
+
+    private final AD ad;
+
+
+    /**
+     * Creates and instance of this localizing facade.
+     *
+     * @param ad The {@link AD} to which calls are delegated.
+     * @param resources The {@link Resources} used to localize return values of
+     * localizable methods.
+     */
+    LocalizedAttributeDefinition( AD ad, Resources resources )
+    {
+        super( resources );
+        this.ad = ad;
+    }
+
+
+    /**
+     * @see org.osgi.service.metatype.AttributeDefinition#getCardinality()
+     */
+    public int getCardinality()
+    {
+        return ad.getCardinality();
+    }
+
+
+    /**
+     * @see org.osgi.service.metatype.AttributeDefinition#getDefaultValue()
+     */
+    public String[] getDefaultValue()
+    {
+        return ad.getDefaultValue();
+    }
+
+
+    /**
+     * @see org.osgi.service.metatype.AttributeDefinition#getDescription()
+     */
+    public String getDescription()
+    {
+        return localize( ad.getDescription() );
+    }
+
+
+    /**
+     * @see org.osgi.service.metatype.AttributeDefinition#getID()
+     */
+    public String getID()
+    {
+        return ad.getID();
+    }
+
+
+    /**
+     * @see org.osgi.service.metatype.AttributeDefinition#getName()
+     */
+    public String getName()
+    {
+        return localize( ad.getName() );
+    }
+
+
+    /**
+     * @see org.osgi.service.metatype.AttributeDefinition#getOptionLabels()
+     */
+    public String[] getOptionLabels()
+    {
+        return localize( ad.getOptionLabels() );
+    }
+
+
+    /**
+     * @see org.osgi.service.metatype.AttributeDefinition#getOptionValues()
+     */
+    public String[] getOptionValues()
+    {
+        return ad.getOptionValues();
+    }
+
+
+    /**
+     * @see org.osgi.service.metatype.AttributeDefinition#getType()
+     */
+    public int getType()
+    {
+        return ad.getType();
+    }
+
+
+    /**
+     * @param value
+     * @see org.osgi.service.metatype.AttributeDefinition#validate(java.lang.String)
+     */
+    public String validate( String value )
+    {
+        String message = ad.validate( value );
+        if ( message == null || message.length() == 0 )
+        {
+            return message;
+        }
+
+        return localize( message );
+    }
+}

Added: incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/LocalizedBase.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/LocalizedBase.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/LocalizedBase.java (added)
+++ incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/LocalizedBase.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,166 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.metatype.internal;
+
+
+import org.apache.felix.metatype.internal.l10n.Resources;
+import org.osgi.service.log.LogService;
+
+
+/**
+ * The <code>LocalizedBase</code> class provides methods to localize strings
+ * and string arrays on demand using a <code>ResourceBundle</code> specified
+ * at construction time.
+ *
+ * @author fmeschbe
+ */
+abstract class LocalizedBase
+{
+
+    /**
+     * The {@link Resources} used to localize strings.
+     * 
+     * @see #localize(String)
+     * @see #localize(String[])
+     */
+    private final Resources resources;
+
+
+    /**
+     * Sets up this class using the given <code>ResourceBundle</code>.
+     * 
+     * @param resources The {@link Resources} used to localize return values of
+     * localizable methods.
+     *      
+     * @throws NullPointerException If <code>resources</code> is
+     *      <code>null</code>.
+     */
+    protected LocalizedBase( Resources resources )
+    {
+        if ( resources == null )
+        {
+            throw new NullPointerException( "resources" );
+        }
+        this.resources = resources;
+    }
+
+
+    /**
+     * Returns the {@link Resources} assigned to this instance.
+     */
+    protected Resources getResources()
+    {
+        return resources;
+    }
+
+
+    /**
+     * Calls {@link #localize(String)} for each string in the array and returns
+     * an array of the resulting localized strings. If <code>strings</code> is
+     * <code>null</code> <code>null</code> is returned.
+     * 
+     * @param strings An array of non-<code>null</code> strings to localize.
+     * 
+     * @return <code>null</code> if <code>strings</code> is <code>null</code> or
+     *      an array of the same size as the <code>strings</code> array
+     *      containing localized strings.
+     */
+    protected String[] localize( String[] strings )
+    {
+        if ( strings == null )
+        {
+            return null;
+        }
+
+        String[] localized = new String[strings.length];
+        for ( int i = 0; i < strings.length; i++ )
+        {
+            localized[i] = localize( strings[i] );
+        }
+        return localized;
+    }
+
+
+    /**
+     * Localizes the string using the
+     * {@link #getResourceBundle() ResourceBundle} set on this instance if
+     * string starts with the percent character (<code>%</code>). If the
+     * string is <code>null</code>, does not start with a percent character
+     * or the resource whose key is the string without the leading the percent
+     * character is not found the string is returned without the leading percent
+     * character.
+     * <p>
+     * Examples of different localizations:
+     * <p>
+     * <table border="0" cellspacing="0" cellpadding="3">
+     *  <tr bgcolor="#ccccff">
+     *   <th><code>string</code></th>
+     *   <th>Key</th>
+     *   <th>Resource</th>
+     *   <th>Result</th>
+     *  </tr>
+     *  <tr>
+     *   <td><code>null</code></td>
+     *   <td>-</td>
+     *   <td>-</td>
+     *  <td><code>null</code></td>
+     *  </tr>
+     *  <tr bgcolor="#eeeeff">
+     *   <td>sample</td>
+     *   <td>-</td>
+     *   <td>-</td>
+     *   <td>sample</td>
+     *  </tr>
+     *  <tr>
+     *   <td><b>%</b>sample</td>
+     *   <td>sample</td>
+     *   <td>-</td>
+     *   <td>sample</td>
+     *  </tr>
+     *  <tr bgcolor="#eeeeff">
+     *   <td><b>%</b>sample</td>
+     *   <td>sample</td>
+     *   <td>resource</td>
+     *   <td>resource</td>
+     *  </tr>
+     * </table>
+     * 
+     * @param string The string to localize
+     * @return The localized string
+     */
+    protected String localize( String string )
+    {
+        if ( string != null && string.startsWith( "%" ) )
+        {
+            string = string.substring( 1 );
+            try
+            {
+                return getResources().getResource( string );
+            }
+            catch ( Exception e )
+            {
+                // ClassCastException, MissingResourceException
+                Activator.log( LogService.LOG_DEBUG, "localize: Failed getting resource '" + string + "'", e );
+            }
+        }
+
+        // just return the string unmodified
+        return string;
+    }
+}

Added: incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/LocalizedObjectClassDefinition.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/LocalizedObjectClassDefinition.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/LocalizedObjectClassDefinition.java (added)
+++ incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/LocalizedObjectClassDefinition.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,251 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.metatype.internal;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.*;
+
+import org.apache.felix.metatype.AD;
+import org.apache.felix.metatype.OCD;
+import org.apache.felix.metatype.internal.l10n.Resources;
+import org.osgi.framework.Bundle;
+import org.osgi.service.metatype.AttributeDefinition;
+import org.osgi.service.metatype.ObjectClassDefinition;
+
+
+/**
+ * The <code>LocalizedObjectClassDefinition</code> class is the implementation
+ * of the <code>ObjectClassDefinition</code> interface. This class delegates
+ * calls to the underlying {@link OCD} localizing the results of the following
+ * methods: {@link #getName()}, {@link #getDescription()}, and
+ * {@link #getIcon(int)}.
+ *
+ * @author fmeschbe
+ */
+public class LocalizedObjectClassDefinition extends LocalizedBase implements ObjectClassDefinition
+{
+
+    private Bundle bundle;
+
+    private OCD ocd;
+
+
+    /**
+     * Creates and instance of this localizing facade.
+     *
+     * @param bundle The <code>Bundle</code> providing this object class
+     *            definition.
+     * @param ocd The {@link OCD} to which calls are delegated.
+     * @param resources The {@link Resources} used to localize return values of
+     * localizable methods.
+     */
+    public LocalizedObjectClassDefinition( Bundle bundle, OCD ocd, Resources resources )
+    {
+        super( resources );
+        this.bundle = bundle;
+        this.ocd = ocd;
+    }
+
+
+    /**
+     * @param filter
+     * @see org.osgi.service.metatype.ObjectClassDefinition#getAttributeDefinitions(int)
+     */
+    public AttributeDefinition[] getAttributeDefinitions( int filter )
+    {
+        if ( ocd.getAttributeDefinitions() == null )
+        {
+            return null;
+        }
+
+        Iterator adhIter = ocd.getAttributeDefinitions().values().iterator();
+        if ( filter == ObjectClassDefinition.OPTIONAL || filter == ObjectClassDefinition.REQUIRED )
+        {
+            boolean required = ( filter == ObjectClassDefinition.REQUIRED );
+            adhIter = new RequiredFilterIterator( adhIter, required );
+        }
+        else if ( filter != ObjectClassDefinition.ALL )
+        {
+            return null;
+        }
+
+        if ( !adhIter.hasNext() )
+        {
+            return null;
+        }
+
+        List result = new ArrayList();
+        while ( adhIter.hasNext() )
+        {
+            result.add( new LocalizedAttributeDefinition( ( AD ) adhIter.next(), getResources() ) );
+        }
+
+        return ( AttributeDefinition[] ) result.toArray( new AttributeDefinition[result.size()] );
+    }
+
+
+    /**
+     * @see org.osgi.service.metatype.ObjectClassDefinition#getDescription()
+     */
+    public String getDescription()
+    {
+        return localize( ocd.getDescription() );
+    }
+
+
+    /**
+     * @param size
+     * @throws IOException
+     * @see org.osgi.service.metatype.ObjectClassDefinition#getIcon(int)
+     */
+    public InputStream getIcon( int size ) throws IOException
+    {
+        // nothing if no icons are defined
+        Map icons = ocd.getIcons();
+        if ( icons == null )
+        {
+            return null;
+        }
+
+        // get exact size
+        String iconPath = ( String ) icons.get( new Integer( size ) );
+        if ( iconPath == null )
+        {
+            // approximate size: largest icon smaller than requested
+            Integer selected = new Integer( Integer.MIN_VALUE );
+            for ( Iterator ei = icons.keySet().iterator(); ei.hasNext(); )
+            {
+                Map.Entry entry = ( Map.Entry ) ei.next();
+                Integer keySize = ( Integer ) entry.getKey();
+                if ( keySize.intValue() <= size && selected.compareTo( keySize ) < 0 )
+                {
+                    selected = keySize;
+                }
+            }
+            // get the raw path, fail if no path can be found
+            iconPath = ( String ) icons.get( selected );
+        }
+
+        // fail if no icon could be found
+        if ( iconPath == null )
+        {
+            return null;
+        }
+
+        // localize the path
+        iconPath = localize( iconPath );
+
+        // try to resolve the path in the bundle
+        URL url = bundle.getEntry( iconPath );
+        if ( url == null )
+        {
+            return null;
+        }
+
+        // open the stream on the URL - this may throw an IOException
+        return url.openStream();
+    }
+
+
+    /**
+     * @see org.osgi.service.metatype.ObjectClassDefinition#getID()
+     */
+    public String getID()
+    {
+        return ocd.getID();
+    }
+
+
+    /**
+     * @see org.osgi.service.metatype.ObjectClassDefinition#getName()
+     */
+    public String getName()
+    {
+        return localize( ocd.getName() );
+    }
+
+    private static class RequiredFilterIterator implements Iterator
+    {
+
+        private final Iterator base;
+
+        private final boolean required;
+
+        private AD next;
+
+
+        private RequiredFilterIterator( Iterator base, boolean required )
+        {
+            this.base = base;
+            this.required = required;
+            this.next = seek();
+        }
+
+
+        public boolean hasNext()
+        {
+            return next != null;
+        }
+
+
+        public Object next()
+        {
+            if ( !hasNext() )
+            {
+                throw new NoSuchElementException();
+            }
+
+            AD toReturn = next;
+            next = seek();
+            return toReturn;
+        }
+
+
+        public void remove()
+        {
+            throw new UnsupportedOperationException( "remove" );
+        }
+
+
+        private AD seek()
+        {
+            if ( base.hasNext() )
+            {
+                AD next;
+                do
+                {
+                    next = ( AD ) base.next();
+                }
+                while ( next.isRequired() != required && base.hasNext() );
+
+                if ( next.isRequired() == required )
+                {
+                    return next;
+                }
+            }
+
+            // nothing found any more
+            return null;
+        }
+
+    }
+}

Added: incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/MetaTypeInformationImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/MetaTypeInformationImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/MetaTypeInformationImpl.java (added)
+++ incubator/ace/trunk/server/src/org/apache/felix/metatype/internal/MetaTypeInformationImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.metatype.internal;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.felix.metatype.DefaultMetaTypeProvider;
+import org.apache.felix.metatype.Designate;
+import org.apache.felix.metatype.DesignateObject;
+import org.apache.felix.metatype.MetaData;
+import org.apache.felix.metatype.OCD;
+import org.osgi.framework.Bundle;
+import org.osgi.service.metatype.MetaTypeInformation;
+import org.osgi.service.metatype.MetaTypeProvider;
+import org.osgi.service.metatype.ObjectClassDefinition;
+
+/**
+ * The <code>MetaTypeInformationImpl</code> class implements the
+ * <code>MetaTypeInformation</code> interface returned from the
+ * <code>MetaTypeService</code>.
+ *
+ * @author fmeschbe
+ */
+public class MetaTypeInformationImpl implements MetaTypeInformation {
+
+    // also defined in org.osgi.service.cm.ConfigurationAdmin, but copied
+    // here to not create a synthetic dependency
+    public static final String SERVICE_FACTORYPID = "service.factoryPid";
+
+    private final Bundle bundle;
+
+    private Set pids;
+
+    private Set factoryPids;
+
+    private Set locales;
+
+    private Map metaTypeProviders;
+
+    protected MetaTypeInformationImpl(Bundle bundle) {
+        this.bundle = bundle;
+        this.pids = new TreeSet();
+        this.factoryPids = new TreeSet();
+        this.metaTypeProviders = new HashMap();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.osgi.service.metatype.MetaTypeInformation#getBundle()
+     */
+    public Bundle getBundle() {
+        return this.bundle;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.osgi.service.metatype.MetaTypeInformation#getFactoryPids()
+     */
+    public String[] getFactoryPids() {
+        return (String[]) this.factoryPids.toArray(new String[this.factoryPids.size()]);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.osgi.service.metatype.MetaTypeInformation#getPids()
+     */
+    public String[] getPids() {
+        return (String[]) this.pids.toArray(new String[this.pids.size()]);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.osgi.service.metatype.MetaTypeProvider#getLocales()
+     */
+    public String[] getLocales() {
+        if (this.locales == null) {
+            synchronized (this) {
+                Set newLocales = new TreeSet();
+                for (Iterator mi = this.metaTypeProviders.values().iterator(); mi.hasNext();) {
+                    MetaTypeProvider mtp = (MetaTypeProvider) mi.next();
+                    this.addValues(newLocales, mtp.getLocales());
+                }
+                this.locales = newLocales;
+            }
+        }
+
+        return (String[]) this.locales.toArray(new String[this.locales.size()]);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.osgi.service.metatype.MetaTypeProvider#getObjectClassDefinition(java.lang.String,
+     *      java.lang.String)
+     */
+    public ObjectClassDefinition getObjectClassDefinition(String id,
+            String locale) {
+        MetaTypeProvider mtp = (MetaTypeProvider) this.metaTypeProviders.get(id);
+        return (mtp != null) ? mtp.getObjectClassDefinition(id, locale) : null;
+    }
+
+    // ---------- internal support for metadata -------------------------------
+
+    Designate getDesignate( String pid )
+    {
+        Object mto = this.metaTypeProviders.get( pid );
+        if ( mto instanceof DefaultMetaTypeProvider )
+        {
+            return ( ( DefaultMetaTypeProvider ) mto ).getDesignate( pid );
+        }
+
+        return null;
+    }
+
+    // ---------- setters to fill the values -----------------------------------
+
+    protected void addMetaData(MetaData md) {
+        if (md.getDesignates() != null) {
+            // meta type provide to register by PID
+            DefaultMetaTypeProvider dmtp = new DefaultMetaTypeProvider(this.bundle, md);
+
+            Iterator designates = md.getDesignates().values().iterator();
+            while (designates.hasNext()) {
+                Designate designate = (Designate) designates.next();
+
+                // get the OCD reference, ignore the designate if none
+                DesignateObject object = designate.getObject();
+                String ocdRef = (object == null) ? null : object.getOcdRef();
+                if (ocdRef == null) {
+                    continue;
+                }
+
+                // get ocd for the reference, ignore designate if none
+                OCD ocd = (OCD) md.getObjectClassDefinitions().get(ocdRef);
+                if (ocd == null) {
+                    continue;
+                }
+
+                // gather pids and factory pids
+                this.pids.add(designate.getPid());
+                if (designate.getFactoryPid() != null) {
+                    this.factoryPids.add( designate.getFactoryPid() );
+                }
+
+                // register a metatype provider for the pid
+                this.addMetaTypeProvider(designate.getPid(), dmtp);
+            }
+        }
+    }
+
+    protected void addPids(String[] pids) {
+        this.addValues(this.pids, pids);
+    }
+
+    protected void removePid(String pid) {
+        this.pids.remove(pid);
+    }
+
+    protected void addFactoryPids(String[] factoryPids) {
+        this.addValues(this.factoryPids, factoryPids);
+    }
+
+    protected void removeFactoryPid(String factoryPid) {
+        this.factoryPids.remove(factoryPid);
+    }
+
+    protected void addMetaTypeProvider(String key, MetaTypeProvider mtp) {
+        if (key != null && mtp != null) {
+            this.metaTypeProviders.put(key, mtp);
+            this.locales = null;
+        }
+    }
+
+    protected MetaTypeProvider removeMetaTypeProvider(String key) {
+        if (key != null) {
+            this.locales = null;
+            return (MetaTypeProvider) this.metaTypeProviders.remove(key);
+        }
+
+        return null;
+    }
+
+    private void addValues(Collection dest, Object[] values) {
+        if (values != null && values.length > 0) {
+            dest.addAll(Arrays.asList(values));
+        }
+    }
+}