001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package org.apache.geronimo.schema;
019    
020    import java.util.HashMap;
021    import java.util.Map;
022    
023    import javax.xml.namespace.QName;
024    
025    import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
026    import org.apache.xmlbeans.SchemaType;
027    import org.apache.xmlbeans.XmlCursor;
028    import org.apache.xmlbeans.XmlDocumentProperties;
029    import org.apache.xmlbeans.XmlException;
030    import org.apache.xmlbeans.XmlObject;
031    
032    /**
033     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
034     */
035    public class SchemaConversionUtils {
036        public static final String J2EE_NAMESPACE = "http://java.sun.com/xml/ns/j2ee";
037        public static final String JAVAEE_NAMESPACE = "http://java.sun.com/xml/ns/javaee";
038    
039        static final String GERONIMO_NAMING_NAMESPACE = "http://geronimo.apache.org/xml/ns/naming-1.2";
040        private static final String GERONIMO_SECURITY_NAMESPACE = "http://geronimo.apache.org/xml/ns/security-2.0";
041        private static final String GERONIMO_SERVICE_NAMESPACE = "http://geronimo.apache.org/xml/ns/deployment-1.2";
042        private static final String JPA_PERSISTENCE_NAMESPACE = "http://java.sun.com/xml/ns/persistence";
043    
044        private static final Map<String, ElementConverter> GERONIMO_SCHEMA_CONVERSIONS = new HashMap<String, ElementConverter>();
045    
046        static {
047    
048            GERONIMO_SCHEMA_CONVERSIONS.put("gbean-ref", new NamespaceElementConverter(GERONIMO_NAMING_NAMESPACE));
049            GERONIMO_SCHEMA_CONVERSIONS.put("ejb-ref", new NamespaceElementConverter(GERONIMO_NAMING_NAMESPACE));
050            GERONIMO_SCHEMA_CONVERSIONS.put("ejb-local-ref", new NamespaceElementConverter(GERONIMO_NAMING_NAMESPACE));
051            GERONIMO_SCHEMA_CONVERSIONS.put("service-ref", new NamespaceElementConverter(GERONIMO_NAMING_NAMESPACE));
052            GERONIMO_SCHEMA_CONVERSIONS.put("resource-ref", new NamespaceElementConverter(GERONIMO_NAMING_NAMESPACE));
053            GERONIMO_SCHEMA_CONVERSIONS.put("resource-env-ref", new NamespaceElementConverter(GERONIMO_NAMING_NAMESPACE));
054            GERONIMO_SCHEMA_CONVERSIONS.put("message-destination", new NamespaceElementConverter(GERONIMO_NAMING_NAMESPACE));
055            GERONIMO_SCHEMA_CONVERSIONS.put("cmp-connection-factory", new NamespaceElementConverter(GERONIMO_NAMING_NAMESPACE));
056            GERONIMO_SCHEMA_CONVERSIONS.put("workmanager", new NamespaceElementConverter(GERONIMO_NAMING_NAMESPACE));
057            GERONIMO_SCHEMA_CONVERSIONS.put("resource-adapter", new NamespaceElementConverter(GERONIMO_NAMING_NAMESPACE));
058            GERONIMO_SCHEMA_CONVERSIONS.put("web-container", new NamespaceElementConverter(GERONIMO_NAMING_NAMESPACE));
059    
060            GERONIMO_SCHEMA_CONVERSIONS.put("security", new SecurityElementConverter());
061            GERONIMO_SCHEMA_CONVERSIONS.put("default-subject", new NamespaceElementConverter(GERONIMO_SECURITY_NAMESPACE));
062    
063            GERONIMO_SCHEMA_CONVERSIONS.put("gbean", new GBeanElementConverter());
064            GERONIMO_SCHEMA_CONVERSIONS.put("environment", new NamespaceElementConverter(GERONIMO_SERVICE_NAMESPACE));
065            GERONIMO_SCHEMA_CONVERSIONS.put("client-environment", new NamespaceElementConverter(GERONIMO_SERVICE_NAMESPACE));
066            GERONIMO_SCHEMA_CONVERSIONS.put("server-environment", new NamespaceElementConverter(GERONIMO_SERVICE_NAMESPACE));
067            GERONIMO_SCHEMA_CONVERSIONS.put("persistence", new NamespaceElementConverter(JPA_PERSISTENCE_NAMESPACE));
068        }
069    
070        private SchemaConversionUtils() {
071        }
072    
073        public static void registerNamespaceConversions(Map conversions) {
074            GERONIMO_SCHEMA_CONVERSIONS.putAll(conversions);
075        }
076    
077        public static void convertToGeronimoSubSchemas(XmlCursor cursor) {
078            cursor.toStartDoc();
079            XmlCursor end = cursor.newCursor();
080            try {
081                while (cursor.hasNextToken()) {
082                    convertSingleElementToGeronimoSubSchemas(cursor, end);
083                    cursor.toNextToken();
084                }
085            } finally {
086                end.dispose();
087            }
088        }
089    
090        public static boolean convertSingleElementToGeronimoSubSchemas(XmlCursor cursor, XmlCursor end) {
091            if (cursor.isStart()) {
092                String localName = cursor.getName().getLocalPart();
093                ElementConverter converter = (ElementConverter) GERONIMO_SCHEMA_CONVERSIONS.get(localName);
094                if (converter != null) {
095                    converter.convertElement(cursor, end);
096                    return true;
097                }
098                return false;
099            }
100            //you should only call this method at a start token
101            return false;
102        }
103    
104        public static XmlObject fixGeronimoSchema(XmlObject rawPlan, QName desiredElement, SchemaType desiredType) throws XmlException {
105            XmlCursor cursor = rawPlan.newCursor();
106            try {
107                if (findNestedElement(cursor, desiredElement)) {
108                    cursor.push();
109                    convertToGeronimoSubSchemas(cursor);
110                    cursor.pop();
111                    XmlObject temp = cursor.getObject();
112    
113                    XmlObject result = temp.changeType(desiredType);
114                    if (result == null || result.schemaType() != desiredType) {
115                        result = temp.copy().changeType(desiredType);
116                    }
117                    XmlBeansUtil.validateDD(result);
118                    return result;
119                } else {
120                    return null;
121                }
122            } finally {
123                cursor.dispose();
124            }
125        }
126    
127        public static XmlObject getNestedObject(XmlObject xmlObject, QName desiredElement) {
128            XmlCursor cursor = xmlObject.newCursor();
129            try {
130                if (findNestedElement(cursor, desiredElement)) {
131                    XmlObject child = cursor.getObject();
132                    //The copy seems to be needed to make the type change work for some documents!
133                    return child.copy();
134                }
135            } finally {
136                cursor.dispose();
137            }
138            throw new IllegalArgumentException("xmlobject did not have desired element: " + desiredElement + "/n" + xmlObject);
139        }
140    
141        public static boolean findNestedElement(XmlCursor cursor, QName desiredElement) {
142            while (cursor.hasNextToken()) {
143                if (cursor.isStart()) {
144                    QName element = cursor.getName();
145                    if (element.equals(desiredElement)) {
146                        return true;
147                    }
148                }
149                cursor.toNextToken();
150            }
151            return false;
152        }
153    
154        public static boolean findNestedElement(XmlCursor cursor, String desiredElement) {
155            while (cursor.hasNextToken()) {
156                if (cursor.isStart()) {
157                    String element = cursor.getName().getLocalPart();
158                    if (element.equals(desiredElement)) {
159                        return true;
160                    }
161                }
162                cursor.toNextToken();
163            }
164            return false;
165        }
166    
167        public static XmlObject getNestedObjectAsType(XmlObject xmlObject, QName desiredElement, SchemaType type) {
168            XmlCursor cursor = xmlObject.newCursor();
169            try {
170                if (findNestedElement(cursor, desiredElement)) {
171                    XmlObject child = cursor.getObject();
172                    //The copy seems to be needed to make the type change work for some documents!
173                    XmlObject result = child.copy().changeType(type);
174                    assert result.schemaType() == type;
175                    return result;
176                }
177            } finally {
178                cursor.dispose();
179            }
180            throw new IllegalArgumentException("xmlobject did not have desired element: " + desiredElement + "\n" + xmlObject);
181        }
182    
183    
184        public static boolean convertToSchema(XmlCursor cursor, String namespace, String schemaLocationURL, String version) {
185            //remove dtd
186            XmlDocumentProperties xmlDocumentProperties = cursor.documentProperties();
187            xmlDocumentProperties.remove(XmlDocumentProperties.DOCTYPE_NAME);
188            xmlDocumentProperties.remove(XmlDocumentProperties.DOCTYPE_PUBLIC_ID);
189            xmlDocumentProperties.remove(XmlDocumentProperties.DOCTYPE_SYSTEM_ID);
190            //convert namespace
191            boolean isFirstStart = true;
192            while (cursor.hasNextToken()) {
193                if (cursor.isStart()) {
194                    if (namespace.equals(cursor.getName().getNamespaceURI())) {
195                        //already has correct schema, exit
196                        return false;
197                    }
198                    cursor.setName(new QName(namespace, cursor.getName().getLocalPart()));
199                    cursor.toNextToken();
200                    if (isFirstStart) {
201                        cursor.insertNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
202                        cursor.insertAttributeWithValue(new QName("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation", "xsi"), namespace + "  " + schemaLocationURL);
203                        cursor.insertAttributeWithValue(new QName("version"), version);
204                        isFirstStart = false;
205                    }
206                } else {
207                    cursor.toNextToken();
208                }
209            }
210            return true;
211        }
212    
213        public static boolean convertSchemaVersion (XmlCursor cursor, String namespace, String schemaLocationURL, String version) {
214            boolean isFirstStart = true;
215    
216    
217            while (cursor.hasNextToken()) {
218                if (cursor.isStart()) {
219                    if (isFirstStart) {
220                        //HACK to work around digester's difficulty with namespaces
221                        if (cursor.getAttributeText(new QName("xmlns")) != null) {
222                            cursor.removeAttribute(new QName("xmlns"));
223                        }
224                        //if we are at the first element in the document, reset the version number ...
225                        cursor.setAttributeText(new QName("version"), version);
226                        //... and also set the xsi:schemaLocation
227                        cursor.setAttributeText(new QName("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation", "xsi"), namespace + "  "+schemaLocationURL);
228                        isFirstStart = false;
229                    }
230                    //convert namespace of each starting element
231                    cursor.setName(new QName(namespace, cursor.getName().getLocalPart()));
232                    cursor.toNextToken();
233    
234                } else {
235                    cursor.toNextToken();
236                }
237            }
238    
239    
240            return true;
241        }
242    
243        /**
244         * Reorders elements to match descriptionGroup
245         *
246         * @param namespace
247         * @param cursor XmlCursor positioned at first element of "group" to be reordered
248         */
249        public static void convertToDescriptionGroup(String namespace, XmlCursor cursor, XmlCursor moveable) {
250            moveable.toCursor(cursor);
251            moveElements("description", namespace, moveable, cursor);
252            moveElements("display-name", namespace, moveable, cursor);
253            moveElements("icon", namespace, moveable, cursor);
254        }
255    
256        public static void convertToTldTag(String namespace, XmlCursor cursor, XmlCursor moveable) {
257            moveable.toCursor(cursor);
258            moveElements("description", namespace, moveable, cursor);
259            moveElements("display-name", namespace, moveable, cursor);
260            moveElements("icon", namespace, moveable, cursor);
261            moveElements("name", namespace, moveable, cursor);
262            moveElements("tag-class", namespace, moveable, cursor);
263            moveElements("tei-class", namespace, moveable, cursor);
264            moveElements("body-content", namespace, moveable, cursor);
265            moveElements("variable", namespace, moveable, cursor);
266            moveElements("attribute", namespace, moveable, cursor);
267            moveElements("dynamic-attributes", namespace, moveable, cursor);
268            moveElements("example", namespace, moveable, cursor);
269            moveElements("tag-extension", namespace, moveable, cursor);
270        }
271    
272        public static void convertToTldAttribute(String namespace, XmlCursor cursor, XmlCursor moveable) {
273            moveable.toCursor(cursor);
274            moveElements("description", namespace, moveable, cursor);
275            moveElements("name", namespace, moveable, cursor);
276            moveElements("required", namespace, moveable, cursor);
277            moveElements("rtexprvalue", namespace, moveable, cursor);
278            moveElements("type", namespace, moveable, cursor);
279            moveElements("fragment", namespace, moveable, cursor);
280        }
281    
282        public static void convertToTldInitParam(String namespace, XmlCursor cursor, XmlCursor moveable) {
283            moveable.toCursor(cursor);
284            moveElements("description", namespace, moveable, cursor);
285            moveElements("param-name", namespace, moveable, cursor);
286            moveElements("param-value", namespace, moveable, cursor);
287        }
288    
289        public static void convertToTldValidator(String namespace, XmlCursor cursor, XmlCursor moveable) {
290            moveable.toCursor(cursor);
291            moveElements("description", namespace, moveable, cursor);
292            moveElements("validator-class", namespace, moveable, cursor);
293            moveElements("init-param", namespace, moveable, cursor);
294            
295            do {
296                String name = cursor.getName().getLocalPart();
297                if ("init-param".equals(name)) {
298                    cursor.push();
299                    cursor.toFirstChild();
300                    convertToTldInitParam(namespace, cursor, moveable);
301                    cursor.pop();
302                }
303            } while (cursor.toPrevSibling());      
304        }
305    
306        public static void convertToTldVariable(String namespace, XmlCursor cursor, XmlCursor moveable) {
307            moveable.toCursor(cursor);
308            moveElements("description", namespace, moveable, cursor);
309            moveElements("name-given", namespace, moveable, cursor);
310            moveElements("name-from-attribute", namespace, moveable, cursor);
311            moveElements("variable-class", namespace, moveable, cursor);
312            moveElements("declare", namespace, moveable, cursor);
313            moveElements("scope", namespace, moveable, cursor);
314        }
315    
316        public static void convertToJNDIEnvironmentRefsGroup(String namespace, XmlCursor cursor, XmlCursor moveable) {       
317            moveElements("env-entry", namespace, moveable, cursor);
318            moveElements("ejb-ref", namespace, moveable, cursor);
319            moveElements("ejb-local-ref", namespace, moveable, cursor);
320            moveElements("resource-ref", namespace, moveable, cursor);
321            moveElements("resource-env-ref", namespace, moveable, cursor);
322            moveElements("message-destination-ref", namespace, moveable, cursor);
323                    
324            do {
325                String name = cursor.getName().getLocalPart();
326                if ("env-entry".equals(name)) {
327                    cursor.push();
328                    cursor.toFirstChild();
329                    convertToDescriptionGroup(namespace, cursor, moveable);
330                    convertToEnvEntryGroup(namespace, cursor, moveable);
331                    cursor.pop();
332                }
333            } while (cursor.toPrevSibling());      
334        }
335    
336        public static void convertToEnvEntryGroup(String namespace, XmlCursor cursor, XmlCursor moveable) {
337            moveElements("env-entry-name", namespace, moveable, cursor);
338            moveElements("env-entry-type", namespace, moveable, cursor);
339            moveElements("env-entry-value", namespace, moveable, cursor);
340        }
341    
342        private static void moveElements(String localName, String namespace, XmlCursor moveable, XmlCursor toHere) {
343            QName name = new QName(namespace, localName);
344            //skip elements already in the correct order.
345            while (name.equals(toHere.getName()) && toHere.toNextSibling()) {
346            }
347            moveable.toCursor(toHere);
348            while (moveable.toNextSibling(name)) {
349                moveable.moveXml(toHere);
350            }
351        }
352    
353    }