View Javadoc

1   /*
2    * $HeadURL: $
3    * $Date: $
4    * $Revision: $
5    * $Author: $
6    * 
7    * Copyright (c) 2005 MindTree Consulting Ltd. 
8    * 
9    * This file is part of Insight.
10   * 
11   * Insight is free software: you can redistribute it and/or modify it under the 
12   * terms of the GNU General Public License as published by the Free Software 
13   * Foundation, either version 3 of the License, or (at your option) any later 
14   * version.
15   * 
16   * Insight is distributed in the hope that it will be useful, but 
17   * WITHOUT ANY WARRANTY; without even the implied warranty of 
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General 
19   * Public License for more details.
20   * 
21   * You should have received a copy of the GNU General Public License along with 
22   * Insight.  If not, see <http://www.gnu.org/licenses/>.
23   */
24  
25  package com.mindtree.techworks.insight.preferences.xmlpersistence;
26  
27  import java.io.File;
28  import java.io.FileOutputStream;
29  import java.io.IOException;
30  import java.util.Iterator;
31  import java.util.List;
32  
33  import javax.xml.parsers.DocumentBuilder;
34  import javax.xml.parsers.DocumentBuilderFactory;
35  import javax.xml.parsers.ParserConfigurationException;
36  
37  import org.apache.xml.serialize.OutputFormat;
38  import org.apache.xml.serialize.XMLSerializer;
39  import org.w3c.dom.Document;
40  import org.w3c.dom.Element;
41  import org.w3c.dom.NodeList;
42  import org.xml.sax.SAXException;
43  
44  import com.mindtree.techworks.insight.preferences.PreferenceHandlerStoreException;
45  import com.mindtree.techworks.insight.preferences.model.ListPreferenceAttribute;
46  import com.mindtree.techworks.insight.preferences.model.Preference;
47  import com.mindtree.techworks.insight.preferences.model.PreferenceAttribute;
48  import com.mindtree.techworks.insight.preferences.model.PreferenceAttributeType;
49  import com.mindtree.techworks.insight.preferences.util.Crypt;
50  
51  /**
52   * This class saves the preferences into the preference file. No new top-level
53   * preferences can be added, however child preferences and preference attributes
54   * can be added to existing preferences. This implementation creates a DOM model
55   * of the preferences, and stores the model.
56   * 
57   * @see com.mindtree.techworks.insight.preferences.xmlpersistence.XMLPreferenceDataHandler
58   *      XMLPreferenceDataHandler
59   * @author <a href="mailto:bindul_bhowmik@mintree.com">Bindul Bhowmik</a>
60   * @version $Revision: 27 $ $Date: 2007-12-16 04:58:03 -0700 (Sun, 16 Dec 2007) $
61   */
62  public class XMLDOMPreferenceDataWriter implements PreferenceXMLNameConstants {
63  
64  	/**
65  	 * The preference file to store.
66  	 */
67  	private String preferenceFile;
68  
69  	/**
70  	 * Default Constructor which gets the list of preferences to store.
71  	 * 
72  	 * @param preferenceFile
73  	 *            The preferenceFile to store.
74  	 */
75  	public XMLDOMPreferenceDataWriter (String preferenceFile) {
76  
77  		this.preferenceFile = preferenceFile;
78  	}
79  
80  	/**
81  	 * Writes the preferences to the store
82  	 * 
83  	 * @param preferences
84  	 *            The list of preferences to store
85  	 * @param outputFile
86  	 *            The filt to which the preferences are to be stored
87  	 * @throws PreferenceHandlerStoreException
88  	 *             If the preference cannot be stored.
89  	 */
90  	public void writePreferences (List preferences, String outputFile)
91  			throws PreferenceHandlerStoreException {
92  
93  		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
94  				.newInstance ();
95  		documentBuilderFactory.setNamespaceAware (true);
96  		try {
97  			DocumentBuilder documentBuilder = documentBuilderFactory
98  					.newDocumentBuilder ();
99  			Document document = documentBuilder
100 					.parse (new File (preferenceFile));
101 			processPreferenceList (preferences, document);
102 
103 			// Write the DOM
104 			OutputFormat format = new OutputFormat ();
105 			format.setMethod ("xml");
106 			format.setIndenting (true);
107 			FileOutputStream fos = new FileOutputStream (outputFile);
108 			XMLSerializer serializer = new XMLSerializer (format
109 					.getEncodingInfo ().getWriter (fos), format);
110 			serializer.asDOMSerializer ();
111 			serializer.serialize (document);
112 			try {
113 				fos.close ();
114 			} catch (IOException e) {
115 				// Forget about it.
116 			}
117 			serializer = null;
118 
119 		} catch (ParserConfigurationException e) {
120 			throw new PreferenceHandlerStoreException (
121 					"Parser Configuration Exception in DocumentBuilder", e);
122 		} catch (SAXException e) {
123 			throw new PreferenceHandlerStoreException (
124 					"SAX Exception in reading document", e);
125 		} catch (IOException e) {
126 			throw new PreferenceHandlerStoreException (
127 					"IO Exception in reading document", e);
128 		}
129 
130 	}
131 
132 	/**
133 	 * Processes the list of preferences to store
134 	 * 
135 	 * @param preferences
136 	 *            Preferences to store
137 	 * @param document
138 	 *            Document to which preferences are being written.
139 	 */
140 	private void processPreferenceList (List preferences, Document document) {
141 
142 		Element rootElement = document.getDocumentElement ();
143 
144 		NodeList nodeList = rootElement
145 				.getElementsByTagNameNS (DEFAULT_NAMESPACE, XMLNAME_PREFERENCE);
146 		int nodeCount = nodeList.getLength ();
147 
148 		Iterator preferencesIterator = preferences.iterator ();
149 		while (preferencesIterator.hasNext ()) {
150 			Preference preference = (Preference) preferencesIterator.next ();
151 			String id = preference.getId ();
152 
153 			// Get the old element
154 			Element oldElement = null;
155 			for (int i = 0; i < nodeCount; i++ ) {
156 				Element currentElement = (Element) nodeList.item (i);
157 				if (id.equals (currentElement
158 						.getAttribute (XMLNAME_PREFERENCE_ID))) {
159 					oldElement = currentElement;
160 					break;
161 				}
162 			}
163 
164 			// Replace the old element
165 			if (null != oldElement) {
166 				Element newElement = createPreferenceElement (preference,
167 																document);
168 				rootElement.replaceChild (newElement, oldElement);
169 				if (preferencesIterator.hasNext ()) {
170 					nodeList = rootElement
171 							.getElementsByTagNameNS (DEFAULT_NAMESPACE,
172 														XMLNAME_PREFERENCE);
173 					nodeCount = nodeList.getLength ();
174 				}
175 			}
176 		}
177 
178 	}
179 
180 	/**
181 	 * Creates an element for a preference.
182 	 * 
183 	 * @param preference
184 	 *            The preference for which the element is to be created.
185 	 * @param document
186 	 *            The document which is the parent of the element.
187 	 * @return An element containing the current element
188 	 */
189 	private Element createPreferenceElement (Preference preference,
190 			Document document) {
191 
192 		// Create the element
193 		Element preferenceElement = document
194 				.createElementNS (DEFAULT_NAMESPACE, XMLNAME_PREFERENCE);
195 
196 		// Populate the mandatory paramenters
197 		preferenceElement.setAttribute (XMLNAME_PREFERENCE_ID, preference
198 				.getId ());
199 		preferenceElement.setAttribute (XMLNAME_PREFERENCE_NAME, preference
200 				.getName ());
201 		preferenceElement.setAttribute (XMLNAME_PREFERENCE_ISUSERMOD, String
202 				.valueOf (preference.isUserModifiable ()));
203 		preferenceElement.setAttribute (XMLNAME_PREFERENCE_ISAGGREGATED, String
204 				.valueOf (preference.isAggregated ()));
205 
206 		// Populate the optional parameters
207 		String displayClass = preference.getDisplayClass ();
208 		if (null != displayClass && displayClass.length () > 0) {
209 			preferenceElement.setAttribute (XMLNAME_PREFERENCE_DISPLAY_CLASS,
210 											displayClass);
211 		}
212 
213 		// Populate the Preference Attributes
214 		Iterator preferenceAttrItr = preference.iteratePreferenceAttributes ();
215 		while (preferenceAttrItr.hasNext ()) {
216 			preferenceElement.appendChild (createPreferenceAttributeElement (
217 								(PreferenceAttribute) preferenceAttrItr.next (),
218 								document));
219 		}
220 
221 		// Populate the child Preferences
222 		Iterator childPreferenceItr = preference.iterateChildPreferences ();
223 		while (null != childPreferenceItr && childPreferenceItr.hasNext ()) {
224 			preferenceElement
225 					.appendChild (createPreferenceElement (
226 								(Preference) childPreferenceItr.next (),
227 								document));
228 		}
229 
230 		return preferenceElement;
231 	}
232 
233 	/**
234 	 * Creates a PreferenceAttribute element
235 	 * 
236 	 * @param attribute
237 	 *            The preference attribute for which the element is required.
238 	 * @param document
239 	 *            The document which is the parent of the element.
240 	 * @return An element for the preference attribute
241 	 */
242 	private Element createPreferenceAttributeElement (
243 			PreferenceAttribute attribute, Document document) {
244 
245 		// Create the element
246 		Element preferenceAttributeElement = document
247 				.createElementNS (DEFAULT_NAMESPACE,
248 									XMLNAME_PREFERENCE_ATTRIBUTE);
249 
250 		// Populate the attributes
251 		preferenceAttributeElement.setAttribute (XMLNAME_PREFERENCE_ATT_ID,
252 													attribute.getId ());
253 		preferenceAttributeElement.setAttribute (XMLNAME_PREFERENCE_ATT_NAME,
254 													attribute.getName ());
255 
256 		// Set the optional element
257 		String defaultValue = attribute.getDefaultValue ();
258 		if (null != defaultValue && defaultValue.length () > 0) {
259 			int indexOfLT = defaultValue.indexOf ("<");
260 			if (indexOfLT > -1 && defaultValue.indexOf (">", indexOfLT) > -1) {
261 				preferenceAttributeElement.appendChild (createCDATAElement (
262 										DEFAULT_NAMESPACE,
263 										XMLNAME_PREF_ATT_DEFAULT_VALUE,
264 										defaultValue,
265 										document));
266 			} else {
267 				preferenceAttributeElement
268 						.appendChild (createTextElement (
269 										DEFAULT_NAMESPACE,
270 										XMLNAME_PREF_ATT_DEFAULT_VALUE,
271 										defaultValue,
272 										document));
273 			}
274 		}
275 
276 		if (attribute.isPersistant ()) {
277 			// This is the change for Enhancement Tracker #73 which requires
278 			// data containing XML data to be stored as CDATA sections in the
279 			// XML preference
280 			String value = attribute.getValue ();
281 			if(value == null)
282 				value = "";
283 			int indexOfLT = value.indexOf ("<");
284 			if (indexOfLT > -1 && value.indexOf (">", indexOfLT) > -1) {
285 				// Need to write this as CDATA
286 				preferenceAttributeElement
287 						.appendChild (createCDATAElement (
288 										DEFAULT_NAMESPACE,
289 										XMLNAME_PREF_ATT_VALUE,
290 										(attribute.isEncrypted())?Crypt.encryptHexString(value):value, document));
291 			} else {
292 				preferenceAttributeElement
293 						.appendChild (createTextElement (
294 										DEFAULT_NAMESPACE,
295 										XMLNAME_PREF_ATT_VALUE,
296 										(attribute.isEncrypted())?Crypt.encryptHexString(value):value, document));
297 			}
298 		}
299 
300 		// Set the mandatory elements
301 		preferenceAttributeElement
302 				.appendChild (createTextElement (DEFAULT_NAMESPACE,
303 									XMLNAME_PREF_ATT_TYPE,
304 									attribute.getType ().getName (),
305 									document));
306 		if(attribute.isEncrypted()){
307 			preferenceAttributeElement
308 			.appendChild (createTextElement (DEFAULT_NAMESPACE,
309 					XMLNAME_PREF_ATT_ENCRYPTED,
310 					String.valueOf (attribute.isEncrypted ()),
311 					document));
312 		}
313 		preferenceAttributeElement
314 				.appendChild (createTextElement (DEFAULT_NAMESPACE,
315 									XMLNAME_PREF_ATT_PERSISTANT,
316 									String.valueOf (attribute.isPersistant ()),
317 									document));
318 		preferenceAttributeElement
319 				.appendChild (createTextElement (DEFAULT_NAMESPACE,
320 									XMLNAME_PREF_ATT_USER_MOD,
321 									String.valueOf (
322 									      attribute.isUserModifiable ()),
323 									document));
324 
325 		// Check List preference Attribute
326 		if (attribute.getType ().getType () == PreferenceAttributeType.LIST_TYPE
327 				&& attribute instanceof ListPreferenceAttribute) {
328 			preferenceAttributeElement
329 					.setAttributeNS (XSI_NAMESPACE, XMLNAME_XSI_TYPE,
330 										XMLNAME_LIST_PREFERENCE_ATTRIBUTE);
331 			Element optionsElement = document
332 					.createElementNS (DEFAULT_NAMESPACE,
333 										XMLNAME_PREF_ATT_OPTIONS);
334 
335 			ListPreferenceAttribute listPreferenceAttribute = (ListPreferenceAttribute) attribute;
336 			Iterator options = listPreferenceAttribute.iterateOptions ();
337 			while (options.hasNext ()) {
338 				String optionsValue = (String) options.next ();
339 				int indexOfLT = optionsValue.indexOf ("<");
340 				if (indexOfLT > -1
341 						&& optionsValue.indexOf (">", indexOfLT) > -1) {
342 					optionsElement
343 							.appendChild (createCDATAElement (DEFAULT_NAMESPACE,
344 										XMLNAME_PREF_ATT_OPTION_VAL,
345 										optionsValue,
346 										document));
347 				} else {
348 					optionsElement
349 							.appendChild (createTextElement (DEFAULT_NAMESPACE,
350 										XMLNAME_PREF_ATT_OPTION_VAL,
351 										optionsValue,
352 										document));
353 				}
354 			}
355 
356 			preferenceAttributeElement.appendChild (optionsElement);
357 		}
358 
359 		return preferenceAttributeElement;
360 	}
361 
362 	/**
363 	 * Creates a text element with the name of the element and the value
364 	 * supplied.
365 	 * 
366 	 * @param namespace
367 	 *            The namespace of the element.
368 	 * @param elementName
369 	 *            The local name of the element.
370 	 * @param value
371 	 *            The value to be added to the element.
372 	 * @param document
373 	 *            The document which will be the parent of the element.
374 	 * @return And element containing a text value
375 	 */
376 	private Element createTextElement (String namespace, String elementName,
377 			String value, Document document) {
378 
379 		Element textElement = null;
380 
381 		if (null == namespace) {
382 			textElement = document.createElement (elementName);
383 		} else {
384 			textElement = document.createElementNS (namespace, elementName);
385 		}
386 
387 		textElement.appendChild (document.createTextNode (value));
388 		return textElement;
389 	}
390 
391 	/**
392 	 * Creates a text element with the name of the element and the value
393 	 * supplied.
394 	 * 
395 	 * @param namespace
396 	 *            The namespace of the element.
397 	 * @param elementName
398 	 *            The local name of the element.
399 	 * @param value
400 	 *            The value to be added to the element as a CDATA section.
401 	 * @param document
402 	 *            The document which will be the parent of the element.
403 	 * @return An element containing a CDATA section with the value provided.
404 	 */
405 	private Element createCDATAElement (String namespace, String elementName,
406 			String value, Document document) {
407 
408 		Element cdataElement = null;
409 
410 		if (null == namespace) {
411 			cdataElement = document.createElement (elementName);
412 		} else {
413 			cdataElement = document.createElementNS (namespace, elementName);
414 		}
415 
416 		cdataElement.appendChild (document.createCDATASection (value));
417 		return cdataElement;
418 	}
419 
420 }