Package org.codehaus.enunciate.contract.jaxb

Source Code of org.codehaus.enunciate.contract.jaxb.ElementRef$CollectionOfElementRefChoices

/*
* Copyright 2006-2008 Web Cohesion
*
* Licensed 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.codehaus.enunciate.contract.jaxb;

import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.declaration.ClassDeclaration;
import com.sun.mirror.declaration.MemberDeclaration;
import com.sun.mirror.declaration.TypeDeclaration;
import com.sun.mirror.type.*;
import com.sun.mirror.util.Types;
import net.sf.jelly.apt.Context;
import net.sf.jelly.apt.decorations.TypeMirrorDecorator;
import net.sf.jelly.apt.decorations.declaration.DecoratedTypeDeclaration;
import net.sf.jelly.apt.decorations.type.DecoratedTypeMirror;
import net.sf.jelly.apt.freemarker.FreemarkerModel;
import org.codehaus.enunciate.apt.EnunciateFreemarkerModel;
import org.codehaus.enunciate.contract.jaxb.types.XmlType;
import org.codehaus.enunciate.contract.validation.ValidationException;
import org.codehaus.enunciate.json.JsonName;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementRefs;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.namespace.QName;
import java.util.*;

/**
* An accessor that is marshalled in xml to an xml element.
*
* @author Ryan Heaton
*/
public class ElementRef extends Element {

  private final XmlElementRef xmlElementRef;
  private final Collection<ElementRef> choices;
  private final QName ref;
  private boolean isChoice = false;

  public ElementRef(MemberDeclaration delegate, TypeDefinition typedef) {
    super(delegate, typedef, null);

    XmlElementRef xmlElementRef = getAnnotation(XmlElementRef.class);
    XmlElementRefs xmlElementRefs = getAnnotation(XmlElementRefs.class);

    if (xmlElementRefs != null) {
      XmlElementRef[] elementRefChoices = xmlElementRefs.value();
      if (elementRefChoices.length == 0) {
        xmlElementRefs = null;
      }
      else if ((xmlElementRef == null) && (elementRefChoices.length == 1)) {
        xmlElementRef = elementRefChoices[0];
        xmlElementRefs = null;
      }
    }

    this.xmlElementRef = xmlElementRef;
    Collection<ElementRef> choices;
    if (xmlElementRefs != null) {
      choices = new ArrayList<ElementRef>();
      for (XmlElementRef elementRef : xmlElementRefs.value()) {
        choices.add(new ElementRef((MemberDeclaration) getDelegate(), getTypeDefinition(), elementRef));
      }

      this.ref = null;
    }
    else if (((DecoratedTypeMirror) getBareAccessorType()).isInstanceOf(JAXBElement.class.getName())) {
      //this is either a single-valued JAXBElement, or a parametric collection of them...
      //todo: throw an exception if this is referencing a non-global element for this namespace?
      choices = new ArrayList<ElementRef>();
      choices.add(this);
      this.ref = new QName(xmlElementRef.namespace(), xmlElementRef.name());
    }
    else if (isCollectionType()) {
      choices = new CollectionOfElementRefChoices();
      this.ref = null;
    }
    else {
      choices = new ArrayList<ElementRef>();
      choices.add(this);
      this.ref = loadRef();
    }
    this.choices = choices;
  }

  /**
   * Determines whether the class declaration is an instance of the declared type of the given fully-qualified name.
   *
   * @param classDeclaration The class declaration.
   * @param fqn              The FQN.
   * @return Whether the class declaration is an instance of the declared type of the given fully-qualified name.
   */
  protected boolean isInstanceOf(ClassDeclaration classDeclaration, String fqn) {
    AnnotationProcessorEnvironment env = Context.getCurrentEnvironment();
    Types utils = env.getTypeUtils();
    DeclaredType declaredType = utils.getDeclaredType(env.getTypeDeclaration(classDeclaration.getQualifiedName()));
    DecoratedTypeMirror decorated = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(declaredType);
    return decorated.isInstanceOf(fqn);
  }

  /**
   * Construct an element accessor with a specific element ref annotation.
   *
   * @param delegate      The delegate.
   * @param typedef       The type definition.
   * @param xmlElementRef The specific element ref annotation.
   */
  protected ElementRef(MemberDeclaration delegate, TypeDefinition typedef, XmlElementRef xmlElementRef) {
    super(delegate, typedef);
    this.xmlElementRef = xmlElementRef;
    this.choices = new ArrayList<ElementRef>();
    this.choices.add(this);
    this.ref = loadRef();
    this.isChoice = true;
  }

  /**
   * Construct an element accessor with a specific base type.
   *
   * @param delegate The delegate.
   * @param typedef  The type definition.
   * @param ref      The referenced root element.
   */
  private ElementRef(MemberDeclaration delegate, TypeDefinition typedef, RootElementDeclaration ref) {
    super(delegate, typedef);
    this.xmlElementRef = null;
    this.choices = new ArrayList<ElementRef>();
    this.choices.add(this);
    this.ref = new QName(ref.getNamespace(), ref.getName());
    this.isChoice = true;
  }

