Package aQute.bnd.make.metatype

Source Code of aQute.bnd.make.metatype.MetaTypeReader

package aQute.bnd.make.metatype;

import java.io.*;
import java.util.*;
import java.util.regex.*;

import aQute.bnd.annotation.metatype.*;
import aQute.bnd.osgi.*;
import aQute.bnd.osgi.Clazz.MethodDef;
import aQute.bnd.osgi.Descriptors.TypeRef;
import aQute.lib.tag.*;
import aQute.libg.generics.*;

public class MetaTypeReader extends WriteResource {
  final Analyzer      reporter;
  Clazz          clazz;
  String          interfaces[];
  Tag            metadata  = new Tag("metatype:MetaData", new String[] {
      "xmlns:metatype", "http://www.osgi.org/xmlns/metatype/v1.1.0"
                    });
  Tag            ocd      = new Tag(metadata, "OCD");
  Tag            designate  = new Tag(metadata, "Designate");
  Tag            object    = new Tag(designate, "Object");

  // Resource
  String          extra;

  // One time init
  boolean          finished;

  // Designate
  boolean          override;
  String          designatePid;
  boolean          factory;

  // AD
  Map<MethodDef,Meta.AD>  methods    = new LinkedHashMap<MethodDef,Meta.AD>();

  // OCD
  Annotation        ocdAnnotation;

  MethodDef        method;

  public MetaTypeReader(Clazz clazz, Analyzer reporter) {
    this.clazz = clazz;
    this.reporter = reporter;
  }

  /**
   * @param id
   * @param name
   * @param cardinality
   * @param required
   * @param deflt
   * @param type
   * @param max
   * @param min
   * @param optionLabels
   * @param optionValues
   */

  static Pattern  COLLECTION  = Pattern.compile("(.*(Collection|Set|List|Queue|Stack|Deque))<(L.+;)>");

  private void addMethod(MethodDef method, Meta.AD ad) throws Exception {

    // Set all the defaults.

    String rtype = method.getGenericReturnType();
    String id = Configurable.mangleMethodName(method.getName());
    String name = Clazz.unCamel(id);

    int cardinality = 0;

    if (rtype.endsWith("[]")) {
      cardinality = Integer.MAX_VALUE;
      rtype = rtype.substring(0, rtype.length() - 2);
    }
    if (rtype.indexOf('<') > 0) {
      if (cardinality != 0)
        reporter.error(
            "AD for %s.%s uses an array of collections in return type (%s), Metatype allows either Vector or array",
            clazz.getClassName().getFQN(), method.getName(), method.getType().getFQN());
      Matcher m = COLLECTION.matcher(rtype);
      if (m.matches()) {
        rtype = Clazz.objectDescriptorToFQN(m.group(3));
        cardinality = Integer.MIN_VALUE;
      }
    }

    Meta.Type type = getType(rtype);

    boolean required = ad == null || ad.required();
    String deflt = null;
    String max = null;
    String min = null;
    String[] optionLabels = null;
    String[] optionValues = null;
    String description = null;

    TypeRef typeRef = reporter.getTypeRefFromFQN(rtype);
    Clazz c = reporter.findClass(typeRef);
    if (c != null && c.isEnum()) {
      optionValues = parseOptionValues(c);
    }

    // Now parse the annotation for any overrides

    if (ad != null) {
      if (ad.id() != null)
        id = ad.id();
      if (ad.name() != null)
        name = ad.name();
      if (ad.cardinality() != 0)
        cardinality = ad.cardinality();
      if (ad.type() != null)
        type = ad.type();
      // if (ad.required() || ad.deflt() == null)
      // required = true;

      if (ad.description() != null)
        description = ad.description();

      if (ad.optionLabels() != null)
        optionLabels = ad.optionLabels();
      if (ad.optionValues() != null)
        optionValues = ad.optionValues();

      if (ad.min() != null)
        min = ad.min();
      if (ad.max() != null)
        max = ad.max();

      if (ad.deflt() != null)
        deflt = ad.deflt();
    }

    if (optionValues != null) {
      if (optionLabels == null || optionLabels.length == 0) {
        optionLabels = new String[optionValues.length];
        for (int i = 0; i < optionValues.length; i++)
          optionLabels[i] = Clazz.unCamel(optionValues[i]);
      }

      if (optionLabels.length != optionValues.length) {
        reporter.error("Option labels and option values not the same length for %s", id);
        optionLabels = optionValues;
      }
    }

    Tag adt = new Tag(this.ocd, "AD");
    adt.addAttribute("name", name);
    adt.addAttribute("id", id);
    adt.addAttribute("cardinality", cardinality);
    adt.addAttribute("required", required);
    adt.addAttribute("default", deflt);
    adt.addAttribute("type", type);
    adt.addAttribute("max", max);
    adt.addAttribute("min", min);
    adt.addAttribute("description", description);

    if (optionLabels != null && optionValues != null) {
      for (int i = 0; i < optionLabels.length; i++) {
        Tag option = new Tag(adt, "Option");
        option.addAttribute("label", optionLabels[i]);
        option.addAttribute("value", optionValues[i]);
      }
    }
  }

