001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020package org.apache.felix.ipojo;
021
022import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
023import org.apache.felix.ipojo.metadata.Attribute;
024import org.apache.felix.ipojo.metadata.Element;
025import org.apache.felix.ipojo.parser.PojoMetadata;
026import org.osgi.framework.Bundle;
027
028import java.util.Dictionary;
029import java.util.HashSet;
030import java.util.Set;
031
032/**
033 * This class defines the description of primitive (non-composite) component
034 * types. An instance of this class will be returned when invoking the
035 * {@link org.apache.felix.ipojo.ComponentFactory#getComponentDescription()} method.
036 *
037 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
038 */
039final class PrimitiveTypeDescription extends ComponentTypeDescription {
040
041    /**
042     * Set to keep component's all super-class class-names.
043     */
044    private Set<String> m_superClasses = new HashSet<String>();
045
046    /**
047     * Set to keep component's all interface class-names.
048     */
049    private Set<String> m_interfaces = new HashSet<String>();
050
051    /**
052     * The described component factory.
053     */
054    private ComponentFactory m_factory;
055
056    /**
057     * Creates a PrimitiveTypeDescription object.
058     *
059     * @param factory the m_factory attached to this component type description.
060     */
061    public PrimitiveTypeDescription(ComponentFactory factory) {
062        super(factory);
063        this.m_factory = factory;
064
065        try {
066            // The inspection can be done only for primitive components
067            if (factory.getClassName() != null) {
068                // Read inherited classes and interfaces into given Sets.
069                new InheritanceInspector(factory.getPojoMetadata(), getBundleContext().getBundle()).
070                        computeInterfacesAndSuperClasses(m_interfaces, m_superClasses);
071            }
072        } catch (ClassNotFoundException e) {
073            m_interfaces.clear();
074            m_superClasses.clear();
075        }
076
077    }
078
079    /**
080     * Computes the properties to publish.
081     * The <code>component.class</code> property contains the implementation class name.
082     *
083     * @return the dictionary of properties to publish
084     * @see org.apache.felix.ipojo.architecture.ComponentTypeDescription#getPropertiesToPublish()
085     */
086    public Dictionary<String, Object> getPropertiesToPublish() {
087        Dictionary<String, Object> dict = super.getPropertiesToPublish();
088        if (m_factory.getClassName() != null) {
089            dict.put("component.class", m_factory.getClassName());
090        }
091        return dict;
092    }
093
094    /**
095     * Adds the "implementation-class" attribute to the type description.
096     *
097     * @return the component type description.
098     * @see org.apache.felix.ipojo.architecture.ComponentTypeDescription#getDescription()
099     */
100    public Element getDescription() {
101        Element elem = super.getDescription();
102        elem.addAttribute(new Attribute("Implementation-Class", m_factory.getClassName()));
103
104        /* Adding interfaces and super-classes of component into description */
105        Element inheritance = new Element("Inherited", "");
106
107        inheritance.addAttribute(new Attribute("Interfaces", m_interfaces.toString()));
108        inheritance.addAttribute(new Attribute("SuperClasses", m_superClasses.toString()));
109
110        elem.addElement(inheritance);
111
112        return elem;
113    }
114
115    /**
116     * This class is used to collect interfaces and super-classes of given component in specified Sets.
117     *
118     * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
119     */
120    private final class InheritanceInspector {
121        /*
122            * PojoMetadata of target Component.
123            */
124        private PojoMetadata m_pojoMetadata;
125        /*
126            * Bundle exposing target component.
127            */
128        private Bundle m_bundle;
129
130
131        /**
132         * Creates a TypeCollector object
133         *
134         * @param pojoMetadata PojoMetadata describing Component.
135         * @param bundle       Bundle which has been exposed the intended Component.
136         */
137        public InheritanceInspector(PojoMetadata pojoMetadata, Bundle bundle) {
138            m_pojoMetadata = pojoMetadata;
139            m_bundle = bundle;
140        }
141
142        /**
143         * Collect interfaces implemented by the POJO into given Sets.
144         *
145         * @param interfaces : the set of implemented interfaces
146         * @param classes    : the set of extended classes
147         * @throws ClassNotFoundException : occurs when an interface cannot be loaded.
148         */
149        public void computeInterfacesAndSuperClasses(Set<String> interfaces, Set<String> classes) throws ClassNotFoundException {
150            String[] immediateInterfaces = m_pojoMetadata.getInterfaces();
151            String parentClass = m_pojoMetadata.getSuperClass();
152
153            // First iterate on found specification in manipulation metadata
154            for (String immediateInterface : immediateInterfaces) {
155                interfaces.add(immediateInterface);
156                // Iterate on interfaces implemented by the current interface
157                Class<?> clazz = m_bundle.loadClass(immediateInterface);
158                collectInterfaces(clazz, interfaces, m_bundle);
159            }
160
161            // Look for parent class.
162            if (parentClass != null) {
163                Class clazz = m_bundle.loadClass(parentClass);
164                collectInterfacesFromClass(clazz, interfaces, m_bundle);
165                classes.add(parentClass);
166                collectParentClassesFromClass(clazz, classes, m_bundle);
167            }
168
169            // Removing Object Class from the inherited classes list.
170            classes.remove(Object.class.getName());
171        }
172
173        /**
174         * Look for inherited interfaces.
175         *
176         * @param clazz  : interface name to explore (class object)
177         * @param acc    : set (accumulator)
178         * @param bundle : bundle
179         * @throws ClassNotFoundException : occurs when an interface cannot be loaded.
180         */
181        private void collectInterfaces(Class<?> clazz, Set<String> acc, Bundle bundle) throws ClassNotFoundException {
182            Class[] clazzes = clazz.getInterfaces();
183            for (Class clazze : clazzes) {
184                acc.add(clazze.getName());
185                collectInterfaces(clazze, acc, bundle);
186            }
187        }
188
189        /**
190         * Collect interfaces for the given class.
191         * This method explores super class to.
192         *
193         * @param clazz  : class object.
194         * @param acc    : set of implemented interface (accumulator)
195         * @param bundle : bundle.
196         * @throws ClassNotFoundException : occurs if an interface cannot be load.
197         */
198        private void collectInterfacesFromClass(Class<?> clazz, Set<String> acc,
199                                                Bundle bundle) throws ClassNotFoundException {
200            Class[] clazzes = clazz.getInterfaces();
201            for (Class clazze : clazzes) {
202                acc.add(clazze.getName());
203                collectInterfaces(clazze, acc, bundle);
204            }
205            // Iterate on parent classes
206            Class sup = clazz.getSuperclass();
207            if (sup != null) {
208                collectInterfacesFromClass(sup, acc, bundle);
209            }
210        }
211
212        /**
213         * Collect parent classes for the given class.
214         *
215         * @param clazz  : class object.
216         * @param acc    : set of extended classes (accumulator)
217         * @param bundle : bundle.
218         * @throws ClassNotFoundException : occurs if an interface cannot be load.
219         */
220        private void collectParentClassesFromClass(Class<?> clazz, Set<String> acc, Bundle bundle) throws ClassNotFoundException {
221            Class<?> parent = clazz.getSuperclass();
222            if (parent != null) {
223                acc.add(parent.getName());
224                collectParentClassesFromClass(parent, acc, bundle);
225            }
226        }
227    }
228}