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 }