View Javadoc
1   package ca.uhn.fhir.model.primitive;
2   
3   /*
4    * #%L
5    * HAPI FHIR - Core Library
6    * %%
7    * Copyright (C) 2014 - 2018 University Health Network
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  import ca.uhn.fhir.model.api.BasePrimitive;
24  import ca.uhn.fhir.model.api.annotation.DatatypeDef;
25  import ca.uhn.fhir.model.api.annotation.SimpleSetter;
26  import ca.uhn.fhir.parser.DataFormatException;
27  import ca.uhn.fhir.util.XmlDetectionUtil;
28  import ca.uhn.fhir.util.XmlUtil;
29  
30  import java.util.List;
31  
32  import static org.apache.commons.lang3.StringUtils.isNotBlank;
33  
34  /**
35   * Note that as of HAPI FHIR 3.1.0, this method no longer uses
36   * the StAX XMLEvent type as the XML representation, and uses a
37   * String instead. If you need to work with XML as StAX events, you
38   * can use the {@link XmlUtil#parse(String)} and {@link XmlUtil#encode(List)}
39   * methods to do so.
40   */
41  @DatatypeDef(name = "xhtml")
42  public class XhtmlDt extends BasePrimitive<String> {
43  
44  	private static final String DECL_XMLNS = " xmlns=\"http://www.w3.org/1999/xhtml\"";
45  	public static final String DIV_OPEN_FIRST = "<div" + DECL_XMLNS + ">";
46  	private static final long serialVersionUID = 1L;
47  
48  	/**
49  	 * Constructor
50  	 */
51  	public XhtmlDt() {
52  		// nothing
53  	}
54  
55  	/**
56  	 * Constructor which accepts a string code
57  	 *
58  	 * @see #setValueAsString(String) for a description of how this value is applied
59  	 */
60  	@SimpleSetter()
61  	public XhtmlDt(@SimpleSetter.Parameter(name = "theTextDiv") String theTextDiv) {
62  		setValueAsString(theTextDiv);
63  	}
64  
65  	@Override
66  	protected String encode(String theValue) {
67  		return theValue;
68  	}
69  
70  	public boolean hasContent() {
71  		return isNotBlank(getValue());
72  	}
73  
74  	@Override
75  	public boolean isEmpty() {
76  		return super.isBaseEmpty() && (getValue() == null || getValue().isEmpty());
77  	}
78  
79  	@Override
80  	protected String parse(String theValue) {
81  		if (XmlDetectionUtil.isStaxPresent()) {
82  			// for validation
83  			XmlUtil.parse(theValue);
84  		}
85  		return theValue;
86  	}
87  
88  
89  	/**
90  	 * Note that as of HAPI FHIR 3.1.0, this method no longer uses
91  	 * the StAX XMLEvent type as the XML representation, and uses a
92  	 * String instead. If you need to work with XML as StAX events, you
93  	 * can use the {@link XmlUtil#parse(String)} and {@link XmlUtil#encode(List)}
94  	 * methods to do so.
95  	 */
96  	@Override
97  	public String getValue() {
98  		return super.getValue();
99  	}
100 
101 	/**
102 	 * Note that as of HAPI FHIR 3.1.0, this method no longer uses
103 	 * the StAX XMLEvent type as the XML representation, and uses a
104 	 * String instead. If you need to work with XML as StAX events, you
105 	 * can use the {@link XmlUtil#parse(String)} and {@link XmlUtil#encode(List)}
106 	 * methods to do so.
107 	 */
108 	@Override
109 	public BasePrimitive<String> setValue(String theValue) throws DataFormatException {
110 		return super.setValue(theValue);
111 	}
112 
113 	/**
114 	 * Accepts a textual DIV and parses it into XHTML events which are stored internally.
115 	 * <p>
116 	 * <b>Formatting note:</b> The text will be trimmed {@link String#trim()}. If the text does not start with an HTML tag (generally this would be a div tag), a div tag will be automatically placed
117 	 * surrounding the text.
118 	 * </p>
119 	 * <p>
120 	 * Also note that if the parsed text contains any entities (&amp;foo;) which are not a part of the entities defined in core XML (e.g. &amp;sect; which is valid in XHTML 1.0 but not in XML 1.0) they
121 	 * will be parsed and converted to their equivalent unicode character.
122 	 * </p>
123 	 */
124 	@Override
125 	public void setValueAsString(String theValue) throws DataFormatException {
126 		if (theValue == null || theValue.isEmpty()) {
127 			super.setValueAsString(null);
128 		} else {
129 			String value = theValue.trim();
130 			value = preprocessXhtmlNamespaceDeclaration(value);
131 
132 			super.setValueAsString(value);
133 		}
134 	}
135 
136 	public static String preprocessXhtmlNamespaceDeclaration(String value) {
137 		if (value.charAt(0) != '<') {
138 			value = DIV_OPEN_FIRST + value + "</div>";
139 		}
140 
141 		boolean hasProcessingInstruction = value.startsWith("<?");
142 		int firstTagIndex = value.indexOf("<", hasProcessingInstruction ? 1 : 0);
143 		if (firstTagIndex != -1) {
144 			int firstTagEnd = value.indexOf(">", firstTagIndex);
145 			int firstSlash = value.indexOf("/", firstTagIndex);
146 			if (firstTagEnd != -1) {
147 				if (firstSlash > firstTagEnd) {
148 					String firstTag = value.substring(firstTagIndex, firstTagEnd);
149 					if (!firstTag.contains(" xmlns")) {
150 						value = value.substring(0, firstTagEnd) + DECL_XMLNS + value.substring(firstTagEnd);
151 					}
152 				}
153 			}
154 		}
155 		return value;
156 	}
157 
158 }