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 (&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
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}