  /**
   * Load the qname of the referenced root element declaration.
   *
   * @return the qname of the referenced root element declaration.
   */
  protected QName loadRef() {
    TypeDeclaration declaration = null;
    String elementDeclaration;
    DecoratedTypeMirror refType;
    try {
      if ((xmlElementRef != null) && (xmlElementRef.type() != XmlElementRef.DEFAULT.class)) {
        Class typeClass = xmlElementRef.type();
        elementDeclaration = typeClass.getName();
        declaration = getEnv().getTypeDeclaration(typeClass.getName());
        refType = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(getEnv().getTypeUtils().getDeclaredType(declaration));
      }
      else {
        TypeMirror accessorType = getBareAccessorType();
        elementDeclaration = accessorType.toString();
        if (accessorType instanceof DeclaredType) {
          declaration = ((DeclaredType) accessorType).getDeclaration();
        }
        refType = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(accessorType);
      }
    }
    catch (MirroredTypeException e) {
      //This exception implies the ref is within the source base.
      TypeMirror typeMirror = e.getTypeMirror();
      elementDeclaration = typeMirror.toString();
      if (typeMirror instanceof DeclaredType) {
        declaration = ((DeclaredType) typeMirror).getDeclaration();
      }
      refType = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(typeMirror);
    }

    QName refQName = null;
    if (refType.isInstanceOf(JAXBElement.class.getName())) {
      String localName = xmlElementRef != null && !"##default".equals(xmlElementRef.name()) ? xmlElementRef.name() : null;
      String namespace = xmlElementRef != null ? xmlElementRef.namespace() : "";
      if (localName == null) {
        throw new ValidationException(getPosition(), "Member " + getName() + " of " + getTypeDefinition().getQualifiedName() + ": @XmlElementRef annotates a type JAXBElement without specifying the name of the JAXB element.");
      }
      refQName = new QName(namespace, localName);
    }
    else if (declaration instanceof ClassDeclaration && declaration.getAnnotation(XmlRootElement.class) != null) {
      ClassDeclaration classDeclaration = (ClassDeclaration) declaration;
      RootElementDeclaration refElement = new RootElementDeclaration(classDeclaration, ((EnunciateFreemarkerModel) FreemarkerModel.get()).findTypeDefinition(classDeclaration));
      refQName = new QName(refElement.getNamespace(), refElement.getName());
    }

    if (refQName == null) {
      throw new ValidationException(getPosition(), "Member " + getSimpleName() + " of " + getTypeDefinition().getQualifiedName() + ": " + elementDeclaration + " is neither JAXBElement nor a root element declaration.");
    }

    return refQName;
  }

  /**
   * Whether this is a choice of multiple element refs.
   *
   * @return Whether this is a choice of multiple element refs.
   */
  public boolean isElementRefs() {
    return (this.ref == null);
  }

  /**
   * The name of an element ref is the name of the element it references, unless the type is a JAXBElement, in which
   * case the name is specified.
   *
   * @return The name of the element ref.
   */
  @Override
  public String getName() {
    if (isElementRefs()) {
      throw new UnsupportedOperationException("No single reference for this element: multiple choices.");
    }

    return this.ref.getLocalPart();
  }

  /**
   * The namespace of an element ref is the namespace of the element it references, unless the type is a JAXBElement, in which
   * case the namespace is specified.
   *
   * @return The namespace of the element ref.
   */
  @Override
  public String getNamespace() {
    if (isElementRefs()) {
      throw new UnsupportedOperationException("No single reference for this element: multiple choices.");
    }

    //it's kind of weird to return null when the namespace is the default namesapce, but that's what the rest of the classes do...
    return XMLConstants.NULL_NS_URI.equals(this.ref.getNamespaceURI()) ? null : this.ref.getNamespaceURI();
  }

  /**
   * The ref for this element ref.
   *
   * @return The ref.
   */
  @Override
  public QName getRef() {
    if (isElementRefs()) {
      throw new UnsupportedOperationException("No single reference for this element: multiple choices.");
    }

    return this.ref;
  }

  /**
   * There is no base type for an element ref.
   *
   * @throws UnsupportedOperationException Because there is no such things as a base type for an element ref.
   */
  @Override
  public XmlType getBaseType() {
    throw new UnsupportedOperationException("There is no base type for an element ref.");
  }

  @Override
  public boolean isQNameType() {
    return false;
  }

