felix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cziege...@apache.org
Subject svn commit: r569278 - in /felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml: ComponentDescriptorIO.java IOUtils.java MetaTypeIO.java
Date Fri, 24 Aug 2007 07:27:05 GMT
Author: cziegeler
Date: Fri Aug 24 00:27:04 2007
New Revision: 569278

URL: http://svn.apache.org/viewvc?rev=569278&view=rev
Log:
Add detection mechanism for proper namespace handling. If the current sax implementation has
bugs a workaround is installed (fixes FELIX-347)

Added:
    felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/IOUtils.java   (with
props)
Modified:
    felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/ComponentDescriptorIO.java
    felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/MetaTypeIO.java

Modified: felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/ComponentDescriptorIO.java
URL: http://svn.apache.org/viewvc/felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/ComponentDescriptorIO.java?rev=569278&r1=569277&r2=569278&view=diff
==============================================================================
--- felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/ComponentDescriptorIO.java
(original)
+++ felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/ComponentDescriptorIO.java
Fri Aug 24 00:27:04 2007
@@ -19,21 +19,11 @@
 package org.apache.felix.scrplugin.xml;
 
 import java.io.File;
-import java.io.FileReader;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.util.Iterator;
 import java.util.StringTokenizer;
 
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
 import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.sax.SAXResult;
-import javax.xml.transform.sax.SAXTransformerFactory;
-import javax.xml.transform.sax.TransformerHandler;
-import javax.xml.transform.stream.StreamResult;
-import javax.xml.transform.stream.StreamSource;
 
 import org.apache.felix.scrplugin.om.Component;
 import org.apache.felix.scrplugin.om.Components;
@@ -87,15 +77,11 @@
 
     private static final String INTERFACE_QNAME = PREFIX + ':' + INTERFACE;
 