  private String[] parseOptionValues(Clazz c) throws Exception {
    final List<String> values = Create.list();

    c.parseClassFileWithCollector(new ClassDataCollector() {
      @Override
      public void field(Clazz.FieldDef def) {
        if (def.isEnum()) {
          values.add(def.getName());
        }
      }
    });
    return values.toArray(new String[values.size()]);
  }

  Meta.Type getType(String rtype) {
    if (rtype.endsWith("[]")) {
      rtype = rtype.substring(0, rtype.length() - 2);
      if (rtype.endsWith("[]"))
        throw new IllegalArgumentException("Can only handle array of depth one");
    }

    if ("boolean".equals(rtype) || Boolean.class.getName().equals(rtype))
      return Meta.Type.Boolean;
    else if ("byte".equals(rtype) || Byte.class.getName().equals(rtype))
      return Meta.Type.Byte;
    else if ("char".equals(rtype) || Character.class.getName().equals(rtype))
      return Meta.Type.Character;
    else if ("short".equals(rtype) || Short.class.getName().equals(rtype))
      return Meta.Type.Short;
    else if ("int".equals(rtype) || Integer.class.getName().equals(rtype))
      return Meta.Type.Integer;
    else if ("long".equals(rtype) || Long.class.getName().equals(rtype))
      return Meta.Type.Long;
    else if ("float".equals(rtype) || Float.class.getName().equals(rtype))
      return Meta.Type.Float;
    else if ("double".equals(rtype) || Double.class.getName().equals(rtype))
      return Meta.Type.Double;
    else
      return Meta.Type.String;
  }

  class Find extends ClassDataCollector {

    @Override
    public void method(MethodDef mdef) {
      method = mdef;
      methods.put(mdef, null);
    }

    @Override
    public void annotation(Annotation annotation) {
      try {
        Meta.OCD ocd = annotation.getAnnotation(Meta.OCD.class);
        Meta.AD ad = annotation.getAnnotation(Meta.AD.class);
        if (ocd != null) {
          MetaTypeReader.this.ocdAnnotation = annotation;
        }
        if (ad != null) {
          assert method != null;
          // Fixup required since it is default true
          // but we have no access to these defaults.
          // i.e. the defaults are implemented in the code
          // thus here
          if ( annotation.get("required") == null)
            annotation.put("required", true);
         
          methods.put(method, ad);
        }
      }
      catch (Exception e) {
        reporter.error("Error during annotation parsing %s : %s", clazz, e);
        e.printStackTrace();
      }
    }

  }

  @Override
  public void write(OutputStream out) throws IOException {
    try {
      finish();
    }
    catch (Exception e) {
      throw new RuntimeException(e);
    }
    PrintWriter pw = new PrintWriter(new OutputStreamWriter(out, "UTF-8"));
    pw.println("<?xml version='1.0'?>");
    metadata.print(0, pw);
    pw.flush();
  }

  void finish() throws Exception {
    if (!finished) {
      finished = true;
      clazz.parseClassFileWithCollector(new Find());
      Meta.OCD ocd = null;
      if (this.ocdAnnotation != null)
        ocd = this.ocdAnnotation.getAnnotation(Meta.OCD.class);
      else
        ocd = Configurable.createConfigurable(Meta.OCD.class, new HashMap<String,Object>());

      // defaults
      String id = clazz.getClassName().getFQN();
      String name = Clazz.unCamel(clazz.getClassName().getShortName());
      String description = null;
      String localization = id;
      boolean factory = this.factory;

      if (ocd.id() != null)
        id = ocd.id();

      if (ocd.name() != null)
        name = ocd.name();

      if (ocd.localization() != null)
        localization = ocd.localization();

      if (ocd.description() != null)
        description = ocd.description();

      String pid = id;
      if (override) {
        pid = this.designatePid;
        factory = this.factory;
        id = this.designatePid; // for the felix problems
      } else {
        if (ocdAnnotation.get("factory") != null) {
          factory = true;
        }
      }

      this.ocd.addAttribute("name", name);
      this.ocd.addAttribute("id", id);
      this.ocd.addAttribute("description", description);
      this.ocd.addAttribute("localization", localization);

      // do ADs
      for (Map.Entry<MethodDef,Meta.AD> entry : methods.entrySet())
        addMethod(entry.getKey(), entry.getValue());

      this.designate.addAttribute("pid", pid);
      if (factory)
        this.designate.addAttribute("factoryPid", pid);

      this.object.addAttribute("ocdref", id);

    }
  }

  public void setDesignate(String pid, boolean factory) {
    this.override = true;
    this.factory = factory;
    this.designatePid = pid;
  }

  @Override
  public long lastModified() {
    return 0;
  }
}
TOP

Related Classes of aQute.bnd.make.metatype.MetaTypeReader

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.