1 /*
2 * The MIT License
3 *
4 * Original work sponsored and donated by National Board of e-Health (NSI), Denmark (http://www.nsi.dk)
5 *
6 * Copyright (C) 2011 National Board of e-Health (NSI), Denmark (http://www.nsi.dk)
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy of
9 * this software and associated documentation files (the "Software"), to deal in
10 * the Software without restriction, including without limitation the rights to
11 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 * of the Software, and to permit persons to whom the Software is furnished to do
13 * so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in all
16 * copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 *
26 * $HeadURL$
27 * $Id$
28 */
29
30 package dk.sosi.seal.model;
31
32 import dk.sosi.seal.model.constants.Tag;
33 import dk.sosi.seal.xml.XmlUtil;
34 import org.w3c.dom.*;
35
36 import java.text.ParseException;
37 import java.util.*;
38
39 /**
40 * Abstract class used by classes directly modifying an underlying DOM model object.
41 *
42 * @author ads
43 */
44 public class AbstractDOMInfoExtractor {
45
46 /** The underlying DOM representation */
47 protected Element dom;
48 private Map<String, String> namespaces;
49
50 /**
51 * Converts the attribute of the supplied <code>Element</code> to a <code>Date</code> using <code>XMLUtil.getDateFormat(true)...</code>.
52 *
53 * @param element
54 * The <code>Element</code> to retrieve the attribute from.
55 * @param attribute
56 * The name of the attribute to convert.
57 * @return The converted <code>Date</code> object.
58 */
59 protected Date convertToDate(Element element, String attribute) {
60 String value;
61 if(attribute != null) {
62 value = element.getAttribute(attribute);
63 } else {
64 value = element.getTextContent();
65 }
66
67 try {
68 return XmlUtil.getDateFormat(true).parse(value);
69 } catch (ParseException e) {
70 throw new ModelException("Invalid date format of " + (attribute != null ? attribute : "text contents") + " (" + value + ")", e);
71 }
72 }
73
74 /**
75 * Filter the supplied <code>NodeList</code> and retrieve the <code>Element</code> havning the supplied attribute matching the supplied value.<br />
76 *
77 * @param nl
78 * The <code>NodeList</code> to filter.
79 * @param attribute
80 * The attribute to test.
81 * @param value
82 * The value to filter for.
83 * @return The matching <code>Element</code> or <code>null</code> if no mathces was found.
84 */
85 protected Element getFilteredElement(List<Element> nl, String attribute, String value) {
86 for (int i = 0; i < nl.size(); i++) {
87 Element tmp = nl.get(i);
88
89 String val = tmp.getAttribute(attribute);
90 if(val != null) {
91 if(val.equals(value)) {
92 return tmp;
93 }
94 }
95 }
96 return null;
97 }
98
99 /**
100 * Retrieve the <code>Element</code> identified by the supplied tag path structure.<br />
101 * This method will traverse the DOM retrieve the first child for each specified tag.
102 *
103 * @param tags
104 * List of tag names to traverse.
105 * @return The <code>Element</code> matching the final tag in the list.
106 */
107 protected Element getTag(Tag... tags) {
108 if(tags.length == 1) {
109 return dom;
110 }
111 return getFirstElement(getTags(tags));
112 }
113
114 /**
115 * Retrieve the <code>NodeList</code> identified by the supplied tag path structure.<br />
116 * This method will traverse the DOM retrieve the first child for each specified tag.
117 *
118 * @param tags
119 * List of tag names to traverse.
120 * @return The <code>NodeList</code> matching the final tag in the list.
121 */
122 protected List<Element> getTags(Tag... tags) {
123 Element elm = dom;
124
125 for (int i = 1; i < tags.length - 1; i++) {
126 Tag tag = tags[i];
127 elm = getFirstElement(getElements(elm, tag, true));
128 }
129
130 Tag tag = tags[tags.length - 1];
131 return getElements(elm, tag, false);
132 }
133
134 private List<Element> getElements(Element elm, Tag tag, boolean onlyFirst) {
135 List<Element> res = new ArrayList<Element>();
136
137 if (elm == null) {
138 return res;
139 }
140
141 NodeList childNodes = elm.getChildNodes();
142 for (int i = 0; i < childNodes.getLength(); i++) {
143 Node childNode = childNodes.item(i);
144 if(childNode instanceof Element) {
145 Element childElm = (Element)childNode;
146 if(matches(childElm, tag)) {
147 res.add(childElm);
148 if(onlyFirst) {
149 break;
150 }
151 }
152 }
153 }
154 return res;
155 }
156
157 private boolean matches(Element childElm, Tag tag) {
158 if(childElm.getLocalName() != null) {
159 if(childElm.getNamespaceURI() != null) {
160 return childElm.getNamespaceURI().equals(tag.getNS()) && tag.getTagName().equals(childElm.getLocalName());
161 }
162 return tag.getTagName().equals(childElm.getLocalName());
163 }
164
165 String tn = childElm.getTagName();
166 int colonIndex = tn.indexOf(':');
167
168 if(colonIndex == -1) {
169 if(tag.getNS() == null) {
170 return tag.getTagName().equals(tn);
171 }
172 return false;
173 }
174
175 String nsPrefix = getNamesSpaces().get(tag.getNS());
176 return childElm.getTagName().equals(nsPrefix + ":" + tag.getTagName());
177 }
178
179 private Map<String, String> getNamesSpaces() {
180 if(namespaces == null) {
181 namespaces = new HashMap<String, String>();
182
183 NamedNodeMap attributes = dom.getAttributes();
184 for (int i = 0; i < attributes.getLength(); i++) {
185 Attr attribute = (Attr)attributes.item(i);
186
187 String name = attribute.getName();
188 String value = attribute.getValue();
189
190 if(name.startsWith("xmlns:")) {
191 namespaces.put(value, name.substring("xmlns:".length()));
192 }
193 }
194 }
195 return namespaces;
196 }
197
198 private Element getFirstElement(List<Element> nodeList) {
199 if(nodeList.isEmpty()) {
200 return null;
201 }
202 return nodeList.get(0);
203 }
204 }