001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU General
007: * Public License Version 2 only ("GPL") or the Common Development and Distribution
008: * License("CDDL") (collectively, the "License"). You may not use this file except in
009: * compliance with the License. You can obtain a copy of the License at
010: * http://www.netbeans.org/cddl-gplv2.html or nbbuild/licenses/CDDL-GPL-2-CP. See the
011: * License for the specific language governing permissions and limitations under the
012: * License. When distributing the software, include this License Header Notice in
013: * each file and include the License file at nbbuild/licenses/CDDL-GPL-2-CP. Sun
014: * designates this particular file as subject to the "Classpath" exception as
015: * provided by Sun in the GPL Version 2 section of the License file that
016: * accompanied this code. If applicable, add the following below the License Header,
017: * with the fields enclosed by brackets [] replaced by your own identifying
018: * information: "Portions Copyrighted [year] [name of copyright owner]"
019: *
020: * Contributor(s):
021: *
022: * The Original Software is NetBeans. The Initial Developer of the Original Software
023: * is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun Microsystems, Inc. All
024: * Rights Reserved.
025: *
026: * If you wish your version of this file to be governed by only the CDDL or only the
027: * GPL Version 2, indicate your decision by adding "[Contributor] elects to include
028: * this software in this distribution under the [CDDL or GPL Version 2] license." If
029: * you do not indicate a single choice of license, a recipient has the option to
030: * distribute your version of this file under either the CDDL, the GPL Version 2 or
031: * to extend the choice of license to its licensees as provided above. However, if
032: * you add GPL Version 2 code and therefore, elected the GPL Version 2 license, then
033: * the option applies only if the new code is made subject to such option by the
034: * copyright holder.
035: */
036:
037: package org.netbeans.installer.utils;
038:
039: import java.io.File;
040: import java.io.FileInputStream;
041: import java.io.FileOutputStream;
042: import java.io.IOException;
043: import java.io.InputStream;
044: import java.io.OutputStream;
045: import java.net.URI;
046: import java.net.URISyntaxException;
047: import java.util.ArrayList;
048: import java.util.Arrays;
049: import java.util.HashMap;
050: import java.util.LinkedList;
051: import java.util.List;
052: import java.util.Locale;
053: import java.util.Map;
054: import java.util.Properties;
055: import javax.xml.parsers.DocumentBuilderFactory;
056: import javax.xml.parsers.ParserConfigurationException;
057: import javax.xml.transform.Result;
058: import javax.xml.transform.Source;
059: import javax.xml.transform.Transformer;
060: import javax.xml.transform.TransformerConfigurationException;
061: import javax.xml.transform.TransformerException;
062: import javax.xml.transform.TransformerFactory;
063: import javax.xml.transform.dom.DOMSource;
064: import javax.xml.transform.stream.StreamResult;
065: import javax.xml.transform.stream.StreamSource;
066: import org.netbeans.installer.product.dependencies.Conflict;
067: import org.netbeans.installer.product.dependencies.InstallAfter;
068: import org.netbeans.installer.product.dependencies.Requirement;
069: import org.netbeans.installer.utils.helper.Dependency;
070: import org.netbeans.installer.utils.exceptions.DownloadException;
071: import org.netbeans.installer.utils.exceptions.ParseException;
072: import org.netbeans.installer.utils.exceptions.XMLException;
073: import org.netbeans.installer.utils.helper.ExtendedUri;
074: import org.netbeans.installer.utils.helper.Feature;
075: import org.netbeans.installer.utils.helper.NbiProperties;
076: import org.netbeans.installer.utils.helper.Version;
077: import org.w3c.dom.Document;
078: import org.w3c.dom.Element;
079: import org.w3c.dom.Node;
080: import org.w3c.dom.NodeList;
081: import org.xml.sax.SAXException;
082:
083: /**
084: *
085: * @author Dmitry Lipin
086: */
087: public abstract class XMLUtils {
088: /////////////////////////////////////////////////////////////////////////////////
089: // Static
090: public static void saveXMLDocument(final Document document,
091: final File file) throws XMLException {
092: LogManager.logEntry("saving document to xml file : " + file);
093: FileOutputStream output = null;
094:
095: for (int i = 0; i < MAXIMUM_SAVE_ATTEMPTS; i++) {
096: LogManager.log("... attempt " + (i + 1));
097: try {
098: LogManager.log("... opening output stream");
099: output = new FileOutputStream(file);
100: LogManager.log("... saving XML to output stream");
101: saveXMLDocument(document, output);
102: LogManager.log("... saving XML to output stream done");
103: // check the size of the resulting file -- sometimes it just happens
104: // to be empty, we need to resave then; if we fail to save for
105: // several times (MAXIMUM_SAVE_ATTEMPTS) -- fail with an
106: // XMLException
107: } catch (IOException e) {
108: LogManager.log(
109: "... can`t save XML, an exception caught", e);
110: throw new XMLException("Cannot save XML document", e);
111: } finally {
112: LogManager
113: .log("... closing output stream if necessary");
114: if (output != null) {
115: try {
116: output.close();
117: output = null;
118: } catch (IOException e) {
119: ErrorManager.notifyDebug(
120: "Could not close the stream", e);
121: }
122: LogManager.log("... output stream is closed now");
123: } else {
124: LogManager.log("... no need to do that");
125: }
126: }
127: LogManager.log("... checking file length");
128: if (file.length() > 0) {
129: LogManager
130: .logExit("... file length is more that 0 so exit");
131: return;
132: }
133: LogManager
134: .log("... file length is zero so go to next cycle");
135: }
136: LogManager
137: .logExit("... throwing XML exception since xml file could not be saved for several attemps");
138: throw new XMLException("Cannot save XML document after "
139: + MAXIMUM_SAVE_ATTEMPTS + " attempts, the resulting "
140: + "file is empty.");
141:
142: }
143:
144: public static void saveXMLDocument(final Document document,
145: final OutputStream output) throws XMLException {
146: try {
147: LogManager
148: .logEntry("... saving XML to output stream started");
149: final Source source = new DOMSource(document);
150: LogManager.log("... DOMSource created");
151: final Result result = new StreamResult(output);
152: LogManager.log("... StreamResult created");
153: final Source xslt = new StreamSource(FileProxy
154: .getInstance().getFile(XSLT_REFORMAT_URI, true));
155: LogManager.log("... XSLT loaded");
156: TransformerFactory tf = TransformerFactory.newInstance();
157: LogManager.log("... transformer factory created");
158: final Transformer transformer = tf.newTransformer(xslt);
159: LogManager.log("... transformer created from xslt");
160: transformer.transform(source, result);
161: LogManager.log("... transformation done");
162: } catch (DownloadException e) {
163: throw new XMLException("Cannot save XML document", e);
164: } catch (TransformerConfigurationException e) {
165: throw new XMLException("Cannot save XML document", e);
166: } catch (TransformerException e) {
167: throw new XMLException("Cannot save XML document", e);
168: } finally {
169: LogManager
170: .logExit("... saving XML to output stream ended (finally)");
171: }
172: }
173:
174: public static Document loadXMLDocument(final File file)
175: throws XMLException {
176: FileInputStream input = null;
177:
178: try {
179: return loadXMLDocument(input = new FileInputStream(file));
180: } catch (IOException e) {
181: throw new XMLException("Cannot open XML file", e);
182: } finally {
183: if (input != null) {
184: try {
185: input.close();
186: } catch (IOException e) {
187: ErrorManager.notifyDebug("Cannot close the stream",
188: e);
189: }
190: }
191: }
192: }
193:
194: public static Document loadXMLDocument(final InputStream input)
195: throws XMLException {
196: try {
197: return DocumentBuilderFactory.newInstance()
198: .newDocumentBuilder().parse(input);
199: } catch (ParserConfigurationException e) {
200: throw new XMLException("Cannot parse XML", e);
201: } catch (SAXException e) {
202: throw new XMLException("Cannot parse XML", e);
203: } catch (IOException e) {
204: throw new XMLException("Cannot parse XML", e);
205: }
206: }
207:
208: public static Element getDocumentElement(final File file)
209: throws XMLException {
210: return loadXMLDocument(file).getDocumentElement();
211: }
212:
213: public static Element getDocumentElement(final InputStream input)
214: throws XMLException {
215: return loadXMLDocument(input).getDocumentElement();
216: }
217:
218: public static List<Element> getChildren(final Element element) {
219: final List<Element> children = new LinkedList<Element>();
220:
221: final NodeList list = element.getChildNodes();
222: for (int i = 0; i < list.getLength(); i++) {
223: final Node node = list.item(i);
224:
225: if (node instanceof Element) {
226: children.add((Element) node);
227: }
228: }
229:
230: return children;
231: }
232:
233: public static List<Element> getChildren(final Element element,
234: final String... names) {
235: final List<Element> children = new LinkedList<Element>();
236:
237: final NodeList list = element.getChildNodes();
238: for (int i = 0; i < list.getLength(); i++) {
239: final Node node = list.item(i);
240:
241: if (node instanceof Element) {
242: for (int j = 0; j < names.length; j++) {
243: if (node.getNodeName().equals(names[j])) {
244: children.add((Element) node);
245: break;
246: }
247: }
248: }
249: }
250:
251: return children;
252: }
253:
254: public static Element getChild(final Element element,
255: final String path) {
256: final String[] pathComponents;
257:
258: if (path.contains(StringUtils.FORWARD_SLASH)) {
259: pathComponents = path.split(StringUtils.FORWARD_SLASH);
260: } else {
261: pathComponents = new String[] { path };
262: }
263:
264: Element child = element;
265:
266: for (String name : pathComponents) {
267: boolean found = false;
268:
269: for (Element subchild : getChildren(child)) {
270: if (subchild.getNodeName().equals(name)) {
271: child = subchild;
272: found = true;
273: break;
274: }
275: }
276:
277: if (!found) {
278: child = null;
279: break;
280: }
281: }
282:
283: return child;
284: }
285:
286: public static int countDescendants(final Element element,
287: final String... names) {
288: return countDescendants(element, Arrays.asList(names));
289: }
290:
291: public static int countDescendants(final Element element,
292: final List<String> names) {
293: int count = 0;
294: for (Element child : getChildren(element)) {
295: if (names.contains(child.getNodeName())) {
296: count++;
297: }
298:
299: count += countDescendants(child, names);
300: }
301:
302: return count;
303: }
304:
305: public static Element appendChild(final Element element,
306: final String name, final String text) {
307: final Element child = element.getOwnerDocument().createElement(
308: name);
309:
310: child.setTextContent(text != null ? text
311: : StringUtils.EMPTY_STRING);
312: element.appendChild(child);
313:
314: return child;
315: }
316:
317: // object <-> dom ///////////////////////////////////////////////////////////////
318: public static Dependency parseDependency(final Element element)
319: throws ParseException {
320: final String type = element.getNodeName();
321: final String uid = element.getAttribute("uid");
322: final Version lower = Version.getVersion(element
323: .getAttribute("version-lower"));
324: final Version upper = Version.getVersion(element
325: .getAttribute("version-upper"));
326: final Version resolved = Version.getVersion(element
327: .getAttribute("version-resolved"));
328: Dependency dependency = null;
329: if (type.equals(Requirement.NAME)) {
330: List<List<Requirement>> orList = new ArrayList<List<Requirement>>();
331: for (Element orElement : getChildren(element, "or")) {
332: List<Requirement> requirements = new ArrayList<Requirement>();
333: for (Element el : getChildren(orElement)) {
334: Dependency dep = parseDependency(el);
335: if (dep instanceof Requirement) {
336: requirements.add((Requirement) dep);
337: } else {
338: throw new ParseException(
339: "OR dependencies are not supported for "
340: + dep.getName());
341: }
342: }
343: orList.add(requirements);
344: }
345: dependency = new Requirement(uid, lower, upper, resolved,
346: orList);
347: } else if (type.equals(Conflict.NAME)) {
348: dependency = new Conflict(uid, lower, upper, resolved);
349: } else if (type.equals(InstallAfter.NAME)) {
350: dependency = new InstallAfter(uid, lower, upper, resolved);
351: } else {
352: throw new ParseException("Unknown dependency : " + type);
353: }
354: return dependency;
355: }
356:
357: public static Element saveDependency(final Dependency dependency,
358: final Element element) {
359: element.setAttribute("uid", dependency.getUid());
360:
361: if (dependency.getVersionLower() != null) {
362: element.setAttribute("version-lower", dependency
363: .getVersionLower().toString());
364: }
365: if (dependency.getVersionUpper() != null) {
366: element.setAttribute("version-upper", dependency
367: .getVersionUpper().toString());
368: }
369: if (dependency.getVersionResolved() != null) {
370: element.setAttribute("version-resolved", dependency
371: .getVersionResolved().toString());
372: }
373: if (dependency instanceof Requirement) {
374: Requirement requirement = (Requirement) dependency;
375: List<List<Requirement>> orList = requirement
376: .getAlternatives();
377: for (List<Requirement> requirememntsBlock : orList) {
378: element.appendChild(saveDependencies(
379: requirememntsBlock, element.getOwnerDocument()
380: .createElement("or")));
381: }
382: }
383:
384: return element;
385: }
386:
387: public static List<Dependency> parseDependencies(
388: final Element element) throws ParseException {
389: final List<Dependency> dependencies = new LinkedList<Dependency>();
390:
391: for (Element child : getChildren(element)) {
392: dependencies.add(parseDependency(child));
393: }
394:
395: return dependencies;
396: }
397:
398: public static Element saveDependencies(
399: final List<? extends Dependency> dependencies,
400: final Element element) {
401: final Document document = element.getOwnerDocument();
402:
403: for (Dependency dependency : dependencies) {
404: element.appendChild(saveDependency(dependency, document
405: .createElement(dependency.getName())));
406: }
407:
408: return element;
409: }
410:
411: public static Properties parseProperties(final Element element)
412: throws ParseException {
413: final Properties properties = new Properties();
414:
415: if (element != null) {
416: for (Element child : XMLUtils.getChildren(element,
417: "property")) {
418: String name = child.getAttribute("name");
419: String value = child.getTextContent();
420:
421: properties.setProperty(name, value);
422: }
423: }
424:
425: return properties;
426: }
427:
428: public static Element saveProperties(final Properties properties,
429: final Element element) {
430: final Document document = element.getOwnerDocument();
431:
432: for (Object key : properties.keySet()) {
433: final Element propertyElement = document
434: .createElement("property");
435:
436: propertyElement.setAttribute("name", key.toString());
437: propertyElement.setTextContent(properties.get(key)
438: .toString());
439:
440: element.appendChild(propertyElement);
441: }
442:
443: return element;
444: }
445:
446: public static NbiProperties parseNbiProperties(final Element element)
447: throws ParseException {
448: return new NbiProperties(parseProperties(element));
449: }
450:
451: public static Element saveNbiProperties(
452: final NbiProperties properties, final Element element) {
453: return saveProperties(properties, element);
454: }
455:
456: public static ExtendedUri parseExtendedUri(final Element element)
457: throws ParseException {
458: try {
459: final URI uri = new URI(getChild(element, "default-uri")
460: .getTextContent());
461: final long size = Long.parseLong(element
462: .getAttribute("size"));
463: final String md5 = element.getAttribute("md5");
464:
465: final List<URI> alternates = new LinkedList<URI>();
466:
467: for (Element alternateElement : XMLUtils.getChildren(
468: element, "alternate-uri")) {
469: alternates.add(new URI(alternateElement
470: .getTextContent()));
471: }
472:
473: if (uri.getScheme().equals("file")) {
474: return new ExtendedUri(uri, alternates, uri, size, md5);
475: } else {
476: return new ExtendedUri(uri, alternates, size, md5);
477: }
478: } catch (URISyntaxException e) {
479: throw new ParseException("Cannot parse extended URI", e);
480: } catch (NumberFormatException e) {
481: throw new ParseException("Cannot parse extended URI", e);
482: }
483: }
484:
485: public static Element saveExtendedUri(final ExtendedUri uri,
486: final Element element) {
487: final Document document = element.getOwnerDocument();
488:
489: element.setAttribute("size", Long.toString(uri.getSize()));
490: element.setAttribute("md5", uri.getMd5());
491:
492: // the default uri would be either "local" (if it's present) or the
493: // "remote" one
494: Element uriElement = document.createElement("default-uri");
495: if (uri.getLocal() != null) {
496: uriElement.setTextContent(uri.getLocal().toString());
497: } else {
498: uriElement.setTextContent(uri.getRemote().toString());
499: }
500: element.appendChild(uriElement);
501:
502: // if the "local" uri is not null, we should save the "remote" uri as the
503: // first alternate, unless it's the same as the local
504: if ((uri.getLocal() != null)
505: && !uri.getRemote().equals(uri.getLocal())) {
506: uriElement = document.createElement("alternate-uri");
507:
508: uriElement.setTextContent(uri.getRemote().toString());
509: element.appendChild(uriElement);
510: }
511:
512: for (URI alternateUri : uri.getAlternates()) {
513: if (!alternateUri.equals(uri.getRemote())) {
514: uriElement = document.createElement("alternate-uri");
515:
516: uriElement.setTextContent(alternateUri.toString());
517: element.appendChild(uriElement);
518: }
519: }
520:
521: return element;
522: }
523:
524: public static List<ExtendedUri> parseExtendedUrisList(
525: final Element element) throws ParseException {
526: final List<ExtendedUri> uris = new LinkedList<ExtendedUri>();
527:
528: for (Element uriElement : getChildren(element)) {
529: uris.add(parseExtendedUri(uriElement));
530: }
531:
532: return uris;
533: }
534:
535: public static Element saveExtendedUrisList(
536: final List<ExtendedUri> uris, final Element element) {
537: final Document document = element.getOwnerDocument();
538:
539: for (ExtendedUri uri : uris) {
540: element.appendChild(saveExtendedUri(uri, document
541: .createElement("file")));
542: }
543:
544: return element;
545: }
546:
547: public static Map<Locale, String> parseLocalizedString(
548: final Element element) throws ParseException {
549: final Map<Locale, String> map = new HashMap<Locale, String>();
550:
551: final Element defaultElement = getChild(element, "default");
552: map.put(new Locale(StringUtils.EMPTY_STRING), StringUtils
553: .parseAscii(defaultElement.getTextContent()));
554:
555: for (Element localizedElement : getChildren(element,
556: "localized")) {
557: final Locale locale = StringUtils
558: .parseLocale(localizedElement
559: .getAttribute("locale"));
560: final String localizedString = StringUtils
561: .parseAscii(localizedElement.getTextContent());
562:
563: map.put(locale, localizedString);
564: }
565:
566: return map;
567: }
568:
569: public static Element saveLocalizedString(
570: final Map<Locale, String> map, final Element element) {
571: final Document document = element.getOwnerDocument();
572:
573: final Element defaultElement = document
574: .createElement("default");
575: defaultElement.setTextContent(StringUtils.convertToAscii(map
576: .get(new Locale(StringUtils.EMPTY_STRING))));
577: element.appendChild(defaultElement);
578:
579: for (Locale locale : map.keySet()) {
580: if (!map.get(locale).equals(
581: map.get(new Locale(StringUtils.EMPTY_STRING)))) {
582: final Element localizedElement = document
583: .createElement("localized");
584:
585: localizedElement.setAttribute("locale", locale
586: .toString());
587: localizedElement.setTextContent(StringUtils
588: .convertToAscii(map.get(locale)));
589:
590: element.appendChild(localizedElement);
591: }
592: }
593:
594: return element;
595: }
596:
597: public static Feature parseFeature(final Element element)
598: throws ParseException {
599: final String id = element.getAttribute("id");
600: final long offset = Long.parseLong(element
601: .getAttribute("offset"));
602: final ExtendedUri iconUri = parseExtendedUri(getChild(element,
603: "icon"));
604:
605: final Map<Locale, String> displayNames = parseLocalizedString(getChild(
606: element, "display-name"));
607: final Map<Locale, String> descriptions = parseLocalizedString(getChild(
608: element, "description"));
609:
610: return new Feature(id, offset, iconUri, displayNames,
611: descriptions);
612: }
613:
614: public static Element saveFeature(final Feature feature,
615: final Element element) {
616: final Document document = element.getOwnerDocument();
617:
618: element.setAttribute("id", feature.getId());
619: element.setAttribute("offset", Long.toString(feature
620: .getOffset()));
621:
622: element.appendChild(saveExtendedUri(feature.getIconUri(),
623: document.createElement("icon")));
624: element.appendChild(saveLocalizedString(feature
625: .getDisplayNames(), document
626: .createElement("display-name")));
627: element.appendChild(saveLocalizedString(feature
628: .getDescriptions(), document
629: .createElement("description")));
630:
631: return element;
632: }
633:
634: public static List<Feature> parseFeaturesList(final Element element)
635: throws ParseException {
636: final List<Feature> features = new LinkedList<Feature>();
637:
638: for (Element featureElement : XMLUtils.getChildren(element)) {
639: features.add(XMLUtils.parseFeature(featureElement));
640: }
641:
642: return features;
643: }
644:
645: public static Element saveFeaturesList(
646: final List<Feature> features, final Element element) {
647: final Document document = element.getOwnerDocument();
648:
649: for (Feature feature : features) {
650: element.appendChild(saveFeature(feature, document
651: .createElement("feature")));
652: }
653:
654: return element;
655: }
656:
657: /////////////////////////////////////////////////////////////////////////////////
658: // Constants
659: public static final String XSLT_REFORMAT_URI = FileProxy.RESOURCE_SCHEME_PREFIX
660: + "org/netbeans/installer/utils/xml/reformat.xslt"; // NOI18N
661:
662: private static final int MAXIMUM_SAVE_ATTEMPTS = 3;
663: }
|