Package com.carrotgarden.osgi.anno.scr.util

Source Code of com.carrotgarden.osgi.anno.scr.util.UtilAsm

/**
* Copyright (C) 2010-2013 Andrei Pozolotin <Andrei.Pozolotin@gmail.com>
*
* All rights reserved. Licensed under the OSI BSD License.
*
* http://www.opensource.org/licenses/bsd-license.php
*/
package com.carrotgarden.osgi.anno.scr.util;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Property;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;

/**
* DS utilities which rely on ASM reflection.
*/
public class UtilAsm {

  public static final String DESC_CONFIGURATION_POLICY = Type
      .getDescriptor(ConfigurationPolicy.class);

  public static final String DESC_COMPONENT = Type
      .getDescriptor(Component.class);

  public static final String DESC_ACTIVATE = Type
      .getDescriptor(Activate.class);

  public static final String DESC_DEACTIVATE = Type
      .getDescriptor(Deactivate.class);

  public static final String DESC_MODIFIED = Type
      .getDescriptor(Modified.class);

  public static final String DESC_PROPERTY = Type
      .getDescriptor(Property.class);

  public static final String DESC_REFERENCE = Type
      .getDescriptor(Reference.class);

  public static final String DESC_REFERENCE_CARDINALITY = Type
      .getDescriptor(ReferenceCardinality.class);

  public static final String DESC_REFERENCE_POLICY = Type
      .getDescriptor(ReferencePolicy.class);

  public static final int SKIP_MODE = ClassReader.SKIP_CODE
      | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;

  /**
   * Extract annotation value as {@link Boolean}.
   */
  public static Boolean asBoolean(final AnnotationNode node, final String name) {

    @SuppressWarnings("unchecked")
    final List<Object> entryList = node.values;

    if (Util.isListNone(entryList)) {
      return null;
    }

    for (int k = 0; k < entryList.size(); k += 2) {
      final String entryName = (String) entryList.get(k);
      if (entryName.equals(name)) {
        final Boolean entryValue = (Boolean) entryList.get(k + 1);
        return entryValue;
      }
    }

    return null;

  }

  /**
   * Extract annotation value as class list.
   */
  public static List<Class<?>> asClassList(final AnnotationNode node,
      final String name) throws Exception {

    @SuppressWarnings("unchecked")
    final List<Object> entryList = node.values;

    if (Util.isListNone(entryList)) {
      return null;
    }

    for (int k = 0; k < entryList.size(); k += 2) {
      final String entryName = (String) entryList.get(k);
      if (entryName.equals(name)) {

        final ClassLoader loader = UtilAsm.class.getClassLoader();
        final List<Class<?>> klazList = new ArrayList<Class<?>>();

        @SuppressWarnings("unchecked")
        final List<Type> entryValue = (List<Type>) entryList.get(k + 1);

        for (final Type type : entryValue) {
          final Class<?> klaz = loader.loadClass(type.getClassName());
          klazList.add(klaz);
        }

        return klazList;
      }
    }

    return null;

  }

  /**
   * Extract annotation value as type safe enum.
   */
  public static <E extends Enum<E>> E asEnum(final AnnotationNode node,
      final String name) throws Exception {

    @SuppressWarnings("unchecked")
    final List<Object> entryList = node.values;

    if (Util.isListNone(entryList)) {
      return null;
    }

    for (int k = 0; k < entryList.size(); k += 2) {

      final String entryName = (String) entryList.get(k);

      if (entryName.equals(name)) {

        final String[] entryValue = (String[]) entryList.get(k + 1);
        final String desc = entryValue[0];
        final String value = entryValue[1];

        final String klazName = Type.getType(desc).getClassName();

        @SuppressWarnings("unchecked")
        final Class<E> klaz = (Class<E>) UtilAsm.class.getClassLoader()
            .loadClass(klazName);

        for (final E entry : klaz.getEnumConstants()) {
          if (entry.name().equals(value)) {
            return entry;
          }
        }

        throw new IllegalStateException("invalid enum klaz=" + klaz);

      }
    }

    return null;

  }

