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