Package ca.uhn.fhir.parser

Source Code of ca.uhn.fhir.parser.ParserState$BaseState

package ca.uhn.fhir.parser;

import static org.apache.commons.lang3.StringUtils.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;

import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimeElemContainedResources;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeNarrativeDefinition;
import ca.uhn.fhir.context.RuntimeResourceBlockDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeResourceReferenceDefinition;
import ca.uhn.fhir.model.api.BaseBundle;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleCategory;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.ICompositeDatatype;
import ca.uhn.fhir.model.api.ICompositeElement;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.IResourceBlock;
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.rest.server.Constants;

class ParserState<T extends IElement> {

  private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ParserState.class);
  private FhirContext myContext;
  private T myObject;
  private BaseState myState;
  private boolean myJsonMode;

  private ParserState(FhirContext theContext, boolean theJsonMode) {
    myContext = theContext;
    myJsonMode = theJsonMode;
  }

  public void attributeValue(String theName, String theValue) throws DataFormatException {
    myState.attributeValue(theName, theValue);
  }

  public void endingElement() throws DataFormatException {
    myState.endingElement();
  }

  public void enteringNewElement(String theNamespaceURI, String theName) throws DataFormatException {
    myState.enteringNewElement(theNamespaceURI, theName);
  }

  public void enteringNewElementExtension(StartElement theElem, String theUrlAttr, boolean theIsModifier) {
    myState.enteringNewElementExtension(theElem, theUrlAttr, theIsModifier);
  }

  @SuppressWarnings("unchecked")
  public T getObject() {
    return (T) myState.getCurrentElement();
  }

  public boolean isComplete() {
    return myObject != null;
  }

  public void string(String theData) {
    myState.string(theData);
  }

  public boolean verifyNamespace(String theExpect, String theActual) {
    if (myJsonMode) {
      return true;
    }
    return StringUtils.equals(theExpect, theActual);
  }

  /**
   * Invoked after any new XML event is individually processed, containing a
   * copy of the XML event. This is basically intended for embedded XHTML
   * content
   */
  public void xmlEvent(XMLEvent theNextEvent) {
    myState.xmlEvent(theNextEvent);
  }

  private void pop() {
    myState = myState.myStack;
    myState.wereBack();
  }

  private void push(BaseState theState) {
    theState.setStack(myState);
    myState = theState;
  }

  public static ParserState<Bundle> getPreAtomInstance(FhirContext theContext, boolean theJsonMode) throws DataFormatException {
    ParserState<Bundle> retVal = new ParserState<Bundle>(theContext, theJsonMode);
    retVal.push(retVal.new PreAtomState());
    return retVal;
  }

  /**
   * @param theResourceType
   *            May be null
   */
  public static <T extends IResource> ParserState<T> getPreResourceInstance(Class<T> theResourceType, FhirContext theContext, boolean theJsonMode) throws DataFormatException {
    ParserState<T> retVal = new ParserState<T>(theContext, theJsonMode);
    retVal.push(retVal.new PreResourceState(theResourceType));
    return retVal;
  }

  public class AtomAuthorState extends BaseState {

    private BaseBundle myInstance;

    public AtomAuthorState(BaseBundle theEntry) {
      super(null);
      myInstance = theEntry;
    }

    @Override
    public void endingElement() throws DataFormatException {
      pop();
    }

    @Override
    public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
      if ("name".equals(theLocalPart)) {
        push(new AtomPrimitiveState(myInstance.getAuthorName()));
      } else if ("uri".equals(theLocalPart)) {
        push(new AtomPrimitiveState(myInstance.getAuthorUri()));
      } else {
        throw new DataFormatException("Unexpected element: " + theLocalPart);
      }
    }

  }

  public class AtomCategoryState extends BaseState {

    private BundleCategory myInstance;

    public AtomCategoryState(BundleCategory theEntry) {
      super(null);
      myInstance = theEntry;
    }

    @Override
    public void attributeValue(String theName, String theValue) throws DataFormatException {
      if ("term".equals(theName)) {
        myInstance.setTerm(theValue);
      } else if ("label".equals(theName)) {
        myInstance.setLabel(theValue);
      } else if ("scheme".equals(theName)) {
        myInstance.setScheme(theValue);
      }
    }

    @Override
    public void endingElement() throws DataFormatException {
      pop();
    }

    @Override
    public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
      throw new DataFormatException("Unexpected element: " + theLocalPart);
    }

  }

  public class AtomEntryState extends BaseState {

    private BundleEntry myEntry;

    public AtomEntryState(Bundle theInstance) {
      super(null);
      myEntry = new BundleEntry();
      theInstance.getEntries().add(myEntry);
    }

    @Override
    public void endingElement() throws DataFormatException {
      populateResourceMetadata();
      pop();
    }

    private void populateResourceMetadata() {
      if (myEntry.getResource() == null) {
        return;
      }

      Map<ResourceMetadataKeyEnum, Object> metadata = myEntry.getResource().getResourceMetadata();
      if (myEntry.getPublished().isEmpty() == false) {
        metadata.put(ResourceMetadataKeyEnum.PUBLISHED, myEntry.getPublished());
      }
      if (myEntry.getUpdated().isEmpty() == false) {
        metadata.put(ResourceMetadataKeyEnum.UPDATED, myEntry.getUpdated());
      }
      if (!myEntry.getLinkSelf().isEmpty()) {
        String subStr = "/" + Constants.PARAM_HISTORY + "/";
        String linkSelfValue = myEntry.getLinkSelf().getValue();
        int startIndex = linkSelfValue.indexOf(subStr);
        if (startIndex > 0) {
          startIndex = startIndex + subStr.length();
          int endIndex = linkSelfValue.indexOf('?', startIndex);
          if (endIndex == -1) {
            endIndex = linkSelfValue.length();
          }
          String versionId = linkSelfValue.substring(startIndex, endIndex);
          if (isNotBlank(versionId)) {
            int idx = versionId.indexOf('/');
            if (idx != -1) {
              // Just in case
              ourLog.warn("Bundle entry link-self contains path information beyond version (this will be ignored): {}", versionId);
              versionId = versionId.substring(0, idx);
            }
            metadata.put(ResourceMetadataKeyEnum.VERSION_ID, versionId);

          }
        }
      }

    }

    @Override
    public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
      if ("title".equals(theLocalPart)) {
        push(new AtomPrimitiveState(myEntry.getTitle()));
      } else if ("id".equals(theLocalPart)) {
        push(new AtomPrimitiveState(myEntry.getId()));
      } else if ("link".equals(theLocalPart)) {
        push(new AtomLinkState(myEntry));
      } else if ("updated".equals(theLocalPart)) {
        push(new AtomPrimitiveState(myEntry.getUpdated()));
      } else if ("published".equals(theLocalPart)) {
        push(new AtomPrimitiveState(myEntry.getPublished()));
      } else if ("author".equals(theLocalPart)) {
        push(new AtomAuthorState(myEntry));
      } else if ("content".equals(theLocalPart)) {
        push(new PreResourceState(myEntry));
      } else if ("summary".equals(theLocalPart)) {
        push(new XhtmlState(getPreResourceState(), myEntry.getSummary(), false));
      } else if ("category".equals(theLocalPart)) {
        push(new AtomCategoryState(myEntry.addCategory()));
      } else {
        throw new DataFormatException("Unexpected element in entry: " + theLocalPart);
      }

      // TODO: handle category
    }

  }

  private class AtomLinkState extends BaseState {

    private BundleEntry myEntry;
    private String myHref;
    private Bundle myInstance;
    private String myRel;

    public AtomLinkState(Bundle theInstance) {
      super(null);
      myInstance = theInstance;
    }

    public AtomLinkState(BundleEntry theEntry) {
      super(null);
      myEntry = theEntry;
    }

    @Override
    public void attributeValue(String theName, String theValue) throws DataFormatException {
      if ("rel".equals(theName)) {
        myRel = theValue;
      } else if ("href".equals(theName)) {
        myHref = theValue;
      }
    }

    @Override
    public void endingElement() throws DataFormatException {
      if (myInstance != null) {
        if ("self".equals(myRel)) {
          myInstance.getLinkSelf().setValueAsString(myHref);
        } else if ("first".equals(myRel)) {
          myInstance.getLinkFirst().setValueAsString(myHref);
        } else if ("previous".equals(myRel)) {
          myInstance.getLinkPrevious().setValueAsString(myHref);
        } else if ("next".equals(myRel)) {
          myInstance.getLinkNext().setValueAsString(myHref);
        } else if ("last".equals(myRel)) {
          myInstance.getLinkLast().setValueAsString(myHref);
        } else if ("fhir-base".equals(myRel)) {
          myInstance.getLinkBase().setValueAsString(myHref);
        }
      } else {
        myEntry.getLinkSelf().setValueAsString(myHref);
      }
      pop();
    }

    @Override
    public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
      throw new DataFormatException("Found unexpected element content '" + theLocalPart + "' within <link>");
    }

  }

  private class AtomPrimitiveState extends BaseState {

    private String myData;
    private IPrimitiveDatatype<?> myPrimitive;

    public AtomPrimitiveState(IPrimitiveDatatype<?> thePrimitive) {
      super(null);
      Validate.notNull(thePrimitive, "thePrimitive");
      myPrimitive = thePrimitive;
    }

    @Override
    public void endingElement() throws DataFormatException {
      myPrimitive.setValueAsString(myData);
      pop();
    }

    @Override
    public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
      throw new DataFormatException("Unexpected nested element in atom tag: " + theLocalPart);
    }

    @Override
    public void string(String theData) {
      if (myData == null) {
        myData = theData;
      } else {
        // this shouldn't generally happen so it's ok that it's
        // inefficient
        myData = myData + theData;
      }
    }

    @Override
    protected IElement getCurrentElement() {
      return null;
    }

  }

  private class AtomState extends BaseState {

    private Bundle myInstance;

    public AtomState(Bundle theInstance) {
      super(null);
      myInstance = theInstance;
    }

    @Override
    public void endingElement() throws DataFormatException {
      pop();
    }

    @Override
    public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
      if ("entry".equals(theLocalPart) && verifyNamespace(XmlParser.ATOM_NS, theNamespaceURI)) {
        push(new AtomEntryState(myInstance));
      } else if (theLocalPart.equals("published")) {
        push(new AtomPrimitiveState(myInstance.getPublished()));
      } else if (theLocalPart.equals("title")) {
        push(new AtomPrimitiveState(myInstance.getTitle()));
      } else if ("id".equals(theLocalPart)) {
        push(new AtomPrimitiveState(myInstance.getBundleId()));
      } else if ("link".equals(theLocalPart)) {
        push(new AtomLinkState(myInstance));
      } else if ("totalResults".equals(theLocalPart) && verifyNamespace(XmlParser.OPENSEARCH_NS, theNamespaceURI)) {
        push(new AtomPrimitiveState(myInstance.getTotalResults()));
      } else if ("updated".equals(theLocalPart)) {
        push(new AtomPrimitiveState(myInstance.getUpdated()));
      } else if ("author".equals(theLocalPart)) {
        push(new AtomAuthorState(myInstance));
      } else {
        throw new DataFormatException("Unexpected element: " + theLocalPart);
      }

      // TODO: handle category and DSig
    }

    @Override
    protected IElement getCurrentElement() {
      return myInstance;
    }

  }

  private abstract class BaseState {

    private PreResourceState myPreResourceState;
    private BaseState myStack;

    public BaseState(PreResourceState thePreResourceState) {
      super();
      myPreResourceState = thePreResourceState;
    }

    @SuppressWarnings("unused")
    public void attributeValue(String theName, String theValue) throws DataFormatException {
      // ignore by default
    }

    @SuppressWarnings("unused")
    public void endingElement() throws DataFormatException {
      // ignore by default
    }

    @SuppressWarnings("unused")
    public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
      // ignore by default
    }

    /**
     * Default implementation just handles undeclared extensions
     */
    public void enteringNewElementExtension(@SuppressWarnings("unused") StartElement theElement, String theUrlAttr, boolean theIsModifier) {
      if (myPreResourceState != null && getCurrentElement() instanceof ISupportsUndeclaredExtensions) {
        ExtensionDt newExtension = new ExtensionDt(theIsModifier, theUrlAttr);
        ISupportsUndeclaredExtensions elem = (ISupportsUndeclaredExtensions) getCurrentElement();
        if (theIsModifier) {
          elem.getUndeclaredModifierExtensions().add(newExtension);
        } else {
          elem.getUndeclaredExtensions().add(newExtension);
        }
        ExtensionState newState = new ExtensionState(myPreResourceState, newExtension);
        push(newState);
      } else {
        throw new DataFormatException("Type " + getCurrentElement() + " does not support undeclared extentions, and found an extension with URL: " + theUrlAttr);
      }
    }

    public PreResourceState getPreResourceState() {
      return myPreResourceState;
    }

    public void setStack(BaseState theState) {
      myStack = theState;
    }

    public void string(@SuppressWarnings("unused") String theData) {
      // ignore by default
    }

    public void wereBack() {
      // allow an implementor to override
    }

    public void xmlEvent(@SuppressWarnings("unused") XMLEvent theNextEvent) {
      // ignore
    }

    protected IElement getCurrentElement() {
      return null;
    }

    public boolean isPreResource() {
      return false;
    }

  }

  private class ContainedResourcesState extends PreResourceState {

    public ContainedResourcesState(PreResourceState thePreResourcesState) {
      super(thePreResourcesState);
    }

    @Override
    public void endingElement() throws DataFormatException {
      pop();
    }

    @Override
    public void wereBack() {
      IResource res = getCurrentElement();
      assert res != null;
      if (res.getId() == null || res.getId().isEmpty()) {
        ourLog.debug("Discarding contained resource with no ID!");
      } else {
        getPreResourceState().getContainedResources().put(res.getId().getValueAsString(), res);
      }
      getPreResourceState().getCurrentElement().getContained().getContainedResources().add(res);
    }

  }

  private class DeclaredExtensionState extends BaseState {

    private IElement myChildInstance;
    private RuntimeChildDeclaredExtensionDefinition myDefinition;
    private IElement myParentInstance;
    private PreResourceState myPreResourceState;

    public DeclaredExtensionState(PreResourceState thePreResourceState, RuntimeChildDeclaredExtensionDefinition theDefinition, IElement theParentInstance) {
      super(thePreResourceState);
      myPreResourceState = thePreResourceState;
      myDefinition = theDefinition;
      myParentInstance = theParentInstance;
    }

    @Override
    public void endingElement() throws DataFormatException {
      pop();
    }

    @Override
    public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
      BaseRuntimeElementDefinition<?> target = myDefinition.getChildByName(theLocalPart);
      if (target == null) {
        throw new DataFormatException("Unknown extension element name: " + theLocalPart);
      }

      switch (target.getChildType()) {
      case COMPOSITE_DATATYPE: {
        BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
        ICompositeDatatype newChildInstance = (ICompositeDatatype) compositeTarget.newInstance();
        myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
        ElementCompositeState newState = new ElementCompositeState(myPreResourceState, compositeTarget, newChildInstance);
        push(newState);
        return;
      }
      case PRIMITIVE_DATATYPE: {
        RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
        IPrimitiveDatatype<?> newChildInstance = primitiveTarget.newInstance();
        myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
        PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
        push(newState);
        return;
      }
      case RESOURCE_REF: {
        ResourceReferenceDt newChildInstance = new ResourceReferenceDt();
        myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
        ResourceReferenceState newState = new ResourceReferenceState(getPreResourceState(), newChildInstance);
        push(newState);
        return;
      }
      case PRIMITIVE_XHTML:
      case RESOURCE:
      case RESOURCE_BLOCK:
      case UNDECL_EXT:
      case EXTENSION_DECLARED:
      default:
        break;
      }
    }

    @Override
    public void enteringNewElementExtension(StartElement theElement, String theUrlAttr, boolean theIsModifier) {
      RuntimeChildDeclaredExtensionDefinition declaredExtension = myDefinition.getChildExtensionForUrl(theUrlAttr);
      if (declaredExtension != null) {
        if (myChildInstance == null) {
          myChildInstance = myDefinition.newInstance();
          myDefinition.getMutator().addValue(myParentInstance, myChildInstance);
        }
        BaseState newState = new DeclaredExtensionState(getPreResourceState(), declaredExtension, myChildInstance);
        push(newState);
      } else {
        super.enteringNewElementExtension(theElement, theUrlAttr, theIsModifier);
      }
    }

    @Override
    protected IElement getCurrentElement() {
      return myParentInstance;
    }

  }

  private class SwallowChildrenWholeState extends BaseState {

    private int myDepth;

    public SwallowChildrenWholeState(PreResourceState thePreResourceState) {
      super(thePreResourceState);
    }

    @Override
    public void endingElement() throws DataFormatException {
      myDepth--;
      if (myDepth < 0) {
        pop();
      }
    }

    @Override
    public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
      myDepth++;
    }

  }

  private class ElementCompositeState extends BaseState {

    private BaseRuntimeElementCompositeDefinition<?> myDefinition;
    private ICompositeElement myInstance;

    public ElementCompositeState(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, ICompositeElement theInstance) {
      super(thePreResourceState);
      myDefinition = theDef;
      myInstance = theInstance;
    }

    @Override
    public void attributeValue(String theName, String theValue) throws DataFormatException {
      if ("id".equals(theName)) {
        myInstance.setId(new IdDt(theValue));
      }
    }

    @SuppressWarnings("unchecked")
    @Override
    public void endingElement() {
      pop();
      if (myState == null) {
        myObject = (T) myInstance;
      }
    }

    @Override
    public void enteringNewElement(String theNamespace, String theChildName) throws DataFormatException {
      BaseRuntimeChildDefinition child;
      try {
        child = myDefinition.getChildByNameOrThrowDataFormatException(theChildName);
      } catch (DataFormatException e) {
        if (false) {// TODO: make this configurable
          throw e;
        }
        ourLog.warn(e.getMessage());
        push(new SwallowChildrenWholeState(getPreResourceState()));
        return;
      }
      BaseRuntimeElementDefinition<?> target = child.getChildByName(theChildName);
      if (target == null) {
        throw new DataFormatException("Found unexpected element '" + theChildName + "' in parent element '" + myDefinition.getName() + "'. Valid names are: " + child.getValidChildNames());
      }

      switch (target.getChildType()) {
      case COMPOSITE_DATATYPE: {
        BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
        ICompositeDatatype newChildInstance = (ICompositeDatatype) compositeTarget.newInstance(child.getInstanceConstructorArguments());
        child.getMutator().addValue(myInstance, newChildInstance);
        ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), compositeTarget, newChildInstance);
        push(newState);
        return;
      }
      case PRIMITIVE_DATATYPE: {
        RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
        IPrimitiveDatatype<?> newChildInstance;
        newChildInstance = primitiveTarget.newInstance(child.getInstanceConstructorArguments());
        child.getMutator().addValue(myInstance, newChildInstance);
        PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
        push(newState);
        return;
      }
      case RESOURCE_REF: {
        RuntimeResourceReferenceDefinition resourceRefTarget = (RuntimeResourceReferenceDefinition) target;
        ResourceReferenceDt newChildInstance = new ResourceReferenceDt();
        getPreResourceState().getResourceReferences().add(newChildInstance);
        child.getMutator().addValue(myInstance, newChildInstance);
        ResourceReferenceState newState = new ResourceReferenceState(getPreResourceState(), newChildInstance);
        push(newState);
        return;
      }
      case RESOURCE_BLOCK: {
        RuntimeResourceBlockDefinition blockTarget = (RuntimeResourceBlockDefinition) target;
        IResourceBlock newBlockInstance = blockTarget.newInstance();
        child.getMutator().addValue(myInstance, newBlockInstance);
        ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), blockTarget, newBlockInstance);
        push(newState);
        return;
      }
      case PRIMITIVE_XHTML: {
        RuntimePrimitiveDatatypeNarrativeDefinition xhtmlTarget = (RuntimePrimitiveDatatypeNarrativeDefinition) target;
        XhtmlDt newDt = xhtmlTarget.newInstance();
        child.getMutator().addValue(myInstance, newDt);
        XhtmlState state = new XhtmlState(getPreResourceState(), newDt, true);
        push(state);
        return;
      }
      case CONTAINED_RESOURCES: {
        RuntimeElemContainedResources targetElem = (RuntimeElemContainedResources) target;
        List<? extends IElement> values = child.getAccessor().getValues(myInstance);
        ContainedDt newDt;
        if (values == null || values.isEmpty() || values.get(0) == null) {
          newDt = targetElem.newInstance();
          child.getMutator().addValue(myInstance, newDt);
        } else {
          newDt = (ContainedDt) values.get(0);
        }
        ContainedResourcesState state = new ContainedResourcesState(getPreResourceState());
        push(state);
        return;
      }
      case UNDECL_EXT:
      case RESOURCE: {
        // Throw an exception because this shouldn't happen here
        break;
      }
      }

      throw new DataFormatException("Illegal resource position: " + target.getChildType());
    }

    @Override
    public void enteringNewElementExtension(StartElement theElement, String theUrlAttr, boolean theIsModifier) {
      RuntimeChildDeclaredExtensionDefinition declaredExtension = myDefinition.getDeclaredExtension(theUrlAttr);
      if (declaredExtension != null) {
        BaseState newState = new DeclaredExtensionState(getPreResourceState(), declaredExtension, myInstance);
        push(newState);
      } else {
        super.enteringNewElementExtension(theElement, theUrlAttr, theIsModifier);
      }
    }

    @Override
    protected IElement getCurrentElement() {
      return myInstance;
    }

  }

  private class ExtensionState extends BaseState {

    private ExtensionDt myExtension;

    public ExtensionState(PreResourceState thePreResourceState, ExtensionDt theExtension) {
      super(thePreResourceState);
      myExtension = theExtension;
    }

    @Override
    public void endingElement() throws DataFormatException {
      if (myExtension.getValue() != null && myExtension.getUndeclaredExtensions().size() > 0) {
        throw new DataFormatException("Extension must not have both a value and other contained extensions");
      }
      pop();
    }

    @Override
    public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
      BaseRuntimeElementDefinition<?> target = myContext.getRuntimeChildUndeclaredExtensionDefinition().getChildByName(theLocalPart);
      if (target == null) {
        throw new DataFormatException("Unknown extension element name: " + theLocalPart);
      }

      switch (target.getChildType()) {
      case COMPOSITE_DATATYPE: {
        BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
        ICompositeDatatype newChildInstance = (ICompositeDatatype) compositeTarget.newInstance();
        myExtension.setValue(newChildInstance);
        ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), compositeTarget, newChildInstance);
        push(newState);
        return;
      }
      case PRIMITIVE_DATATYPE: {
        RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
        IPrimitiveDatatype<?> newChildInstance = primitiveTarget.newInstance();
        myExtension.setValue(newChildInstance);
        PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
        push(newState);
        return;
      }
      case RESOURCE_REF: {
        ResourceReferenceDt newChildInstance = new ResourceReferenceDt();
        myExtension.setValue(newChildInstance);
        ResourceReferenceState newState = new ResourceReferenceState(getPreResourceState(), newChildInstance);
        push(newState);
        return;
      }
      case PRIMITIVE_XHTML:
      case RESOURCE:
      case RESOURCE_BLOCK:
      case UNDECL_EXT:
        break;
      }
    }

    @Override
    protected IElement getCurrentElement() {
      return myExtension;
    }

  }

  private class PreAtomState extends BaseState {

    private Bundle myInstance;

    public PreAtomState() {
      super(null);
    }

    @Override
    public void endingElement() throws DataFormatException {
      // ignore
    }

    @Override
    public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
      if (!"feed".equals(theLocalPart)) {
        throw new DataFormatException("Expecting outer element called 'feed', found: " + theLocalPart);
      }

      myInstance = new Bundle();
      push(new AtomState(myInstance));

    }

    @SuppressWarnings("unchecked")
    @Override
    public void wereBack() {
      myObject = (T) myInstance;
    }

    @Override
    protected IElement getCurrentElement() {
      return myInstance;
    }

  }

  private class PreResourceState extends BaseState {

    private Map<String, IResource> myContainedResources = new HashMap<String, IResource>();
    private BundleEntry myEntry;
    private IResource myInstance;
    private List<ResourceReferenceDt> myResourceReferences = new ArrayList<ResourceReferenceDt>();

    private Class<? extends IResource> myResourceType;

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

    public PreResourceState(BundleEntry theEntry) {
      super(null);
      myEntry = theEntry;
    }

    /**
     * @param theResourceType
     *            May be null
     */
    public PreResourceState(Class<? extends IResource> theResourceType) {
      super(null);
      myResourceType = theResourceType;
    }

    public PreResourceState(PreResourceState thePreResourcesState) {
      super(thePreResourcesState);
    }

    @Override
    public void endingElement() throws DataFormatException {
      pop();
    }

    @Override
    public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
      BaseRuntimeElementDefinition<?> definition;
      if (myResourceType == null) {
        definition = myContext.getResourceDefinition(theLocalPart);
        if (!(definition instanceof RuntimeResourceDefinition)) {
          throw new DataFormatException("Element '" + theLocalPart + "' is not a resource, expected a resource at this position");
        }
      } else {
        definition = myContext.getResourceDefinition(myResourceType);
        if (!StringUtils.equals(theLocalPart, definition.getName())) {
          throw new DataFormatException("Incorrect resource root element '" + theLocalPart + "', expected: '" + definition.getName() + "'");
        }
      }

      RuntimeResourceDefinition def = (RuntimeResourceDefinition) definition;
      myInstance = def.newInstance();
      if (myEntry != null) {
        myEntry.setResource(myInstance);
      }

      push(new ElementCompositeState(this, def, myInstance));
    }

    public Map<String, IResource> getContainedResources() {
      return myContainedResources;
    }

    public List<ResourceReferenceDt> getResourceReferences() {
      return myResourceReferences;
    }

    @SuppressWarnings("unchecked")
    @Override
    public void wereBack() {
      if (myEntry == null) {
        myObject = (T) myInstance;
      }

      for (ResourceReferenceDt nextRef : myResourceReferences) {
        String ref = nextRef.getReference().getValue();
        if (isNotBlank(ref)) {
          if (ref.startsWith("#")) {
            IResource target = myContainedResources.get(ref.substring(1));
            if (target != null) {
              nextRef.setResource(target);
            } else {
              ourLog.warn("Resource contains unknown local ref: " + ref);
            }
          }
        }
      }

    }

    @Override
    protected IResource getCurrentElement() {
      return myInstance;
    }

  }

  private class PrimitiveState extends BaseState {
    private IPrimitiveDatatype<?> myInstance;

    public PrimitiveState(PreResourceState thePreResourceState, IPrimitiveDatatype<?> theInstance) {
      super(thePreResourceState);
      myInstance = theInstance;
    }

    @Override
    public void attributeValue(String theName, String theValue) throws DataFormatException {
      if ("value".equals(theName)) {
        myInstance.setValueAsString(theValue);
      } else if ("id".equals(theName)) {
        myInstance.setId(new IdDt(theValue));
      }
    }

    @Override
    public void endingElement() {
      pop();
    }

    // @Override
    // public void enteringNewElementExtension(StartElement theElement,
    // String theUrlAttr) {
    // if (myInstance instanceof ISupportsUndeclaredExtensions) {
    // UndeclaredExtension ext = new UndeclaredExtension(theUrlAttr);
    // ((ISupportsUndeclaredExtensions)
    // myInstance).getUndeclaredExtensions().add(ext);
    // push(new ExtensionState(ext));
    // }
    // }

    @Override
    public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
      throw new Error("Element " + theLocalPart + " in primitive!"); // TODO:
                                      // can
                                      // this
                                      // happen?
    }

    @Override
    protected IElement getCurrentElement() {
      return myInstance;
    }

  }

  private class ResourceReferenceState extends BaseState {

    private ResourceReferenceDt myInstance;
    private ResourceReferenceSubState mySubState;

    public ResourceReferenceState(PreResourceState thePreResourceState, ResourceReferenceDt theInstance) {
      super(thePreResourceState);
      myInstance = theInstance;
      mySubState = ResourceReferenceSubState.INITIAL;
    }

    @Override
    public void attributeValue(String theName, String theValue) throws DataFormatException {
      if (!"value".equals(theName)) {
        return;
      }

      switch (mySubState) {
      case DISPLAY:
        myInstance.setDisplay(theValue);
        break;
      case INITIAL:
        throw new DataFormatException("Unexpected attribute: " + theValue);
      case REFERENCE:
        myInstance.setReference(theValue);
        break;
      }
    }

    @Override
    public void endingElement() {
      switch (mySubState) {
      case INITIAL:
        pop();
        break;
      case DISPLAY:
      case REFERENCE:
        mySubState = ResourceReferenceSubState.INITIAL;
      }
    }

    @Override
    public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
      switch (mySubState) {
      case INITIAL:
        if ("display".equals(theLocalPart)) {
          mySubState = ResourceReferenceSubState.DISPLAY;
          break;
        } else if ("reference".equals(theLocalPart)) {
          mySubState = ResourceReferenceSubState.REFERENCE;
          break;
        } else if ("resource".equals(theLocalPart)) {
          mySubState = ResourceReferenceSubState.REFERENCE;
          break;
        }
        //$FALL-THROUGH$
      case DISPLAY:
      case REFERENCE:
        throw new DataFormatException("Unexpected element: " + theLocalPart);
      }
    }

    @Override
    protected IElement getCurrentElement() {
      return myInstance;
    }

  }

  private enum ResourceReferenceSubState {
    DISPLAY, INITIAL, REFERENCE
  }

  private class XhtmlState extends BaseState {
    private int myDepth;
    private XhtmlDt myDt;
    private List<XMLEvent> myEvents = new ArrayList<XMLEvent>();
    private boolean myIncludeOuterEvent;

    private XhtmlState(PreResourceState thePreResourceState, XhtmlDt theXhtmlDt, boolean theIncludeOuterEvent) throws DataFormatException {
      super(thePreResourceState);
      myDepth = 0;
      myDt = theXhtmlDt;
      myIncludeOuterEvent = theIncludeOuterEvent;
    }

    @Override
    public void attributeValue(String theName, String theValue) throws DataFormatException {
      if (myJsonMode) {
        myDt.setValueAsString(theValue);
        return;
      }
      super.attributeValue(theName, theValue);
    }

    @Override
    public void endingElement() throws DataFormatException {
      if (myJsonMode) {
        pop();
        return;
      }
      super.endingElement();
    }

    @Override
    public void xmlEvent(XMLEvent theEvent) {
      if (theEvent.isEndElement()) {
        myDepth--;
      }

      if (myIncludeOuterEvent || myDepth > 0) {
        myEvents.add(theEvent);
      }

      if (theEvent.isStartElement()) {
        myDepth++;
      }

      if (theEvent.isEndElement()) {
        if (myDepth == 0) {
          myDt.setValue(myEvents);
          pop();
        }
      }
    }

    @Override
    protected IElement getCurrentElement() {
      return myDt;
    }

  }

  public boolean isPreResource() {
    return myState.isPreResource();
  }

}
TOP

Related Classes of ca.uhn.fhir.parser.ParserState$BaseState

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.