Package org.apache.felix.ipojo.manipulation.annotations

Source Code of org.apache.felix.ipojo.manipulation.annotations.MetadataCollector$ProvidesVisitor$InterfaceArrayVisitor

/*
* 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.ipojo.manipulation.annotations;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.felix.ipojo.metadata.Attribute;
import org.apache.felix.ipojo.metadata.Element;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.EmptyVisitor;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;

/**
* Collect metadata from classes by parsing annotation.
* This class collects type (i.e.) annotations and create method & field collectors.
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class MetadataCollector extends EmptyVisitor implements Opcodes {

    /**
     * Class name.
     */
    private String m_className;

    /**
     * Root element of computed metadata.
     */
    private Element m_elem = null;

    /**
     * True if the visited class is a component type declaration (i.e. contains the @component annotation).
     */
    private boolean m_containsComponentAnnotation = false;

    /**
     * True if the visited class does not have the @Component annotation.
     */
    private boolean m_ignoredBecauseOfMissingComponent = false;

    /**
     * Map of [element ids, element].
     * This map is used to easily get an already created element.
     */
    private Map m_ids = new HashMap();

    /**
     * Map of [element, referto].
     * This map is used to recreate the element hierarchy.
     * Stored element are added under referred element.
     */
    private Map m_elements = new HashMap();

    /**
     * Instance declaration.
     */
    private Element m_instance;

    /**
     * XML document parser.
     */
    private DocumentBuilder m_builder;


    public Element getComponentTypeDeclaration() {
        return m_elem;
    }

    public Element getInstanceDeclaration() {
        return m_instance;
    }

    public boolean isComponentType() {
        return m_containsComponentAnnotation;
    }

    public boolean isIgnoredBecauseOfMissingComponent() {
        return m_ignoredBecauseOfMissingComponent;
    }

    public String getClassName() {
        return m_className;
    }

    /**
     * Start visiting a class.
     * Initialize the getter/setter generator, add the _cm field, add the pojo interface.
     * @param version : class version
     * @param access : class access
     * @param name : class name
     * @param signature : class signature
     * @param superName : class super class
     * @param interfaces : implemented interfaces
     * @see org.objectweb.asm.ClassAdapter#visit(int, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
     */
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        m_ids = new HashMap();
        m_elements = new HashMap();
        m_className = name;
    }


    /**
     * Visit class annotations.
     * This method detects @component and @provides annotations.
     * @param desc : annotation descriptor.
     * @param visible : is the annotation visible at runtime.
     * @return the annotation visitor.
     * @see org.objectweb.asm.ClassAdapter#visitAnnotation(java.lang.String, boolean)
     */
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        // @Component
        if (desc.equals("Lorg/apache/felix/ipojo/annotations/Component;")) {
            // It is a component
            m_elem =  new Element("component", "");
            m_containsComponentAnnotation = true;
            m_elem.addAttribute(new Attribute("className", m_className.replace('/', '.')));
            return new ComponentVisitor();
        }

        // @Handler
        if (desc.equals("Lorg/apache/felix/ipojo/annotations/Handler;")) {
            // It is a handler, change the root element
            m_elem = new Element("handler", "");
            m_containsComponentAnnotation = true;
            m_elem.addAttribute(new Attribute("classname", m_className.replace('/', '.')));
            return new HandlerVisitor();
        }

        // @Provides
        if (desc.equals("Lorg/apache/felix/ipojo/annotations/Provides;")) {
            return new ProvidesVisitor();
        }

        // @HandlerDeclaration
        if (desc.equals("Lorg/apache/felix/ipojo/annotations/HandlerDeclaration;")) {
            return new HandlerDeclarationVisitor();
        }

        // @Instantiate
        if (desc.equals("Lorg/apache/felix/ipojo/annotations/Instantiate;")) {
            return new InstantiateVisitor();
        }

        if (CustomAnnotationVisitor.isCustomAnnotation(desc)) {
            Element elem = CustomAnnotationVisitor.buildElement(desc);
            return new CustomAnnotationVisitor(elem, this, true, true);
        }

        return null;
    }


    /**
     * Visit a field.
     * Call the field collector visitor.
     * @param access : field access.
     * @param name : field name
     * @param desc : field descriptor
     * @param signature : field signature
     * @param value : field value (static field only)
     * @return the field visitor.
     * @see org.objectweb.asm.ClassAdapter#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)
     */
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
        return new FieldCollector(name, this);
    }

    /**
     * Visit a method.
     * Call the method collector visitor.
     * @param access : method access
     * @param name : method name
     * @param desc : method descriptor
     * @param signature : method signature
     * @param exceptions : method exceptions
     * @return the Method Visitor.
     * @see org.objectweb.asm.ClassAdapter#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
     */
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        return new MethodCollector(name, desc, this);
    }

    /**
     * End of the visit : compute final elements.
     * @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()
     */
    public void visitEnd() {
        // If m_elem (Component) is null, print a warning and ignore.
        if (m_elem == null  &&  ! m_elements.isEmpty()) {
            m_ignoredBecauseOfMissingComponent = true;
            return;
        }

        if (! m_containsComponentAnnotation) {
          m_ignoredBecauseOfMissingComponent = true;
            return;
        }

        // Recompute the tree
        Set elems = getElements().keySet();
        Iterator it = elems.iterator();
        while (it.hasNext()) {
            Element current = (Element) it.next();
            String reference = (String) getElements().get(current);
            if (reference == null) {
                m_elem.addElement(current);
            } else {
                Element ref = (Element) getIds().get(reference);
                if (ref == null) {
                    // Add to the root
                    m_elem.addElement(current);
                } else {
                    ref.addElement(current);
                }
            }
        }
    }

    protected Map getIds() {
        return m_ids;
    }

    protected Map getElements() {
        return m_elements;
    }

    /**
     * Creates a 'fresh' document builder.
     * @return a new document builder is not already created, else reset
     * the created one, and return it.
     */
    protected DocumentBuilder getFreshDocumentBuilder() {
        if (m_builder == null) {

            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true);
            try {
                m_builder = factory.newDocumentBuilder();
            } catch (ParserConfigurationException e) {
                // TODO GSA is this acceptable to throw a RuntimeException here ?
                e.printStackTrace();
            }

            return m_builder;
        }

        // The builder has to be reseted
        m_builder.reset();

        return m_builder;
    }

    /**
     * Parse the @provides annotation.
     */
    private class ProvidesVisitor extends EmptyVisitor implements AnnotationVisitor {
        /**
         * Provides element.
         */
        Element m_prov = new Element("provides", "");

        /**
         * Visit @provides annotation attributes.
         * @param arg0 : annotation attribute name
         * @param arg1 : annotation attribute value
         * @see org.objectweb.asm.commons.EmptyVisitor#visit(java.lang.String, java.lang.Object)
         */
        public void visit(String arg0, Object arg1) {
            if (arg0.equals("factory")) { // Should be deprecated
                m_prov.addAttribute(new Attribute("factory", arg1.toString()));
            }
            if (arg0.equals("strategy")) {
                m_prov.addAttribute(new Attribute("strategy", arg1.toString()));
            }
        }

        /**
         * Visit specifications array.
         * @param arg0 : attribute name
         * @return a visitor visiting each element of the array.
         * @see org.objectweb.asm.commons.EmptyVisitor#visitArray(java.lang.String)
         */
        public AnnotationVisitor visitArray(String arg0) {
            if (arg0.equals("specifications")) {
                return new InterfaceArrayVisitor();
            } else if (arg0.equals("properties")) {
              // Create a new simple visitor to visit the nested ServiceProperty annotations
              // Collected properties are collected in m_prov
              return new EmptyVisitor() {
                public AnnotationVisitor visitAnnotation(String ignored, String desc) {
                      return new PropertyAnnotationParser(m_prov);
              }
              };
            } else {
                return null;
            }
        }

        /**
         * End of the visit.
         * Append to the parent element the computed "provides" element.
         * @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()
         */
        public void visitEnd() {
            getIds().put("provides", m_prov);
            getElements().put(m_prov, null);
        }

        private class InterfaceArrayVisitor extends EmptyVisitor {
            /**
             * List of parsed interface.
             */
            private String m_itfs;

            /**
             * Visit one element of the array.
             * @param arg0 : null
             * @param arg1 : element value.
             * @see org.objectweb.asm.commons.EmptyVisitor#visit(java.lang.String, java.lang.Object)
             */
            public void visit(String arg0, Object arg1) {
                if (m_itfs == null) {
                    m_itfs = "{" + ((Type) arg1).getClassName();
                } else {
                    m_itfs += "," + ((Type) arg1).getClassName();
                }
            }

            /**
             * End of the array visit.
             * Add the attribute to 'provides' element.
             * @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()
             */
            public void visitEnd() {
                m_prov.addAttribute(new Attribute("specifications", m_itfs + "}"));
            }

        }


    }

    /**
     * Parse the @Instantitate annotation.
     */
    private class InstantiateVisitor extends EmptyVisitor implements AnnotationVisitor {
        /**
         * Instance name.
         */
        private String m_name;

        /**
         * Visit an annotation attribute.
         * @param arg0 the attribute name
         * @param arg1 the attribute value
         * @see org.objectweb.asm.commons.EmptyVisitor#visit(java.lang.String, java.lang.Object)
         */
        public void visit(String arg0, Object arg1) {
            if (arg0.equals("name")) {
                m_name = arg1.toString();
                return;
            }
        }

        /**
         * End of the visit. Creates the instance element.
         * @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()
         */
        public void visitEnd() {
            m_instance = new Element("instance", "");
            if (m_className != null) { // Should not be null.
                m_instance.addAttribute(new Attribute("component", m_className.replace('/', '.')));
            }
            if (m_name != null) {
                m_instance.addAttribute(new Attribute("name", m_name));
            }
        }
    }


    /**
     * Parse the @component annotation.
     */
    private class ComponentVisitor extends EmptyVisitor implements AnnotationVisitor {

        /**
         * Factory attribute.
         */
        private String m_factory;

        /**
         * Is the component an immediate component?
         */
        private String m_immediate;

        /**
         * Component name (cannot be null).
         */
        private String m_name;

        /**
         * Does the component exposes its architecture?
         */
        private String m_architecture;

        /**
         * Does the component propagate configuration to provided services?
         */
        private String m_propagation;

        /**
         * Managed Service PID.
         */
        private String m_managedservice;

        /**
         * Factory-Method.
         */
        private String m_method;

        /**
         * Version.
         */
        private String m_version;

        /**
         * Element properties.
         */
        private Element m_props;

        /**
         * Visit @component annotation attribute.
         * @param arg0 : attribute name
         * @param arg1 : attribute value
         * @see org.objectweb.asm.commons.EmptyVisitor#visit(java.lang.String, java.lang.Object)
         */
        public void visit(String arg0, Object arg1) {
            if (arg0.equals("public_factory"|| arg0.equals("publicFactory")) {
              // public_factory is deprecated, but must sill be supported
                m_factory = arg1.toString();
                return;
            }
            if (arg0.equals("name")) {
                m_name = arg1.toString();
                return;
            }
            if (arg0.equals("immediate")) {
                m_immediate = arg1.toString();
                return;
            }
            if (arg0.equals("architecture")) {
                m_architecture = arg1.toString();
                return;
            }
            if (arg0.equals("propagation")) {
                m_propagation = arg1.toString();
                return;
            }
            if (arg0.equals("managedservice")) {
                m_managedservice = arg1.toString();
                return;
            }
            if (arg0.equals("factory_method"|| arg0.equals("factoryMethod")) {
              // factory_method is deprecated, but must still be supported.
                m_method = arg1.toString();
                return;
            }
            if (arg0.equals("version")) {
                m_version = arg1.toString();
                return;
            }
        }

        /**
         * End of the visit.
         * Append to the "component" element computed attribute.
         * @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()
         */
        public void visitEnd() {
            if (m_name == null) {
                m_name = m_className.replace('/', '.');
            }
            m_elem.addAttribute(new Attribute("name", m_name));
            if (m_factory != null && m_factory.equalsIgnoreCase("false")) {
                m_elem.addAttribute(new Attribute("public", "false"));
            } else {
                m_elem.addAttribute(new Attribute("public", "true"));
            }
            if (m_architecture != null) {
                m_elem.addAttribute(new Attribute("architecture", m_architecture));
            }
            if (m_immediate != null) {
                m_elem.addAttribute(new Attribute("immediate", m_immediate));
            }
            if (m_method != null) {
                m_elem.addAttribute(new Attribute("factory-method", m_method));
            }
            if (m_version != null) {
                m_elem.addAttribute(new Attribute("version", m_version));
            }
            if (m_propagation != null) {
                if (m_props == null) {
                    m_props = new Element("properties", "");
                    getElements().put(m_props, null);
                    getIds().put("properties", m_props);
                }
                m_props.addAttribute(new Attribute("propagation", m_propagation));
            }
            if (m_managedservice != null) {
                if (m_props == null) {
                    m_props = new Element("properties", "");
                    getElements().put(m_props, null);
                    getIds().put("properties", m_props);
                }
                m_props.addAttribute(new Attribute("pid", m_managedservice));
            }
        }
    }

    /**
     * Parses the @Handler annotation.
     */
    private class HandlerVisitor extends EmptyVisitor implements AnnotationVisitor {

        /**
         * Visit @handler annotation attributes.
         * @param arg0 : annotation attribute name
         * @param arg1 : annotation attribute value
         * @see org.objectweb.asm.commons.EmptyVisitor#visit(java.lang.String, java.lang.Object)
         */
        public void visit(String arg0, Object arg1) {
            if (arg0.equals("name")) {
                m_elem.addAttribute(new Attribute("name", arg1.toString()));
                return;
            }
            if (arg0.equals("namespace")) {
                m_elem.addAttribute(new Attribute("namespace", arg1.toString()));
                return;
            }
            if (arg0.equals("level")) {
                m_elem.addAttribute(new Attribute("level", arg1.toString()));
                return;
            }
            if (arg0.equals("architecture")) {
                m_elem.addAttribute(new Attribute("architecture", arg1.toString()));
                return;
            }
        }
    }

    /**
     * Parse the @HandlerDeclaration annotation.
     */
    private class HandlerDeclarationVisitor extends EmptyVisitor implements AnnotationVisitor {

        /**
         * XML accepted by the handler.
         */
        private String m_value;

        /**
         * Parses the value attribute.
         * @param arg0 'value'
         * @param arg1 the value
         * @see org.objectweb.asm.commons.EmptyVisitor#visit(java.lang.String, java.lang.Object)
         */
        public void visit(String arg0, Object arg1) {
            // there is only a 'value' attribute
            this.m_value = (String) arg1;
        }

        /**
         * End of the visit.
         * Builds the XML document.
         * @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()
         */
        public void visitEnd() {
            // The value is an XML document
            DocumentBuilder builder = getFreshDocumentBuilder();
            InputStream is = new ByteArrayInputStream(m_value.getBytes());
            Document document = null;
            try {
                document = builder.parse(is);
                convertDOMElements(m_elem, document.getDocumentElement());
            } catch (Exception e) {
                // TODO GSA change this to a logger ?
                System.err.println("[warning] Cannot convert " + m_value + " to iPOJO Elements.");
            } finally {
                try {
                    is.close();
                } catch (IOException e) {
                    System.err.println("[warning] Cannot close correctly the value input stream");
                }
            }
        }

        /**
         * Converts recursively the given XML Element into an iPOJO Element.
         * @param root iPOJO root Element
         * @param xmlElement DOM Element to be converted
         */
        private void convertDOMElements(final Element root,
                                        final org.w3c.dom.Element xmlElement) {

            // Create an equivalent iPOJO element
            Element converted = new Element(xmlElement.getLocalName(), xmlElement.getNamespaceURI());

            // Convert attributes if any
            if (xmlElement.hasAttributes()) {
                NamedNodeMap attributes = xmlElement.getAttributes();
                for (int i = 0; i < attributes.getLength(); i++) {
                    Attr attr = (Attr) attributes.item(i);
                    converted.addAttribute(new Attribute(attr.getName(),
                                                         attr.getNamespaceURI(),
                                                         attr.getValue()));
                }
            }

            // Convert child elements if any
            if (xmlElement.hasChildNodes()) {
                NodeList childs = xmlElement.getChildNodes();
                for (int i = 0; i < childs.getLength(); i++) {

                    // Recursive call
                    convertDOMElements(converted, (org.w3c.dom.Element) childs.item(i));
                }
            }

            // Add converted element as a root's child
            root.addElement(converted);
        }
    }

}
TOP

Related Classes of org.apache.felix.ipojo.manipulation.annotations.MetadataCollector$ProvidesVisitor$InterfaceArrayVisitor

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