Merge remote-tracking branch 'origin/master'

This commit is contained in:
Yogthos 2014-02-27 12:07:54 -05:00
commit 115fccc622
5 changed files with 238 additions and 149 deletions

View File

@ -9,7 +9,7 @@ import ca.uhn.fhir.model.primitive.StringDt;
public class Bundle implements IElement {
private StringDt myAuthorDevice;
private StringDt myAuthorUri;
private StringDt myAuthorName;
private List<BundleEntry> myEntries;
private StringDt myId;
@ -24,11 +24,11 @@ public class Bundle implements IElement {
private IntegerDt myTotalResults;
private InstantDt myUpdated;
public StringDt getAuthorDevice() {
if (myAuthorDevice == null) {
myAuthorDevice = new StringDt();
public StringDt getAuthorUri() {
if (myAuthorUri == null) {
myAuthorUri = new StringDt();
}
return myAuthorDevice;
return myAuthorUri;
}
public StringDt getAuthorName() {

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.model.api;
import org.apache.commons.lang3.StringUtils;
public class ResourceReference implements IElement {
@ -22,4 +24,8 @@ public class ResourceReference implements IElement {
myReference = theReference;
}
public boolean hasContent() {
return StringUtils.isNotBlank(myDisplay) || StringUtils.isNotBlank(myReference);
}
}

View File

@ -83,4 +83,8 @@ public class XhtmlDt implements IPrimitiveDatatype<List<XMLEvent>> {
myValue = theValue;
}
public boolean hasContent() {
return myValue != null && myValue.size() > 0;
}
}

View File

@ -3,11 +3,16 @@ package ca.uhn.fhir.parser;
import java.util.ArrayList;
import java.util.List;
import javax.xml.namespace.QName;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import org.apache.commons.lang3.StringUtils;
import com.ctc.wstx.sw.BaseStreamWriter;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
@ -33,6 +38,32 @@ import ca.uhn.fhir.model.primitive.XhtmlDt;
class ParserState<T extends IElement> {
public class AtomAuthorState extends BaseState {
private Bundle myInstance;
public AtomAuthorState(Bundle theInstance) {
myInstance = theInstance;
}
@Override
public void endingElement(EndElement theElem) 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);
}
}
}
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ParserState.class);
private FhirContext myContext;
@ -51,8 +82,8 @@ class ParserState<T extends IElement> {
myState.endingElement(theElem);
}
public void enteringNewElement(StartElement theElement, String theName) throws DataFormatException {
myState.enteringNewElement(theElement, theName);
public void enteringNewElement(String theNamespaceURI, String theName) throws DataFormatException {
myState.enteringNewElement(theNamespaceURI, theName);
}
public void enteringNewElementExtension(StartElement theElem, String theUrlAttr) {
@ -67,10 +98,6 @@ class ParserState<T extends IElement> {
return myObject != null;
}
public void otherEvent(XMLEvent theEvent) throws DataFormatException {
myState.otherEvent(theEvent);
}
private void pop() {
myState = myState.myStack;
myState.wereBack();
@ -93,7 +120,7 @@ class ParserState<T extends IElement> {
return retVal;
}
private class AtomPrimitiveState extends BaseState{
private class AtomPrimitiveState extends BaseState {
private IPrimitiveDatatype<?> myPrimitive;
private String myData;
@ -102,11 +129,6 @@ class ParserState<T extends IElement> {
myPrimitive = thePrimitive;
}
@Override
public void attributeValue(Attribute theAttribute, String theValue) throws DataFormatException {
// ignore
}
@Override
public void endingElement(EndElement theElem) throws DataFormatException {
myPrimitive.setValueAsString(myData);
@ -114,7 +136,7 @@ class ParserState<T extends IElement> {
}
@Override
public void enteringNewElement(StartElement theElement, String theLocalPart) throws DataFormatException {
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
throw new DataFormatException("Unexpected nested element in atom tag ");
}
@ -124,20 +146,66 @@ class ParserState<T extends IElement> {
}
@Override
public void otherEvent(XMLEvent theEvent) throws DataFormatException {
if (theEvent.isCharacters()) {
String data = theEvent.asCharacters().getData();
if (myData == null) {
myData = data;
}else {
// this shouldn't generally happen so it's ok that it's inefficient
myData = myData + data;
}
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;
}
}
}
private class AtomLinkState extends BaseState {
private String myRel;
private String myHref;
private Bundle myInstance;
public AtomLinkState(Bundle theInstance) {
myInstance = theInstance;
}
@Override
public void attributeValue(Attribute theAttribute, String theValue) throws DataFormatException {
String name = theAttribute.getName().getLocalPart();
if ("rel".equals(name)) {
myRel = theValue;
} else if ("href".equals(name)) {
myHref = theValue;
}
}
@Override
public void endingElement(EndElement theElem) throws DataFormatException {
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);
}
pop();
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
throw new DataFormatException("Found unexpected element content");
}
}
private static final QName ATOM_LINK_REL_ATTRIBUTE = new QName("rel");
private static final QName ATOM_LINK_HREF_ATTRIBUTE = new QName("href");
private class AtomState extends BaseState {
private Bundle myInstance;
@ -149,7 +217,7 @@ class ParserState<T extends IElement> {
@Override
public void attributeValue(Attribute theAttribute, String theValue) throws DataFormatException {
// TODO Auto-generated method stub
}
@Override
@ -158,30 +226,33 @@ class ParserState<T extends IElement> {
}
@Override
public void enteringNewElement(StartElement theElement, String theLocalPart) throws DataFormatException {
if (theLocalPart.equals("title")) {
push(new AtomPrimitiveState(myInstance.getTitle()));
}else if ("id".equals(theLocalPart)) {
push(new AtomPrimitiveState(myInstance.getId()));
}else if ("link".equals(theLocalPart)) {
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if ("entry".equals(theLocalPart) && verifyNamespace(XmlParser.ATOM_NS, theNamespaceURI)) {
} else if (theLocalPart.equals("title")) {
push(new AtomPrimitiveState(myInstance.getTitle()));
} else if ("id".equals(theLocalPart)) {
push(new AtomPrimitiveState(myInstance.getId()));
} 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));
}
// TODO: handle category and DSig
}
@Override
protected IElement getCurrentElement() {
// TODO Auto-generated method stub
return null;
return myInstance;
}
@Override
public void otherEvent(XMLEvent theEvent) throws DataFormatException {
// TODO Auto-generated method stub
}
}
private class PreAtomState extends BaseState {
private Bundle myInstance;
@ -197,14 +268,14 @@ class ParserState<T extends IElement> {
}
@Override
public void enteringNewElement(StartElement theElement, String theLocalPart) throws DataFormatException {
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if (!"feed".equals(theLocalPart)) {
throw new DataFormatException("Expecting outer element called 'feed', found: "+theLocalPart);
throw new DataFormatException("Expecting outer element called 'feed', found: " + theLocalPart);
}
myInstance = new Bundle();
push(new AtomState(myInstance));
}
@Override
@ -219,22 +290,26 @@ class ParserState<T extends IElement> {
myObject = (T) myInstance;
}
@Override
public void otherEvent(XMLEvent theEvent) throws DataFormatException {
// ignore
}
}
private abstract class BaseState {
private BaseState myStack;
public abstract void attributeValue(Attribute theAttribute, String theValue) throws DataFormatException;
@SuppressWarnings("unused")
public void attributeValue(Attribute theAttribute, String theValue) throws DataFormatException {
// ignore by default
}
public abstract void endingElement(EndElement theElem) throws DataFormatException;
@SuppressWarnings("unused")
public void endingElement(EndElement theElem) throws DataFormatException {
// ignore by default
}
public abstract void enteringNewElement(StartElement theElement, String theLocalPart) throws DataFormatException;
@SuppressWarnings("unused")
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
// ignore by default
}
/**
* Default implementation just handles undeclared extensions
@ -250,9 +325,9 @@ class ParserState<T extends IElement> {
}
}
protected abstract IElement getCurrentElement();
public abstract void otherEvent(XMLEvent theEvent) throws DataFormatException;
protected IElement getCurrentElement() {
return null;
}
public void setStack(BaseState theState) {
myStack = theState;
@ -262,6 +337,14 @@ class ParserState<T extends IElement> {
// allow an implementor to override
}
public void string(@SuppressWarnings("unused") String theData) {
// ignore by default
}
public void xmlEvent(@SuppressWarnings("unused") XMLEvent theNextEvent) {
// ignore
}
}
private class DeclaredExtensionState extends BaseState {
@ -286,7 +369,7 @@ class ParserState<T extends IElement> {
}
@Override
public void enteringNewElement(StartElement theElement, String theLocalPart) throws DataFormatException {
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);
@ -347,11 +430,6 @@ class ParserState<T extends IElement> {
return myParentInstance;
}
@Override
public void otherEvent(XMLEvent theEvent) throws DataFormatException {
// ignore
}
}
private class ElementCompositeState extends BaseState {
@ -379,7 +457,7 @@ class ParserState<T extends IElement> {
}
@Override
public void enteringNewElement(StartElement theElement, String theChildName) throws DataFormatException {
public void enteringNewElement(String theNamespace, String theChildName) throws DataFormatException {
BaseRuntimeChildDefinition child = myDefinition.getChildByNameOrThrowDataFormatException(theChildName);
BaseRuntimeElementDefinition<?> target = child.getChildByName(theChildName);
if (target == null) {
@ -423,7 +501,7 @@ class ParserState<T extends IElement> {
RuntimePrimitiveDatatypeNarrativeDefinition xhtmlTarget = (RuntimePrimitiveDatatypeNarrativeDefinition) target;
XhtmlDt newDt = xhtmlTarget.newInstance();
child.getMutator().addValue(myInstance, newDt);
XhtmlState state = new XhtmlState(newDt, theElement);
XhtmlState state = new XhtmlState(newDt);
push(state);
return;
}
@ -453,11 +531,6 @@ class ParserState<T extends IElement> {
return myInstance;
}
@Override
public void otherEvent(XMLEvent theEvent) {
// ignore
}
}
private class ExtensionState extends BaseState {
@ -482,7 +555,7 @@ class ParserState<T extends IElement> {
}
@Override
public void enteringNewElement(StartElement theElement, String theLocalPart) throws DataFormatException {
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);
@ -526,11 +599,6 @@ class ParserState<T extends IElement> {
return myExtension;
}
@Override
public void otherEvent(XMLEvent theEvent) throws DataFormatException {
// ignore
}
}
private class PreResourceState extends BaseState {
@ -548,7 +616,7 @@ class ParserState<T extends IElement> {
}
@Override
public void enteringNewElement(StartElement theElement, String theLocalPart) throws DataFormatException {
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
BaseRuntimeElementDefinition<?> definition = myContext.getNameToResourceDefinition().get(theLocalPart);
if (!(definition instanceof RuntimeResourceDefinition)) {
throw new DataFormatException("Element '" + theLocalPart + "' is not a resource, expected a resource at this position");
@ -565,11 +633,6 @@ class ParserState<T extends IElement> {
return myInstance;
}
@Override
public void otherEvent(XMLEvent theEvent) throws DataFormatException {
// ignore
}
@SuppressWarnings("unchecked")
@Override
public void wereBack() {
@ -597,7 +660,7 @@ class ParserState<T extends IElement> {
}
@Override
public void enteringNewElement(StartElement theElement, String theLocalPart) throws DataFormatException {
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
throw new Error("?? can this happen?"); // TODO: can this happen?
}
@ -606,11 +669,6 @@ class ParserState<T extends IElement> {
return myInstance;
}
@Override
public void otherEvent(XMLEvent theEvent) {
// ignore
}
}
private class ResourceReferenceState extends BaseState {
@ -652,7 +710,7 @@ class ParserState<T extends IElement> {
}
@Override
public void enteringNewElement(StartElement theElem, String theLocalPart) throws DataFormatException {
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
switch (mySubState) {
case INITIAL:
if ("display".equals(theLocalPart)) {
@ -674,11 +732,6 @@ class ParserState<T extends IElement> {
return myInstance;
}
@Override
public void otherEvent(XMLEvent theEvent) {
// ignore
}
}
private enum ResourceReferenceSubState {
@ -690,32 +743,9 @@ class ParserState<T extends IElement> {
private XhtmlDt myDt;
private List<XMLEvent> myEvents = new ArrayList<XMLEvent>();
private XhtmlState(XhtmlDt theXhtmlDt, StartElement theXhtmlStartElement) throws DataFormatException {
private XhtmlState(XhtmlDt theXhtmlDt) throws DataFormatException {
myDepth = 1;
myDt = theXhtmlDt;
myEvents.add(theXhtmlStartElement);
}
@Override
public void attributeValue(Attribute theAttr, String theValue) throws DataFormatException {
myEvents.add(theAttr);
}
@Override
public void endingElement(EndElement theElement) throws DataFormatException {
myEvents.add(theElement);
myDepth--;
if (myDepth == 0) {
myDt.setValue(myEvents);
pop();
}
}
@Override
public void enteringNewElement(StartElement theElem, String theLocalPart) throws DataFormatException {
myDepth++;
myEvents.add(theElem);
}
@Override
@ -724,9 +754,36 @@ class ParserState<T extends IElement> {
}
@Override
public void otherEvent(XMLEvent theEvent) throws DataFormatException {
public void xmlEvent(XMLEvent theEvent) {
myEvents.add(theEvent);
if (theEvent.isStartElement()) {
myDepth++;
}
if (theEvent.isEndElement()) {
myDepth--;
if (myDepth == 0) {
myDt.setValue(myEvents);
pop();
}
}
}
}
public void string(String theData) {
myState.string(theData);
}
public boolean verifyNamespace(String theExpect, String theActual) {
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);
}
}

View File

@ -49,12 +49,12 @@ import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.util.PrettyPrintWriterWrapper;
public class XmlParser {
private static final String ATOM_NS = "http://www.w3.org/2005/Atom";
private static final String FHIR_NS = "http://hl7.org/fhir";
private static final String OPENSEARCH_NS = "http://a9.com/-/spec/opensearch/1.1/";
static final String ATOM_NS = "http://www.w3.org/2005/Atom";
static final String FHIR_NS = "http://hl7.org/fhir";
static final String OPENSEARCH_NS = "http://a9.com/-/spec/opensearch/1.1/";
@SuppressWarnings("unused")
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParser.class);
private static final String XHTML_NS = "http://www.w3.org/1999/xhtml";
static final String XHTML_NS = "http://www.w3.org/1999/xhtml";
// private static final Set<String> RESOURCE_NAMESPACES;
@ -106,7 +106,7 @@ public class XmlParser {
if (StringUtils.isNotBlank(theBundle.getAuthorName().getValue())) {
eventWriter.writeStartElement("author");
writeTagWithTextNode(eventWriter, "name", theBundle.getAuthorName());
writeOptionalTagWithTextNode(eventWriter, "device", theBundle.getAuthorDevice());
writeOptionalTagWithTextNode(eventWriter, "uri", theBundle.getAuthorUri());
}
for (BundleEntry nextEntry : theBundle.getEntries()) {
@ -131,15 +131,21 @@ public class XmlParser {
return stringWriter.toString();
}
private void encodeChildElementToStreamWriter(XMLStreamWriter theEventWriter, IElement nextValue, String childName, BaseRuntimeElementDefinition<?> childDef, String theExtensionUrl) throws XMLStreamException, DataFormatException {
private boolean encodeChildElementToStreamWriter(XMLStreamWriter theEventWriter, IElement nextValue, String childName, BaseRuntimeElementDefinition<?> childDef, String theExtensionUrl)
throws XMLStreamException, DataFormatException {
switch (childDef.getChildType()) {
case PRIMITIVE_DATATYPE: {
theEventWriter.writeStartElement(childName);
IPrimitiveDatatype<?> pd = (IPrimitiveDatatype<?>) nextValue;
theEventWriter.writeAttribute("value", pd.getValueAsString());
encodeExtensionsIfPresent(theEventWriter, nextValue);
theEventWriter.writeEndElement();
break;
String value = pd.getValueAsString();
if (value != null) {
theEventWriter.writeStartElement(childName);
theEventWriter.writeAttribute("value", value);
encodeExtensionsIfPresent(theEventWriter, nextValue);
theEventWriter.writeEndElement();
return true;
} else {
return false;
}
}
case RESOURCE_BLOCK:
case COMPOSITE_DATATYPE: {
@ -151,30 +157,41 @@ public class XmlParser {
encodeCompositeElementToStreamWriter(nextValue, theEventWriter, childCompositeDef);
encodeExtensionsIfPresent(theEventWriter, nextValue);
theEventWriter.writeEndElement();
break;
return true;
}
case RESOURCE_REF: {
theEventWriter.writeStartElement(childName);
ResourceReference ref = (ResourceReference) nextValue;
encodeResourceReferenceToStreamWriter(theEventWriter, ref);
theEventWriter.writeEndElement();
break;
if (ref.hasContent()) {
theEventWriter.writeStartElement(childName);
encodeResourceReferenceToStreamWriter(theEventWriter, ref);
theEventWriter.writeEndElement();
return true;
} else {
return false;
}
}
case RESOURCE: {
throw new IllegalStateException(); // should not happen
}
case PRIMITIVE_XHTML: {
XhtmlDt dt = (XhtmlDt) nextValue;
encodeXhtml(dt, theEventWriter);
break;
if (dt.hasContent()) {
encodeXhtml(dt, theEventWriter);
return true;
} else {
return false;
}
}
case UNDECL_EXT: {
throw new IllegalStateException("should not happen");
}
}
return false;
}
private void encodeCompositeElementChildrenToStreamWriter(IElement theElement, XMLStreamWriter theEventWriter, List<? extends BaseRuntimeChildDefinition> children) throws XMLStreamException, DataFormatException {
private void encodeCompositeElementChildrenToStreamWriter(IElement theElement, XMLStreamWriter theEventWriter, List<? extends BaseRuntimeChildDefinition> children) throws XMLStreamException,
DataFormatException {
for (BaseRuntimeChildDefinition nextChild : children) {
List<? extends IElement> values = nextChild.getAccessor().getValues(theElement);
if (values == null || values.isEmpty()) {
@ -205,15 +222,18 @@ public class XmlParser {
}
}
private void encodeCompositeElementToStreamWriter(IElement theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef) throws XMLStreamException, DataFormatException {
private void encodeCompositeElementToStreamWriter(IElement theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef) throws XMLStreamException,
DataFormatException {
encodeExtensionsIfPresent(theEventWriter, theElement);
encodeCompositeElementChildrenToStreamWriter(theElement, theEventWriter, resDef.getExtensions());
encodeCompositeElementChildrenToStreamWriter(theElement, theEventWriter, resDef.getChildren());
}
private void encodeExtensionsIfPresent(XMLStreamWriter theWriter, IElement theResource) throws XMLStreamException, DataFormatException {
boolean retVal = false;
if (theResource instanceof ISupportsUndeclaredExtensions) {
for (UndeclaredExtension next : ((ISupportsUndeclaredExtensions) theResource).getUndeclaredExtensions()) {
retVal =true;
theWriter.writeStartElement("extension");
theWriter.writeAttribute("url", next.getUrl());
@ -388,9 +408,6 @@ public class XmlParser {
StartElement elem = nextEvent.asStartElement();
String namespaceURI = elem.getName().getNamespaceURI();
if (!FHIR_NS.equals(namespaceURI) && !XHTML_NS.equals(namespaceURI)) {
continue;
}
if ("extension".equals(elem.getName().getLocalPart())) {
Attribute urlAttr = elem.getAttributeByName(new QName("url"));
@ -399,7 +416,10 @@ public class XmlParser {
}
parserState.enteringNewElementExtension(elem, urlAttr.getValue());
} else {
parserState.enteringNewElement(elem, elem.getName().getLocalPart());
String elementName = elem.getName().getLocalPart();
parserState.enteringNewElement(namespaceURI, elementName);
}
for (@SuppressWarnings("unchecked")
@ -430,10 +450,12 @@ public class XmlParser {
if (parserState.isComplete()) {
return parserState.getObject();
}
} else {
parserState.otherEvent(nextEvent);
} else if (nextEvent.isCharacters()) {
parserState.string(nextEvent.asCharacters().getData());
}
parserState.xmlEvent(nextEvent);
}
return null;