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.dombuilders;
31  
32  import dk.sosi.seal.model.ModelException;
33  import dk.sosi.seal.model.constants.NameSpaces;
34  import dk.sosi.seal.model.constants.SOAPTags;
35  import dk.sosi.seal.model.constants.Tag;
36  import dk.sosi.seal.xml.XmlUtil;
37  import org.w3c.dom.Document;
38  import org.w3c.dom.Element;
39  
40  /**
41   * Abstract base class for builder classes.
42   * 
43   * @author ads
44   * 
45   * @param <T>
46   *            The type of object returned when <code>#build()</code> is called.
47   */
48  public abstract class AbstractDOMBuilder<T> {
49  
50      private Document localDoc;
51  
52      /**
53       * Instructs the build to construct the object.
54       * 
55       * @return The constructed instance.
56       * @throws ModelException
57       *             Thrown if the build process fails.
58       */
59      public abstract T build() throws ModelException;
60  
61      /**
62       * Method called by {@link #appendBody(Document, Element)}.<br />
63       * Use this method to add the actual contens to the body element.
64       * 
65       * @param doc
66       *            The <code>Document</code> container instance.
67       * @param body
68       *            The body <code>Element</code> instance.
69       */
70      protected abstract void addBodyContent(Document doc, Element body);
71  
72      /**
73       * Method called by {@link #appendHeader(Document, Element)}.<br />
74       * Use this method to add the actual contens to the header element.
75       * 
76       * @param doc
77       *            The <code>Document</code> container instance.
78       * @param header
79       *            The header <code>Element</code> instance.
80       */
81      protected abstract void addHeaderContent(Document doc, Element header);
82  
83      /**
84       * Add a NameSpace attribute to the supplied <code>Element</code>.
85       * 
86       * @param element
87       *            The <code>Element</code> to modify.
88       * @param ns
89       *            The name space short name.<br />
90       *            When added, that value will be prefixed with <i>xmlns:</i>.
91       * @param schema
92       *            The schema location of the NameSpace.
93       */
94      protected final void addNS(Element element, String ns, String schema) {
95          element.setAttributeNS(NameSpaces.XMLNS_SCHEMA, NameSpaces.NS_XMLNS + ":" + ns, schema);
96      }
97  
98      /**
99       * Method called by {@link #appendRoot(Document)}.<br />
100      * Use this method to add additional NameSpace declarations to the <code>Document</code> or attributes to the root element.
101      * 
102      * @param root
103      *            The envelope <code>Element</code> instance.
104      */
105     protected abstract void addRootAttributes(Element root);
106 
107     /**
108      * Method called by {@link #createDocument()}.<br />
109      * Override this method to control the creation of the body <code>Element</code>.<br />
110      * The default implementation adds <i>soapenv:Body</i> element.
111      * 
112      * @param doc
113      *            The <code>Document</code> container instance.
114      * @param envelope
115      *            The root <code>Element</code> instance.
116      */
117     protected void appendBody(Document doc, Element envelope) {
118         Element body = doc.createElementNS(NameSpaces.SOAP_SCHEMA, SOAPTags.BODY_PREFIXED);
119         envelope.appendChild(body);
120         addBodyContent(doc, body);
121     }
122 
123     /**
124      * Method called by {@link #createDocument()}.<br />
125      * Override this method to control the creation of the header <code>Element</code>.<br />
126      * The default implementation adds <i>soapenv:Header</i> element.
127      * 
128      * @param doc
129      *            The <code>Document</code> container instance.
130      * @param envelope
131      *            The root <code>Element</code> instance.
132      */
133     protected void appendHeader(Document doc, Element envelope) {
134         Element header = doc.createElementNS(NameSpaces.SOAP_SCHEMA, SOAPTags.HEADER_PREFIXED);
135         envelope.appendChild(header);
136         addHeaderContent(doc, header);
137     }
138 
139     /**
140      * Method called by {@link #createDocument()}.<br />
141      * Override this method to control the creation of the root <code>Element</code>.<br />
142      * The default implementation adds <i>soapenv:Envelope</i> element and invokes the {@link #addRootAttributes(Element)} method.
143      * 
144      * @param doc
145      *            The <code>Document</code> container instance.
146      * @return The created root <code>Element</code>.
147      */
148     protected Element appendRoot(Document doc) {
149         Element envelope = doc.createElementNS(NameSpaces.SOAP_SCHEMA, SOAPTags.ENVELOPE_PREFIXED);
150         addRootAttributes(envelope);
151         doc.appendChild(envelope);
152         return envelope;
153     }
154 
155     /**
156      * Used internally to iniate the build process.<br />
157      * This method validates the contents, creates an empty <code>Document</code><br />
158      * and then populates the contens be calling {@link #appendRoot(Document)}, {@link #appendHeader(Document, Element)} and {@link #appendBody(Document, Element)}.<br />
159      * This method should be invoked by the extending class when {@link #build()} is called.
160      * 
161      * @return The generated <code>Document</code>.
162      */
163     protected Document createDocument() {
164         validateBeforeBuild();
165 
166         // Store in class to temporary use.
167         localDoc = XmlUtil.createEmptyDocument();
168 
169         Element root = appendRoot(localDoc);
170         appendHeader(localDoc, root);
171         appendBody(localDoc, root);
172 
173         Document result = localDoc;
174         localDoc = null;
175 
176         return result;
177     }
178 
179     protected Element createElement(Tag tag) {
180         return localDoc.createElementNS(tag.getNS(), tag.getPrefix() + ":" + tag.getTagName());
181     }
182 
183     /**
184      * Validate the value of the attribute.<br />
185      * This method is a simple validator, that only checks for <code>null</code> values.
186      * 
187      * @param attribute
188      *            The name of the attribute being validated.<br />
189      *            This value is used for providing an informative exception cause.
190      * @param value
191      *            The value to validate.
192      * @throws ModelException
193      *             Thrown if the value fails validation.
194      */
195     protected final void validate(String attribute, Object value) throws ModelException {
196         if(value == null) {
197             throw new ModelException(attribute + " is mandatory - but was null.");
198         }
199     }
200 
201     /**
202      * Validate an attribute. <br />
203      * This method validates that a <code>String</code> attribute is neither <code>null</code>, an empty <code>String</code> or a <code>String</code> of spaces.
204      * 
205      * @param attribute
206      *            The name of the attribute being validated.<br />
207      *            This value is used for providing an informative exception cause.
208      * @param value
209      *            The value to validate.
210      * @throws ModelException
211      *             Thrown if the value fails validation.
212      */
213     protected final void validate(String attribute, String value) throws ModelException {
214         if(value == null) {
215             throw new ModelException(attribute + " is mandatory - but was null.");
216         }
217         validateValue(attribute, value);
218     }
219 
220     /**
221      * Methdo called by {@link #createDocument()} before the process of creating the <code>Document</code> is initiated.<br />
222      * Implementers can use the method to verify that all mandatory attriutes are set before constructing the <code>Document</code>.
223      * 
224      * @throws ModelException
225      *             Thrown if one or more requirements are not fulfilled.
226      */
227     protected abstract void validateBeforeBuild() throws ModelException;
228 
229     /**
230      * Validate the value of an attribute. <br />
231      * This method validates that a <code>String</code> attribute is neither an empty <code>String</code> or a <code>String</code> of spaces.
232      * 
233      * @param attribute
234      *            The name of the attribute being validated.<br />
235      *            This value is used for providing an informative exception cause.
236      * @param value
237      *            The value to validate - if <code>null</code> the validation will be skipped.
238      * @throws ModelException
239      *             Thrown if the value fails validation.
240      */
241     protected final void validateValue(String attribute, String value) throws ModelException {
242         if(value != null) {
243             if(value.length() == 0) {
244                 throw new ModelException(attribute + " is mandatory - but was an empty String.");
245             } else if(value.charAt(0) == ' ' || value.charAt(value.length() - 1) == ' ') {
246                 if(value.trim().length() == 0) {
247                     throw new ModelException(attribute + " is mandatory - but was an empty String.");
248                 }
249             }
250         }
251     }
252 }