  /**
   * Extract annotation value as a string.
   */
  public static String asString(final AnnotationNode node, final String name) {

    @SuppressWarnings("unchecked")
    final List<Object> entryList = node.values;

    if (Util.isListNone(entryList)) {
      return null;
    }

    for (int k = 0; k < entryList.size(); k += 2) {
      final String entryName = (String) entryList.get(k);
      if (entryName.equals(name)) {
        final String entryValue = (String) entryList.get(k + 1);
        return entryValue;
      }
    }

    return null;

  }

  /**
   * Extract annotation value as string list.
   */
  public static List<String> asStringList(final AnnotationNode node,
      final String name) {

    @SuppressWarnings("unchecked")
    final List<Object> entryList = node.values;

    if (Util.isListNone(entryList)) {
      return null;
    }

    for (int k = 0; k < entryList.size(); k += 2) {
      final String entryName = (String) entryList.get(k);
      if (entryName.equals(name)) {
        @SuppressWarnings("unchecked")
        final List<String> entryValue = (List<String>) entryList
            .get(k + 1);
        return entryValue;
      }
    }

    return null;
  }

  /**
   * Make ASM class node for a JDK class.
   * <p>
   * Use class's own class loader to locate byte codes.
   */
  public static ClassNode classNode(final Class<?> klaz) throws Exception {

    final String name = klaz.getName();
    final String path = name.replace('.', '/') + ".class";

    ClassLoader loader = klaz.getClassLoader();
    if (loader == null) {
      loader = ClassLoader.getSystemClassLoader();
    }

    final InputStream stream = loader.getResourceAsStream(path);

    final ClassReader reader = new ClassReader(stream);

    final ClassNode node = new ClassNode();

    reader.accept(node, SKIP_MODE);

    return node;
  }

  /**
   * Combine class annotations.
   */
  @SuppressWarnings("unchecked")
  public static List<AnnotationNode> combine(final ClassNode node) {
    return Util.concatenate(node.invisibleAnnotations,
        node.visibleAnnotations);
  }

  /**
   * Combine field annotations.
   */
  @SuppressWarnings("unchecked")
  public static List<AnnotationNode> combine(final FieldNode node) {
    return Util.concatenate(node.invisibleAnnotations,
        node.visibleAnnotations);
  }

  /**
   * Combine method annotations.
   */
  @SuppressWarnings("unchecked")
  public static List<AnnotationNode> combine(final MethodNode node) {
    return Util.concatenate(node.invisibleAnnotations,
        node.visibleAnnotations);
  }

  /**
   * Find component annotation on a class.
   *
   * @return {@link Component} {@link AnnotationNode} or null if missing.
   */
  public static AnnotationNode componentAnno(final ClassNode node) {

    final List<AnnotationNode> annoList = combine(node);

    if (Util.isListNone(annoList)) {
      return null;
    }

    for (final AnnotationNode annoNode : annoList) {
      if (isComponentDesc(annoNode.desc)) {
        return annoNode;
      }
    }

    return null;

  }

  /**
   * Extract first method parameter type.
   */
  public static Type firstParamType(final MethodNode node) {
    return parameterArray(node)[0];
  }

  /**
   * Check component annotation on a class.
   */
  public static boolean hasComponentAnno(final Class<?> klaz)
      throws Exception {

    final ClassNode node = classNode(klaz);

    final AnnotationNode anno = componentAnno(node);

    return anno != null;

  }

  /**
   * Check class if class descriptor is {@link Activate}
   */
  public static boolean isActivateDesc(final String desc) {
    return DESC_ACTIVATE.equals(desc);
  }

  /**
   * Check class if class descriptor is {@link Component}
   */
  public static boolean isComponentDesc(final String desc) {
    return DESC_COMPONENT.equals(desc);
  }

