001package ca.uhn.fhir.model.primitive; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2016 University Health Network 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import java.io.StringReader; 024import java.io.StringWriter; 025import java.util.ArrayList; 026import java.util.List; 027 028import javax.xml.stream.FactoryConfigurationError; 029import javax.xml.stream.XMLEventReader; 030import javax.xml.stream.XMLEventWriter; 031import javax.xml.stream.XMLStreamException; 032import javax.xml.stream.events.XMLEvent; 033 034import ca.uhn.fhir.context.ConfigurationException; 035import ca.uhn.fhir.model.api.BasePrimitive; 036import ca.uhn.fhir.model.api.annotation.DatatypeDef; 037import ca.uhn.fhir.model.api.annotation.SimpleSetter; 038import ca.uhn.fhir.parser.DataFormatException; 039import ca.uhn.fhir.util.XmlUtil; 040 041@DatatypeDef(name = "xhtml") 042public class XhtmlDt extends BasePrimitive<List<XMLEvent>> { 043 044 private static final long serialVersionUID = 1L; 045 046 /** 047 * Constructor 048 */ 049 public XhtmlDt() { 050 // nothing 051 } 052 053 /** 054 * Constructor which accepts a string code 055 * 056 * @see #setValueAsString(String) for a description of how this value is applied 057 */ 058 @SimpleSetter() 059 public XhtmlDt(@SimpleSetter.Parameter(name = "theTextDiv") String theTextDiv) { 060 setValueAsString(theTextDiv); 061 } 062 063 @Override 064 protected String encode(List<XMLEvent> theValue) { 065 try { 066 StringWriter w = new StringWriter(); 067 XMLEventWriter ew = XmlUtil.createXmlFragmentWriter(w); 068 069 for (XMLEvent next : getValue()) { 070 if (next.isCharacters()) { 071 ew.add(next); 072 } else { 073 ew.add(next); 074 } 075 } 076 ew.close(); 077 return w.toString(); 078 } catch (XMLStreamException e) { 079 throw new DataFormatException("Problem with the contained XML events", e); 080 } catch (FactoryConfigurationError e) { 081 throw new ConfigurationException(e); 082 } 083 } 084 085 public boolean hasContent() { 086 return getValue() != null && getValue().size() > 0; 087 } 088 089 @Override 090 public boolean isEmpty() { 091 return super.isBaseEmpty() && (getValue() == null || getValue().isEmpty()); 092 } 093 094 @Override 095 protected List<XMLEvent> parse(String theValue) { 096 String val = theValue.trim(); 097 if (!val.startsWith("<")) { 098 val = "<div>" + val + "</div>"; 099 } 100 if (val.startsWith("<?") && val.endsWith("?>")) { 101 return null; 102 } 103 104 try { 105 ArrayList<XMLEvent> value = new ArrayList<XMLEvent>(); 106 StringReader reader = new StringReader(val); 107 XMLEventReader er = XmlUtil.createXmlReader(reader); 108 boolean first = true; 109 while (er.hasNext()) { 110 XMLEvent next = er.nextEvent(); 111 if (first) { 112 first = false; 113 continue; 114 } 115 if (er.hasNext()) { 116 // don't add the last event 117 value.add(next); 118 } 119 } 120 return value; 121 122 } catch (XMLStreamException e) { 123 throw new DataFormatException("String does not appear to be valid XML/XHTML (error is \"" + e.getMessage() + "\"): " + theValue, e); 124 } catch (FactoryConfigurationError e) { 125 throw new ConfigurationException(e); 126 } 127 } 128 129 /** 130 * Accepts a textual DIV and parses it into XHTML events which are stored internally. 131 * <p> 132 * <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 133 * surrounding the text. 134 * </p> 135 * <p> 136 * Also note that if the parsed text contains any entities (&foo;) which are not a part of the entities defined in core XML (e.g. &sect; which is valid in XHTML 1.0 but not in XML 1.0) they 137 * will be parsed and converted to their equivalent unicode character. 138 * </p> 139 */ 140 @Override 141 public void setValueAsString(String theValue) throws DataFormatException { 142 if (theValue == null || theValue.isEmpty()) { 143 super.setValueAsString(null); 144 } else { 145 String value = theValue.trim(); 146 if (value.charAt(0) != '<') { 147 value = "<div>" + value + "</div>"; 148 } 149 super.setValueAsString(value); 150 } 151 } 152 153}