View Javadoc

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 }