  /**
   * Check class if class descriptor is {@link Deactivate}
   */
  public static boolean isDeactivateDesc(final String desc) {
    return DESC_DEACTIVATE.equals(desc);
  }

  /**
   * Check class if class descriptor is {@link Modified}
   */
  public static boolean isModifiedDesc(final String desc) {
    return DESC_MODIFIED.equals(desc);
  }

  /**
   * Check class if class descriptor is {@link Property}
   */
  public static boolean isPropertyDesc(final String desc) {
    return DESC_PROPERTY.equals(desc);
  }

  /**
   * Check class if class descriptor is {@link Reference}
   */
  public static boolean isReferenceDesc(final String desc) {
    return DESC_REFERENCE.equals(desc);
  }

  /**
   * 112.3.1
   * <p>
   * When using the event strategy, the bind and unbind methods must have one
   * of the following proto-types:
   * <p>
   * void <method-name>(ServiceReference);
   * <p>
   * void <method-name>(<parameter-type>);
   * <p>
   * void <method-name>(<parameter-type>, Map);
   * <p>
   * A suitable method is selected using the following priority:
   * <p>
   * 1. The method takes a single argument and the type of the argument is
   * org.osgi.framework.ServiceReference.
   * <p>
   * 2. The method takes a single argument and the type of the argument is the
   * type specified by the reference’s interface attribute.
   * <p>
   * 3. The method takes a single argument and the type of the argument is
   * assignable from the type specified by the reference’s interface
   * attribute. If multiple methods match this rule, this implies the method
   * name is overloaded and SCR may choose any of the methods to call.
   * <p>
   * 4. The method takes two argument and the type of the first argument is
   * the type specified by the reference’s interface attribute and the type of
   * the second argument is java.util.Map.
   * <p>
   * 5. The method takes two argument and the type of the first argument is
   * assignable from the type specified by the reference’s interface attribute
   * and the type of the second argument is java.util.Map. If multiple methods
   * match this rule, this implies the method name is overloaded and SCR may
   * choose any of the methods to call.
   */
  public static boolean isValidBindParam(final ClassLoader loader,
      final MethodNode node) throws Exception {
    final Type[] array = parameterArray(node);
    switch (array.length) {
    case 1: {
      final Class<?> klaz1 = loader.loadClass(array[0].getClassName());
      return klaz1.isInterface();
    }
    case 2: {
      final Class<?> klaz1 = loader.loadClass(array[0].getClassName());
      final Class<?> klaz2 = loader.loadClass(array[1].getClassName());
      return klaz1.isInterface()
          && java.util.Map.class.isAssignableFrom(klaz2);
    }
    default:
      return false;
    }
  }

  /**
   * Extract method parameters.
   */
  public static Type[] parameterArray(final MethodNode node) {
    return Type.getMethodType(node.desc).getArgumentTypes();
  }

  /**
   * Find property annotation on a field.
   *
   * @return {@link Property} {@link AnnotationNode} or null if missing.
   */
  public static AnnotationNode propertyAnno(final FieldNode node) {

    final List<AnnotationNode> annoList = combine(node);

    if (Util.isListNone(annoList)) {
      return null;
    }

    for (final AnnotationNode annoNode : annoList) {
      if (isPropertyDesc(annoNode.desc)) {
        return annoNode;
      }
    }

    return null;

  }

  /**
   * Find reference annotation on a method.
   *
   * @return {@link Reference} {@link AnnotationNode} or null if missing.
   */
  public static AnnotationNode referenceAnno(final MethodNode node) {

    final List<AnnotationNode> annoList = combine(node);

    if (Util.isListNone(annoList)) {
      return null;
    }

    for (final AnnotationNode annoNode : annoList) {
      if (isReferenceDesc(annoNode.desc)) {
        return annoNode;
      }
    }

    return null;

  }

  private UtilAsm() {

  }

}
TOP

Related Classes of com.carrotgarden.osgi.anno.scr.util.UtilAsm

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.