554 lines
17 KiB
Java
Raw Normal View History

2014-02-18 08:26:49 -05:00
package ca.uhn.fhir.parser;
2014-02-22 15:33:02 -05:00
import java.util.ArrayList;
import java.util.List;
2014-02-21 21:06:11 -05:00
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
2014-02-18 08:26:49 -05:00
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
2014-02-19 11:59:12 -05:00
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
2014-02-18 08:26:49 -05:00
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;
2014-02-23 18:13:59 -05:00
import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition;
2014-02-19 11:59:12 -05:00
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
2014-02-22 15:33:02 -05:00
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeNarrativeDefinition;
2014-02-20 12:13:05 -05:00
import ca.uhn.fhir.context.RuntimeResourceBlockDefinition;
2014-02-18 08:26:49 -05:00
import ca.uhn.fhir.context.RuntimeResourceDefinition;
2014-02-19 17:33:46 -05:00
import ca.uhn.fhir.context.RuntimeResourceReferenceDefinition;
2014-02-22 15:33:02 -05:00
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
2014-02-19 11:59:12 -05:00
import ca.uhn.fhir.model.api.ICompositeDatatype;
import ca.uhn.fhir.model.api.ICompositeElement;
2014-02-22 15:33:02 -05:00
import ca.uhn.fhir.model.api.IElement;
2014-02-19 11:59:12 -05:00
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
2014-02-18 08:26:49 -05:00
import ca.uhn.fhir.model.api.IResource;
2014-02-20 12:13:05 -05:00
import ca.uhn.fhir.model.api.IResourceBlock;
2014-02-19 17:33:46 -05:00
import ca.uhn.fhir.model.api.ResourceReference;
2014-02-22 15:33:02 -05:00
import ca.uhn.fhir.model.api.UndeclaredExtension;
2014-02-24 11:46:08 -05:00
import ca.uhn.fhir.model.primitive.XhtmlDt;
2014-02-18 08:26:49 -05:00
class ParserState {
2014-02-23 18:13:59 -05:00
public class DeclaredExtensionState extends BaseState {
private RuntimeChildDeclaredExtensionDefinition myDefinition;
private IElement myParentInstance;
private IElement myChildInstance;
public DeclaredExtensionState(RuntimeChildDeclaredExtensionDefinition theDefinition, IElement theParentInstance) {
myDefinition = theDefinition;
myParentInstance = theParentInstance;
}
@Override
public void attributeValue(Attribute theAttribute, String theValue) throws DataFormatException {
throw new DataFormatException("'value' attribute is invalid in 'extension' element");
}
@Override
public void endingElement(EndElement theElem) throws DataFormatException {
pop();
}
@Override
public void enteringNewElementExtension(StartElement theElement, String theUrlAttr) {
RuntimeChildDeclaredExtensionDefinition declaredExtension = myDefinition.getChildExtensionForUrl(theUrlAttr);
if (declaredExtension != null) {
if (myChildInstance == null) {
myChildInstance = myDefinition.newInstance();
myDefinition.getMutator().addValue(myParentInstance, myChildInstance);
}
BaseState newState = new DeclaredExtensionState(declaredExtension, myChildInstance);
push(newState);
}else {
super.enteringNewElementExtension(theElement, theUrlAttr);
}
}
@Override
public void enteringNewElement(StartElement theElement, 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);
ContainerState newState = new ContainerState(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(newChildInstance);
push(newState);
return;
}
case RESOURCE_REF: {
RuntimeResourceReferenceDefinition resourceRefTarget = (RuntimeResourceReferenceDefinition) target;
ResourceReference newChildInstance = new ResourceReference();
myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
ResourceReferenceState newState = new ResourceReferenceState(resourceRefTarget, newChildInstance);
push(newState);
return;
}
case PRIMITIVE_XHTML:
case RESOURCE:
case RESOURCE_BLOCK:
case UNDECL_EXT:
case EXTENSION_DECLARED:
default:
break;
}
}
@Override
public void otherEvent(XMLEvent theEvent) throws DataFormatException {
// ignore
}
@Override
protected IElement getCurrentElement() {
return myParentInstance;
}
}
2014-02-21 21:06:11 -05:00
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ParserState.class);
2014-02-19 17:33:46 -05:00
2014-02-21 21:06:11 -05:00
private FhirContext myContext;
2014-02-19 17:33:46 -05:00
2014-02-21 21:06:11 -05:00
private Object myObject;
private BaseState myState;
2014-02-19 17:33:46 -05:00
2014-02-21 21:06:11 -05:00
public ParserState(FhirContext theContext) {
myContext = theContext;
}
2014-02-19 17:33:46 -05:00
2014-02-21 21:06:11 -05:00
public void attributeValue(Attribute theAttribute, String theValue) throws DataFormatException {
myState.attributeValue(theAttribute, theValue);
2014-02-19 17:33:46 -05:00
}
2014-02-21 21:06:11 -05:00
public void endingElement(EndElement theElem) throws DataFormatException {
myState.endingElement(theElem);
}
2014-02-19 11:59:12 -05:00
2014-02-21 21:06:11 -05:00
public void enteringNewElement(StartElement theElement, String theName) throws DataFormatException {
myState.enteringNewElement(theElement, theName);
}
2014-02-18 08:26:49 -05:00
2014-02-22 15:33:02 -05:00
public void enteringNewElementExtension(StartElement theElem, String theUrlAttr) {
myState.enteringNewElementExtension(theElem, theUrlAttr);
}
2014-02-21 21:06:11 -05:00
public Object getObject() {
return myObject;
2014-02-19 11:59:12 -05:00
}
2014-02-21 21:06:11 -05:00
public boolean isComplete() {
return myObject != null;
2014-02-19 11:59:12 -05:00
}
2014-02-22 15:33:02 -05:00
public void otherEvent(XMLEvent theEvent) throws DataFormatException {
myState.otherEvent(theEvent);
}
2014-02-21 21:06:11 -05:00
private void pop() {
myState = myState.myStack;
2014-02-19 11:59:12 -05:00
}
private void push(BaseState theState) {
theState.setStack(myState);
myState = theState;
}
private void setState(BaseState theState) {
myState = theState;
2014-02-18 08:26:49 -05:00
}
public static ParserState getResourceInstance(FhirContext theContext, String theLocalPart) throws DataFormatException {
2014-02-18 18:10:50 -05:00
BaseRuntimeElementDefinition<?> definition = theContext.getNameToResourceDefinition().get(theLocalPart);
2014-02-18 08:26:49 -05:00
if (!(definition instanceof RuntimeResourceDefinition)) {
throw new DataFormatException("Element '" + theLocalPart + "' is not a resource, expected a resource at this position");
}
2014-02-19 11:59:12 -05:00
2014-02-18 08:26:49 -05:00
RuntimeResourceDefinition def = (RuntimeResourceDefinition) definition;
IResource instance = def.newInstance();
2014-02-19 11:59:12 -05:00
2014-02-18 08:26:49 -05:00
ParserState retVal = new ParserState(theContext);
2014-02-19 11:59:12 -05:00
retVal.setState(retVal.new ContainerState(def, instance));
2014-02-18 08:26:49 -05:00
return retVal;
}
2014-02-20 12:13:05 -05:00
private abstract class BaseState {
private BaseState myStack;
2014-02-18 08:26:49 -05:00
2014-02-21 21:06:11 -05:00
public abstract void attributeValue(Attribute theAttribute, String theValue) throws DataFormatException;
public abstract void endingElement(EndElement theElem) throws DataFormatException;
2014-02-19 11:59:12 -05:00
2014-02-21 21:06:11 -05:00
public abstract void enteringNewElement(StartElement theElement, String theLocalPart) throws DataFormatException;
2014-02-18 08:26:49 -05:00
2014-02-23 18:13:59 -05:00
/**
* Default implementation just handles undeclared extensions
*/
2014-02-22 15:33:02 -05:00
public void enteringNewElementExtension(@SuppressWarnings("unused") StartElement theElement, String theUrlAttr) {
if (getCurrentElement() instanceof ISupportsUndeclaredExtensions) {
UndeclaredExtension newExtension = new UndeclaredExtension();
newExtension.setUrl(theUrlAttr);
2014-02-23 18:13:59 -05:00
// TODO: fail if we don't support undeclared extensions
2014-02-22 15:33:02 -05:00
((ISupportsUndeclaredExtensions) getCurrentElement()).getUndeclaredExtensions().add(newExtension);
ExtensionState newState = new ExtensionState(newExtension);
push(newState);
}
}
public abstract void otherEvent(XMLEvent theEvent) throws DataFormatException;
2014-02-20 12:13:05 -05:00
public void setStack(BaseState theState) {
myStack = theState;
}
2014-02-22 15:33:02 -05:00
protected abstract IElement getCurrentElement();
2014-02-20 12:13:05 -05:00
}
2014-02-19 11:59:12 -05:00
private class ContainerState extends BaseState {
private BaseRuntimeElementCompositeDefinition<?> myDefinition;
private ICompositeElement myInstance;
2014-02-18 08:26:49 -05:00
2014-02-19 11:59:12 -05:00
public ContainerState(BaseRuntimeElementCompositeDefinition<?> theDef, ICompositeElement theInstance) {
myDefinition = theDef;
2014-02-18 08:26:49 -05:00
myInstance = theInstance;
}
2014-02-19 11:59:12 -05:00
@Override
2014-02-21 21:06:11 -05:00
public void attributeValue(Attribute theAttribute, String theValue) {
2014-02-19 11:59:12 -05:00
ourLog.debug("Ignoring attribute value: {}", theValue);
}
2014-02-18 08:26:49 -05:00
@Override
2014-02-21 21:06:11 -05:00
public void endingElement(EndElement theElem) {
pop();
if (myState == null) {
myObject = myInstance;
}
}
2014-02-23 18:13:59 -05:00
@Override
public void enteringNewElementExtension(StartElement theElement, String theUrlAttr) {
RuntimeChildDeclaredExtensionDefinition declaredExtension = myDefinition.getDeclaredExtension(theUrlAttr);
if (declaredExtension != null) {
BaseState newState = new DeclaredExtensionState(declaredExtension, myInstance);
push(newState);
}else {
super.enteringNewElementExtension(theElement, theUrlAttr);
}
}
2014-02-21 21:06:11 -05:00
@Override
public void enteringNewElement(StartElement theElement, String theChildName) throws DataFormatException {
2014-02-19 11:59:12 -05:00
BaseRuntimeChildDefinition child = myDefinition.getChildByNameOrThrowDataFormatException(theChildName);
BaseRuntimeElementDefinition<?> target = child.getChildByName(theChildName);
2014-02-26 09:03:43 -05:00
if (target == null) {
throw new DataFormatException("Found unexpected element '" + theChildName + "' in parent element '" + myDefinition.getName() + "'. Valid names are: " + child.getValidChildNames());
}
2014-02-19 11:59:12 -05:00
switch (target.getChildType()) {
case COMPOSITE_DATATYPE: {
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
ICompositeDatatype newChildInstance = (ICompositeDatatype) compositeTarget.newInstance();
child.getMutator().addValue(myInstance, newChildInstance);
ContainerState newState = new ContainerState(compositeTarget, newChildInstance);
push(newState);
2014-02-20 12:13:05 -05:00
return;
2014-02-19 11:59:12 -05:00
}
case PRIMITIVE_DATATYPE: {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveDatatype<?> newChildInstance = primitiveTarget.newInstance();
child.getMutator().addValue(myInstance, newChildInstance);
2014-02-20 12:13:05 -05:00
PrimitiveState newState = new PrimitiveState(newChildInstance);
2014-02-19 11:59:12 -05:00
push(newState);
2014-02-20 12:13:05 -05:00
return;
2014-02-19 11:59:12 -05:00
}
2014-02-19 17:33:46 -05:00
case RESOURCE_REF: {
RuntimeResourceReferenceDefinition resourceRefTarget = (RuntimeResourceReferenceDefinition) target;
ResourceReference newChildInstance = new ResourceReference();
child.getMutator().addValue(myInstance, newChildInstance);
ResourceReferenceState newState = new ResourceReferenceState(resourceRefTarget, newChildInstance);
push(newState);
2014-02-20 12:13:05 -05:00
return;
}
case RESOURCE_BLOCK: {
RuntimeResourceBlockDefinition blockTarget = (RuntimeResourceBlockDefinition) target;
IResourceBlock newBlockInstance = blockTarget.newInstance();
child.getMutator().addValue(myInstance, newBlockInstance);
ContainerState newState = new ContainerState(blockTarget, newBlockInstance);
push(newState);
return;
2014-02-19 11:59:12 -05:00
}
2014-02-22 15:33:02 -05:00
case PRIMITIVE_XHTML: {
RuntimePrimitiveDatatypeNarrativeDefinition xhtmlTarget = (RuntimePrimitiveDatatypeNarrativeDefinition) target;
XhtmlDt newDt = xhtmlTarget.newInstance();
child.getMutator().addValue(myInstance, newDt);
XhtmlState state = new XhtmlState(newDt, theElement);
push(state);
return;
}
case UNDECL_EXT:
2014-02-21 21:06:11 -05:00
case RESOURCE: {
2014-02-20 12:13:05 -05:00
// Throw an exception because this shouldn't happen here
break;
2014-02-19 11:59:12 -05:00
}
2014-02-22 15:33:02 -05:00
}
throw new DataFormatException("Illegal resource position: " + target.getChildType());
}
@Override
public void otherEvent(XMLEvent theEvent) {
// ignore
}
2014-02-19 11:59:12 -05:00
2014-02-22 15:33:02 -05:00
@Override
protected IElement getCurrentElement() {
return myInstance;
}
}
private class ExtensionState extends BaseState {
private UndeclaredExtension myExtension;
public ExtensionState(UndeclaredExtension theExtension) {
myExtension = theExtension;
}
@Override
public void attributeValue(Attribute theAttribute, String theValue) throws DataFormatException {
throw new DataFormatException("'value' attribute is invalid in 'extension' element");
}
@Override
public void endingElement(EndElement theElem) throws DataFormatException {
if (myExtension.getValue() != null && myExtension.getUndeclaredExtensions().size() > 0) {
throw new DataFormatException("Extension must not have both a value and other contained extensions");
2014-02-18 18:10:50 -05:00
}
2014-02-22 15:33:02 -05:00
pop();
}
@Override
public void enteringNewElement(StartElement theElement, String theLocalPart) throws DataFormatException {
BaseRuntimeElementDefinition<?> target = myContext.getRuntimeChildUndeclaredExtensionDefinition().getChildByName(theLocalPart);
if (target == null) {
throw new DataFormatException("Unknown extension element name: " + theLocalPart);
2014-02-21 21:06:11 -05:00
}
2014-02-22 15:33:02 -05:00
switch (target.getChildType()) {
case COMPOSITE_DATATYPE: {
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
ICompositeDatatype newChildInstance = (ICompositeDatatype) compositeTarget.newInstance();
myExtension.setValue(newChildInstance);
ContainerState newState = new ContainerState(compositeTarget, newChildInstance);
push(newState);
return;
}
case PRIMITIVE_DATATYPE: {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveDatatype<?> newChildInstance = primitiveTarget.newInstance();
myExtension.setValue(newChildInstance);
PrimitiveState newState = new PrimitiveState(newChildInstance);
push(newState);
return;
}
case RESOURCE_REF: {
RuntimeResourceReferenceDefinition resourceRefTarget = (RuntimeResourceReferenceDefinition) target;
ResourceReference newChildInstance = new ResourceReference();
myExtension.setValue(newChildInstance);
ResourceReferenceState newState = new ResourceReferenceState(resourceRefTarget, newChildInstance);
push(newState);
return;
}
case PRIMITIVE_XHTML:
case RESOURCE:
case RESOURCE_BLOCK:
case UNDECL_EXT:
break;
}
}
@Override
public void otherEvent(XMLEvent theEvent) throws DataFormatException {
// ignore
}
@Override
protected IElement getCurrentElement() {
return myExtension;
2014-02-18 08:26:49 -05:00
}
2014-02-19 11:59:12 -05:00
2014-02-18 08:26:49 -05:00
}
2014-02-19 11:59:12 -05:00
private class PrimitiveState extends BaseState {
private IPrimitiveDatatype<?> myInstance;
2014-02-20 12:13:05 -05:00
public PrimitiveState(IPrimitiveDatatype<?> theInstance) {
2014-02-19 11:59:12 -05:00
super();
myInstance = theInstance;
}
@Override
2014-02-21 21:06:11 -05:00
public void attributeValue(Attribute theAttribute, String theValue) throws DataFormatException {
2014-02-19 11:59:12 -05:00
myInstance.setValueAsString(theValue);
}
@Override
2014-02-21 21:06:11 -05:00
public void endingElement(EndElement theElem) {
pop();
2014-02-19 11:59:12 -05:00
}
@Override
2014-02-21 21:06:11 -05:00
public void enteringNewElement(StartElement theElement, String theLocalPart) throws DataFormatException {
throw new Error("?? can this happen?"); // TODO: can this happen?
2014-02-19 11:59:12 -05:00
}
2014-02-22 15:33:02 -05:00
@Override
public void otherEvent(XMLEvent theEvent) {
// ignore
}
@Override
protected IElement getCurrentElement() {
return myInstance;
}
2014-02-19 11:59:12 -05:00
}
2014-02-21 21:06:11 -05:00
private class ResourceReferenceState extends BaseState {
private RuntimeResourceReferenceDefinition myDefinition;
private ResourceReference myInstance;
private ResourceReferenceSubState mySubState;
public ResourceReferenceState(RuntimeResourceReferenceDefinition theDefinition, ResourceReference theInstance) {
myDefinition = theDefinition;
myInstance = theInstance;
mySubState = ResourceReferenceSubState.INITIAL;
}
@Override
public void attributeValue(Attribute theAttribute, String theValue) throws DataFormatException {
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(EndElement theElement) {
switch (mySubState) {
case INITIAL:
pop();
break;
case DISPLAY:
case REFERENCE:
mySubState = ResourceReferenceSubState.INITIAL;
}
}
@Override
public void enteringNewElement(StartElement theElem, 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;
}
//$FALL-THROUGH$
case DISPLAY:
case REFERENCE:
throw new DataFormatException("Unexpected element: " + theLocalPart);
}
}
2014-02-22 15:33:02 -05:00
@Override
public void otherEvent(XMLEvent theEvent) {
// ignore
}
@Override
protected IElement getCurrentElement() {
return myInstance;
}
2014-02-18 08:26:49 -05:00
}
2014-02-19 11:59:12 -05:00
2014-02-21 21:06:11 -05:00
private enum ResourceReferenceSubState {
DISPLAY, INITIAL, REFERENCE
2014-02-19 11:59:12 -05:00
}
2014-02-21 21:06:11 -05:00
private class XhtmlState extends BaseState {
2014-02-22 15:33:02 -05:00
private int myDepth;
private XhtmlDt myDt;
private List<XMLEvent> myEvents = new ArrayList<XMLEvent>();
private XhtmlState(XhtmlDt theXhtmlDt, StartElement theXhtmlStartElement) throws DataFormatException {
myDepth = 1;
myDt = theXhtmlDt;
myEvents.add(theXhtmlStartElement);
2014-02-21 21:06:11 -05:00
}
@Override
public void attributeValue(Attribute theAttr, String theValue) throws DataFormatException {
2014-02-22 15:33:02 -05:00
myEvents.add(theAttr);
2014-02-21 21:06:11 -05:00
}
@Override
public void endingElement(EndElement theElement) throws DataFormatException {
2014-02-22 15:33:02 -05:00
myEvents.add(theElement);
myDepth--;
if (myDepth == 0) {
myDt.setValue(myEvents);
pop();
2014-02-21 21:06:11 -05:00
}
}
@Override
public void enteringNewElement(StartElement theElem, String theLocalPart) throws DataFormatException {
2014-02-22 15:33:02 -05:00
myDepth++;
myEvents.add(theElem);
}
2014-02-21 21:06:11 -05:00
2014-02-22 15:33:02 -05:00
@Override
public void otherEvent(XMLEvent theEvent) throws DataFormatException {
myEvents.add(theEvent);
2014-02-21 21:06:11 -05:00
}
2014-02-19 11:59:12 -05:00
2014-02-22 15:33:02 -05:00
@Override
protected IElement getCurrentElement() {
return myDt;
}
2014-02-19 11:59:12 -05:00
}
2014-02-18 08:26:49 -05:00
}