  /**
   * The type of an element ref accessor can be specified by an annotation.
   *
   * @return The accessor type.
   */
  @Override
  public TypeMirror getAccessorType() {
    TypeMirror specifiedType = null;
    try {
      if ((xmlElementRef != null) && (xmlElementRef.type() != XmlElementRef.DEFAULT.class)) {
        Class clazz = xmlElementRef.type();
        specifiedType = getAccessorType(clazz);
      }
    }
    catch (MirroredTypeException e) {
      // The mirrored type exception implies that the specified type is within the source base.
      specifiedType = TypeMirrorDecorator.decorate(e.getTypeMirror());
    }

    if (specifiedType != null) {
      if (!isChoice) {
        DecoratedTypeMirror accessorType = (DecoratedTypeMirror) super.getAccessorType();

        if (accessorType.isCollection()) {
          AnnotationProcessorEnvironment ape = Context.getCurrentEnvironment();
          Types types = ape.getTypeUtils();
          if (specifiedType instanceof PrimitiveType) {
            specifiedType = types.getPrimitiveType(((PrimitiveType) specifiedType).getKind());
          }
          else {
            specifiedType = types.getDeclaredType(ape.getTypeDeclaration(((DeclaredType) specifiedType).getDeclaration().getQualifiedName()));
          }
          specifiedType = TypeMirrorDecorator.decorate(types.getDeclaredType(ape.getTypeDeclaration(((DeclaredType) accessorType).getDeclaration().getQualifiedName()), specifiedType));
        }
        else if (accessorType.isArray() && !(specifiedType instanceof ArrayType)) {
          Types types = Context.getCurrentEnvironment().getTypeUtils();
          if (specifiedType instanceof PrimitiveType) {
            specifiedType = types.getPrimitiveType(((PrimitiveType) specifiedType).getKind());
          }
          else {
            TypeDeclaration decl = ((DeclaredType) specifiedType).getDeclaration();
            while (decl instanceof DecoratedTypeDeclaration) {
              decl = (TypeDeclaration) ((DecoratedTypeDeclaration) decl).getDelegate();
            }
            specifiedType = types.getDeclaredType(decl);
          }
          specifiedType = TypeMirrorDecorator.decorate(types.getArrayType(specifiedType));
        }
      }

      return specifiedType;
    }

    return super.getAccessorType();
  }

  /**
   * An element ref is not binary data.  It may refer to an element that is binary data, though...
   *
   * @return false
   */
  @Override
  public boolean isBinaryData() {
    return false;
  }

  /**
   * An element ref is not a swa ref.  In may refer to an element that is a swa ref, though...
   *
   * @return false
   */
  @Override
  public boolean isSwaRef() {
    return false;
  }

  /**
   * An element ref is not an MTOM attachment.  It may refer to an element that is an MTOM attachment, though...
   *
   * @return false
   */
  @Override
  public boolean isMTOMAttachment() {
    return false;
  }

  /**
   * An element ref is not nillable.
   *
   * @return false
   */
  @Override
  public boolean isNillable() {
    return false;
  }

  /**
   * An element ref is not required.
   *
   * @return false.
   */
  @Override
  public boolean isRequired() {
    return false;
  }

  /**
   * The min occurs of an element ref is 0
   *
   * @return 0
   */
  @Override
  public int getMinOccurs() {
    return isRequired() ? 1 : 0;
  }

  /**
   * The choices for this element.
   *
   * @return The choices for this element.
   */
  @Override
  public Collection<ElementRef> getChoices() {
    return choices;
  }

  /**
   * The current environment.
   *
   * @return The current environment.
   */
  protected AnnotationProcessorEnvironment getEnv() {
    return Context.getCurrentEnvironment();
  }

  @Override
  public boolean isElementRef() {
    return true;
  }

  @Override
  public String getJsonMemberName() {
    JsonName jsonName = getAnnotation(JsonName.class);
    return jsonName == null ? (isElementRefs() ? getSimpleName() : getName()) : jsonName.value();
  }

  /**
   * Lazy-loaded collection of element ref choices.
   *
   * @author Ryan Heaton
   */
  private class CollectionOfElementRefChoices extends AbstractCollection<ElementRef> {

    public Iterator<ElementRef> iterator() {
      return lookupRefs().iterator();
    }

    public int size() {
      return lookupRefs().size();
    }

    private Collection<ElementRef> lookupRefs() {
      Collection<ElementRef> choices = new ArrayList<ElementRef>();
      Collection<QName> qnamesAdded = new HashSet<QName>();

      //if it's a parametric collection type, we need to provide a choice between all subclasses of the base type.
      TypeMirror typeMirror = getBareAccessorType();
      if (typeMirror instanceof DeclaredType) {
        String fqn = ((DeclaredType) typeMirror).getDeclaration().getQualifiedName();
        EnunciateFreemarkerModel model = ((EnunciateFreemarkerModel) FreemarkerModel.get());
        for (RootElementDeclaration rootElement : model.getRootElementDeclarations()) {
          if (isInstanceOf(rootElement, fqn)) {
            if (qnamesAdded.add(rootElement.getQname())) {
              choices.add(new ElementRef((MemberDeclaration) getDelegate(), getTypeDefinition(), rootElement));
            }
          }
        }
      }

      return choices;
    }
  }
}
TOP

Related Classes of org.codehaus.enunciate.contract.jaxb.ElementRef$CollectionOfElementRefChoices

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.