-    private static final SAXTransformerFactory FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
-
     public static Components read(File file)
     throws MojoExecutionException {
         try {
-            final Transformer transformer = FACTORY.newTransformer();
             final XmlHandler xmlHandler = new XmlHandler();
-            transformer.transform(new StreamSource(new FileReader(file)),
-                    new SAXResult(xmlHandler));
+            IOUtils.parse(file, xmlHandler);
             return xmlHandler.components;
         } catch (TransformerException e) {
             throw new MojoExecutionException("Unable to read xml.", e);
@@ -113,16 +99,7 @@
     public static void write(Components components, File file)
     throws MojoExecutionException {
         try {
-            FileWriter writer = new FileWriter(file);
-            final TransformerHandler transformerHandler = FACTORY.newTransformerHandler();
-            final Transformer transformer = transformerHandler.getTransformer();
-            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
-            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
-            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
-            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
-            transformerHandler.setResult(new StreamResult(writer));
-
-            generateXML(components, transformerHandler);
+            generateXML(components, IOUtils.getSerializer(file));
         } catch (TransformerException e) {
             throw new MojoExecutionException("Unable to write xml to " + file, e);
         } catch (SAXException e) {

Added: felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/IOUtils.java
URL: http://svn.apache.org/viewvc/felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/IOUtils.java?rev=569278&view=auto
==============================================================================
--- felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/IOUtils.java (added)
+++ felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/IOUtils.java Fri Aug
24 00:27:04 2007
@@ -0,0 +1,378 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.scrplugin.xml;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * Utility class for xml/sax handling.
+ * It provides support for "older" sax implementations (like the default one shipped with
JDK 1.4.2)
+ * which have bugs in the namespace handling.
+ */
+public class IOUtils {
+
+    /** The transformer factory. */
+    private static final SAXTransformerFactory FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
+
+    /** The URI for xml namespaces */
+    private static final String XML_NAMESPACE_URI = "http://www.w3.org/XML/1998/namespace";
+
+    /**
+     * Parse a file and send the sax events to the content handler.
+     * @param file
+     * @param handler
+     * @throws IOException
+     * @throws TransformerException
+     */
+    public static final void parse(File file, ContentHandler handler)
+    throws IOException, TransformerException {
+        final Transformer transformer = FACTORY.newTransformer();
+        transformer.transform(new StreamSource(new FileReader(file)),
+                new SAXResult(handler));
+    }
+
+    public static ContentHandler getSerializer(File file)
+    throws IOException, TransformerException {
+        final FileWriter writer = new FileWriter(file);
+
+        final TransformerHandler transformerHandler = FACTORY.newTransformerHandler();
+        final Transformer transformer = transformerHandler.getTransformer();
+
+        final Properties format = new Properties();
+        format.put(OutputKeys.METHOD, "xml");
+        format.put(OutputKeys.OMIT_XML_DECLARATION, "no");
+        format.put(OutputKeys.ENCODING, "UTF-8");
+        format.put(OutputKeys.INDENT, "yes");
+        transformer.setOutputProperties(format);
+
+        transformerHandler.setResult(new StreamResult(writer));
+
+        try {
+            if ( needsNamespacesAsAttributes(format) ) {
+                return new NamespaceAsAttributes(transformerHandler);
+            }
+        } catch (SAXException se) {
+            throw new TransformerException("Unable to detect of namespace support for sax
works properly.", se);
+        }
+        return transformerHandler;
+    }
+
+    /**
+     * Checks if the used Trax implementation correctly handles namespaces set using
+     * <code>startPrefixMapping()</code>, but wants them also as 'xmlns:' attributes.
+     * <p>
+     * The check consists in sending SAX events representing a minimal namespaced document
+     * with namespaces defined only with calls to <code>startPrefixMapping</code>
(no
+     * xmlns:xxx attributes) and check if they are present in the resulting text.
+     */
+    protected static boolean needsNamespacesAsAttributes(Properties format)
+    throws TransformerException, SAXException {
+        // Serialize a minimal document to check how namespaces are handled.
+        final StringWriter writer = new StringWriter();
+
+        final String uri = "namespaceuri";
+        final String prefix = "nsp";
+        final String check = "xmlns:" + prefix + "='" + uri + "'";
+
+        final TransformerHandler handler = FACTORY.newTransformerHandler();
+
+        handler.getTransformer().setOutputProperties(format);
+        handler.setResult(new StreamResult(writer));
+
+        // Output a single element
+        handler.startDocument();
+        handler.startPrefixMapping(prefix, uri);
+        handler.startElement(uri, "element", "element", new AttributesImpl());
+        handler.endElement(uri, "element", "element");
+        handler.endPrefixMapping(prefix);
+        handler.endDocument();
+
+        final String text = writer.toString();
+
+        // Check if the namespace is there (replace " by ' to be sure of what we search in)
+        boolean needsIt = (text.replace('"', '\'').indexOf(check) == -1);
+
+        return needsIt;
+    }
+
+    /**
+     * A pipe that ensures that all namespace prefixes are also present as
+     * 'xmlns:' attributes. This used to circumvent Xalan's serialization behaviour
+     * which is to ignore namespaces if they're not present as 'xmlns:xxx' attributes.
+     */
+    public static class NamespaceAsAttributes implements ContentHandler {
+
+        /** The wrapped content handler. */
+        private final ContentHandler contentHandler;
+
+        /**
+         * The prefixes of startPrefixMapping() declarations for the coming element.
+         */
+        private List prefixList = new ArrayList();
+
+        /**
+         * The URIs of startPrefixMapping() declarations for the coming element.
+         */
+        private List uriList = new ArrayList();
+
+        /**
+         * Maps of URI<->prefix mappings. Used to work around a bug in the Xalan
+         * serializer.
+         */
+        private Map uriToPrefixMap = new HashMap();
+        private Map prefixToUriMap = new HashMap();
+
+        /**
+         * True if there has been some startPrefixMapping() for the coming element.
+         */
+        private boolean hasMappings = false;
+
+        public NamespaceAsAttributes(ContentHandler ch) {
+            this.contentHandler = ch;
+        }
+
+        public void startDocument() throws SAXException {
+            // Cleanup
+            this.uriToPrefixMap.clear();
+            this.prefixToUriMap.clear();
+            clearMappings();
+            this.contentHandler.startDocument();
+        }
+
+        /**
+         * Track mappings to be able to add <code>xmlns:</code> attributes
+         * in <code>startElement()</code>.
+         */
+        public void startPrefixMapping(String prefix, String uri) throws SAXException {
+            // Store the mappings to reconstitute xmlns:attributes
+            // except prefixes starting with "xml": these are reserved
+            // VG: (uri != null) fixes NPE in startElement
+            if (uri != null && !prefix.startsWith("xml")) {
+                this.hasMappings = true;
+                this.prefixList.add(prefix);
+                this.uriList.add(uri);
+
+                // append the prefix colon now, in order to save concatenations later, but
+                // only for non-empty prefixes.
+                if (prefix.length() > 0) {
+                    this.uriToPrefixMap.put(uri, prefix + ":");
+                } else {
+                    this.uriToPrefixMap.put(uri, prefix);
+                }
+
+                this.prefixToUriMap.put(prefix, uri);
+            }
+            this.contentHandler.startPrefixMapping(prefix, uri);
+        }
+
+        /**
+         * Ensure all namespace declarations are present as <code>xmlns:</code>
attributes
+         * and add those needed before calling superclass. This is a workaround for a Xalan
bug
+         * (at least in version 2.0.1) : <code>org.apache.xalan.serialize.SerializerToXML</code>
+         * ignores <code>start/endPrefixMapping()</code>.
+         */
+        public void startElement(String eltUri, String eltLocalName, String eltQName, Attributes
attrs)
+                throws SAXException {
+
+            // try to restore the qName. The map already contains the colon
+            if (null != eltUri && eltUri.length() != 0 && this.uriToPrefixMap.containsKey(eltUri))
{
+                eltQName = this.uriToPrefixMap.get(eltUri) + eltLocalName;
+            }
+            if (this.hasMappings) {
+                // Add xmlns* attributes where needed
+
+                // New Attributes if we have to add some.
+                AttributesImpl newAttrs = null;
+
+                int mappingCount = this.prefixList.size();
+                int attrCount = attrs.getLength();
+
+                for (int mapping = 0; mapping < mappingCount; mapping++) {
+
+                    // Build infos for this namespace
+                    String uri = (String) this.uriList.get(mapping);
+                    String prefix = (String) this.prefixList.get(mapping);
+                    String qName = prefix.length() == 0 ? "xmlns" : ("xmlns:" + prefix);
+
+                    // Search for the corresponding xmlns* attribute
+                    boolean found = false;
+                    for (int attr = 0; attr < attrCount; attr++) {
+                        if (qName.equals(attrs.getQName(attr))) {
+                            // Check if mapping and attribute URI match
+                            if (!uri.equals(attrs.getValue(attr))) {
+                                throw new SAXException("URI in prefix mapping and attribute
do not match");
+                            }
+                            found = true;
+                            break;
+                        }
+                    }
+
+                    if (!found) {
+                        // Need to add this namespace
+                        if (newAttrs == null) {
+                            // Need to test if attrs is empty or we go into an infinite loop...
+                            // Well know SAX bug which I spent 3 hours to remind of :-(
+                            if (attrCount == 0) {
+                                newAttrs = new AttributesImpl();
+                            } else {
+                                newAttrs = new AttributesImpl(attrs);
+                            }
+                        }
+
+                        if (prefix.length() == 0) {
+                            newAttrs.addAttribute(XML_NAMESPACE_URI, "xmlns", "xmlns", "CDATA",
uri);
+                        } else {
+                            newAttrs.addAttribute(XML_NAMESPACE_URI, prefix, qName, "CDATA",
uri);
+                        }
+                    }
+                } // end for mapping
+
+                // Cleanup for the next element
+                clearMappings();
+
+                // Start element with new attributes, if any
+                this.contentHandler.startElement(eltUri, eltLocalName, eltQName, newAttrs
== null ? attrs : newAttrs);
+            } else {
+                // Normal job
+                this.contentHandler.startElement(eltUri, eltLocalName, eltQName, attrs);
+            }
+        }
+
+
+        /**
+         * Receive notification of the end of an element.
+         * Try to restore the element qName.
+         */
+        public void endElement(String eltUri, String eltLocalName, String eltQName) throws
SAXException {
+            // try to restore the qName. The map already contains the colon
+            if (null != eltUri && eltUri.length() != 0 && this.uriToPrefixMap.containsKey(eltUri))
{
+                eltQName = this.uriToPrefixMap.get(eltUri) + eltLocalName;
+            }
+            this.contentHandler.endElement(eltUri, eltLocalName, eltQName);
+        }
+
+        /**
+         * End the scope of a prefix-URI mapping:
+         * remove entry from mapping tables.
+         */
+        public void endPrefixMapping(String prefix) throws SAXException {
+            // remove mappings for xalan-bug-workaround.
+            // Unfortunately, we're not passed the uri, but the prefix here,
+            // so we need to maintain maps in both directions.
+            if (this.prefixToUriMap.containsKey(prefix)) {
+                this.uriToPrefixMap.remove(this.prefixToUriMap.get(prefix));
+                this.prefixToUriMap.remove(prefix);
+            }
+
+            if (hasMappings) {
+                // most of the time, start/endPrefixMapping calls have an element event between
them,
+                // which will clear the hasMapping flag and so this code will only be executed
in the
+                // rather rare occasion when there are start/endPrefixMapping calls with
no element
+                // event in between. If we wouldn't remove the items from the prefixList
and uriList here,
+                // the namespace would be incorrectly declared on the next element following
the
+                // endPrefixMapping call.
+                int pos = prefixList.lastIndexOf(prefix);
+                if (pos != -1) {
+                    prefixList.remove(pos);
+                    uriList.remove(pos);
+                }
+            }
+
+            this.contentHandler.endPrefixMapping(prefix);
+        }
+
+        /**
+         * @see org.xml.sax.ContentHandler#endDocument()
+         */
+        public void endDocument() throws SAXException {
+            // Cleanup
+            this.uriToPrefixMap.clear();
+            this.prefixToUriMap.clear();
+            clearMappings();
+            this.contentHandler.endDocument();
+        }
+
+        private void clearMappings() {
+            this.hasMappings = false;
+            this.prefixList.clear();
+            this.uriList.clear();
+        }
+
+        /**
+         * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+         */
+        public void characters(char[] ch, int start, int length) throws SAXException {
+            contentHandler.characters(ch, start, length);
+        }
+
+        /**
+         * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
+         */
+        public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException
{
+            contentHandler.ignorableWhitespace(ch, start, length);
+        }
+
+        /**
+         * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String)
+         */
+        public void processingInstruction(String target, String data) throws SAXException
{
+            contentHandler.processingInstruction(target, data);
+        }
+
+        /**
+         * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
+         */
+        public void setDocumentLocator(Locator locator) {
+            contentHandler.setDocumentLocator(locator);
+        }
+
+        /**
+         * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String)
+         */
+        public void skippedEntity(String name) throws SAXException {
+            contentHandler.skippedEntity(name);
+        }
+    }
+
+}

Propchange: felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/IOUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/IOUtils.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Modified: felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/MetaTypeIO.java
URL: http://svn.apache.org/viewvc/felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/MetaTypeIO.java?rev=569278&r1=569277&r2=569278&view=diff
==============================================================================
--- felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/MetaTypeIO.java (original)
+++ felix/trunk/scrplugin/src/main/java/org/apache/felix/scrplugin/xml/MetaTypeIO.java Fri
Aug 24 00:27:04 2007
@@ -19,18 +19,11 @@
 package org.apache.felix.scrplugin.xml;
 
 import java.io.File;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.util.Iterator;
 import java.util.Map;
 
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
 import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.sax.SAXTransformerFactory;
-import javax.xml.transform.sax.TransformerHandler;
-import javax.xml.transform.stream.StreamResult;
 
 import org.apache.felix.scrplugin.om.metatype.AttributeDefinition;
 import org.apache.felix.scrplugin.om.metatype.Designate;
@@ -51,8 +44,6 @@
  */
 public class MetaTypeIO {
 
-    private static final SAXTransformerFactory FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
-
     public static final String NAMESPACE_URI = "http://www.osgi.org/xmlns/metatype/v1.0.0";
 
     public static final String PREFIX = "metatype";
@@ -75,16 +66,7 @@
     public static void write(MetaData metaData, File file)
     throws MojoExecutionException {
         try {
-            FileWriter writer = new FileWriter(file);
-            final TransformerHandler transformerHandler = FACTORY.newTransformerHandler();
-            final Transformer transformer = transformerHandler.getTransformer();
-            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
-            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
-            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
-            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
-            transformerHandler.setResult(new StreamResult(writer));
-
-            generateXML(metaData, transformerHandler);
+            generateXML(metaData, IOUtils.getSerializer(file));
         } catch (TransformerException e) {
             throw new MojoExecutionException("Unable to write xml to " + file, e);
         } catch (SAXException e) {



Mime
View raw message