001    /**
002     *
003     *  Licensed to the Apache Software Foundation (ASF) under one or more
004     *  contributor license agreements.  See the NOTICE file distributed with
005     *  this work for additional information regarding copyright ownership.
006     *  The ASF licenses this file to You under the Apache License, Version 2.0
007     *  (the "License"); you may not use this file except in compliance with
008     *  the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *  Unless required by applicable law or agreed to in writing, software
013     *  distributed under the License is distributed on an "AS IS" BASIS,
014     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     *  See the License for the specific language governing permissions and
016     *  limitations under the License.
017     */
018    
019    package org.apache.geronimo.schema;
020    
021    import java.util.HashMap;
022    import java.util.Map;
023    
024    import javax.xml.namespace.QName;
025    
026    import org.apache.geronimo.xbeans.j2ee.ApplicationClientDocument;
027    import org.apache.geronimo.xbeans.j2ee.ApplicationDocument;
028    import org.apache.geronimo.xbeans.j2ee.ConnectorDocument;
029    import org.apache.geronimo.xbeans.j2ee.EjbJarDocument;
030    import org.apache.geronimo.xbeans.j2ee.WebAppDocument;
031    import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
032    import org.apache.xmlbeans.SchemaType;
033    import org.apache.xmlbeans.XmlCursor;
034    import org.apache.xmlbeans.XmlDocumentProperties;
035    import org.apache.xmlbeans.XmlException;
036    import org.apache.xmlbeans.XmlObject;
037    
038    /**
039     * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
040     */
041    public class SchemaConversionUtils {
042        public static final String J2EE_NAMESPACE = "http://java.sun.com/xml/ns/j2ee";
043        public static final String JAVAEE_NAMESPACE = "http://java.sun.com/xml/ns/javaee";
044    
045        static final String GERONIMO_NAMING_NAMESPACE = "http://geronimo.apache.org/xml/ns/naming-1.2";
046        private static final String GERONIMO_SECURITY_NAMESPACE = "http://geronimo.apache.org/xml/ns/security-1.2";
047        private static final String GERONIMO_SERVICE_NAMESPACE = "http://geronimo.apache.org/xml/ns/deployment-1.2";
048    
049        private static final Map GERONIMO_SCHEMA_CONVERSIONS = new HashMap();
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-principal", 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        }
073    
074        private SchemaConversionUtils() {
075        }
076    
077        public static void registerNamespaceConversions(Map conversions) {
078            GERONIMO_SCHEMA_CONVERSIONS.putAll(conversions);
079        }
080    
081        public static void convertToGeronimoSubSchemas(XmlCursor cursor) {
082            cursor.toStartDoc();
083            XmlCursor end = cursor.newCursor();
084            try {
085                while (cursor.hasNextToken()) {
086                    convertSingleElementToGeronimoSubSchemas(cursor, end);
087                    cursor.toNextToken();
088                }
089            } finally {
090                end.dispose();
091            }
092        }
093    
094        public static boolean convertSingleElementToGeronimoSubSchemas(XmlCursor cursor, XmlCursor end) {
095            if (cursor.isStart()) {
096                String localName = cursor.getName().getLocalPart();
097                ElementConverter converter = (ElementConverter) GERONIMO_SCHEMA_CONVERSIONS.get(localName);
098                if (converter != null) {
099                    converter.convertElement(cursor, end);
100                    return true;
101                }
102                return false;
103            }
104            //you should only call this method at a start token
105            return false;
106        }
107    
108        public static XmlObject fixGeronimoSchema(XmlObject rawPlan, QName desiredElement, SchemaType desiredType) throws XmlException {
109            XmlCursor cursor = rawPlan.newCursor();
110            try {
111                if (findNestedElement(cursor, desiredElement)) {
112                    cursor.push();
113                    convertToGeronimoSubSchemas(cursor);
114                    cursor.pop();
115                    XmlObject temp = cursor.getObject();
116    
117                    XmlObject result = temp.changeType(desiredType);
118                    if (result == null || result.schemaType() != desiredType) {
119                        result = temp.copy().changeType(desiredType);
120                    }
121                    XmlBeansUtil.validateDD(result);
122                    return result;
123                } else {
124                    return null;
125                }
126            } finally {
127                cursor.dispose();
128            }
129        }
130    
131        public static XmlObject getNestedObject(XmlObject xmlObject, QName desiredElement) {
132            XmlCursor cursor = xmlObject.newCursor();
133            try {
134                if (findNestedElement(cursor, desiredElement)) {
135                    XmlObject child = cursor.getObject();
136                    //The copy seems to be needed to make the type change work for some documents!
137                    return child.copy();
138                }
139            } finally {
140                cursor.dispose();
141            }
142            throw new IllegalArgumentException("xmlobject did not have desired element: " + desiredElement + "/n" + xmlObject);
143        }
144    
145        public static boolean findNestedElement(XmlCursor cursor, QName desiredElement) {
146            while (cursor.hasNextToken()) {
147                if (cursor.isStart()) {
148                    QName element = cursor.getName();
149                    if (element.equals(desiredElement)) {
150                        return true;
151                    }
152                }
153                cursor.toNextToken();
154            }
155            return false;
156        }
157    
158        public static boolean findNestedElement(XmlCursor cursor, String desiredElement) {
159            while (cursor.hasNextToken()) {
160                if (cursor.isStart()) {
161                    String element = cursor.getName().getLocalPart();
162                    if (element.equals(desiredElement)) {
163                        return true;
164                    }
165                }
166                cursor.toNextToken();
167            }
168            return false;
169        }
170    
171        public static XmlObject getNestedObjectAsType(XmlObject xmlObject, QName desiredElement, SchemaType type) {
172            XmlCursor cursor = xmlObject.newCursor();
173            try {
174                if (findNestedElement(cursor, desiredElement)) {
175                    XmlObject child = cursor.getObject();
176                    //The copy seems to be needed to make the type change work for some documents!
177                    XmlObject result = child.copy().changeType(type);
178                    assert result.schemaType() == type;
179                    return result;
180                }
181            } finally {
182                cursor.dispose();
183            }
184            throw new IllegalArgumentException("xmlobject did not have desired element: " + desiredElement + "\n" + xmlObject);
185        }
186    
187    
188        public static boolean convertToSchema(XmlCursor cursor, String namespace, String schemaLocationURL, String version) {
189            //remove dtd
190            XmlDocumentProperties xmlDocumentProperties = cursor.documentProperties();
191            xmlDocumentProperties.remove(XmlDocumentProperties.DOCTYPE_NAME);
192            xmlDocumentProperties.remove(XmlDocumentProperties.DOCTYPE_PUBLIC_ID);
193            xmlDocumentProperties.remove(XmlDocumentProperties.DOCTYPE_SYSTEM_ID);
194            //convert namespace
195            boolean isFirstStart = true;
196            while (cursor.hasNextToken()) {
197                if (cursor.isStart()) {
198                    if (namespace.equals(cursor.getName().getNamespaceURI())) {
199                        //already has correct schema, exit
200                        return false;
201                    }
202                    cursor.setName(new QName(namespace, cursor.getName().getLocalPart()));
203                    cursor.toNextToken();
204                    if (isFirstStart) {
205                        cursor.insertNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
206                        cursor.insertAttributeWithValue(new QName("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation", "xsi"), namespace + schemaLocationURL);
207                        cursor.insertAttributeWithValue(new QName("version"), version);
208                        isFirstStart = false;
209                    }
210                } else {
211                    cursor.toNextToken();
212                }
213            }
214            return true;
215        }
216    
217        public static boolean convertSchemaVersion (XmlCursor cursor, String namespace, String schemaLocationURL, String version) {
218            boolean isFirstStart = true;
219    
220    
221            while (cursor.hasNextToken()) {
222                if (cursor.isStart()) {
223                    //convert namespace of each starting element
224                    cursor.setName(new QName(namespace, cursor.getName().getLocalPart()));
225                    if (isFirstStart) {
226                        //if we are at the first element in the document, reset the version number ...
227                        cursor.setAttributeText(new QName("version"), version);
228                        //... and also set the xsi:schemaLocation
229                        cursor.setAttributeText(new QName("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation", "xsi"), namespace + "  "+schemaLocationURL);
230                        isFirstStart = false;
231                    }
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 convertToJNDIEnvironmentRefsGroup(String namespace, XmlCursor cursor, XmlCursor moveable) {
257            moveElements("env-entry", namespace, moveable, cursor);
258            moveElements("ejb-ref", namespace, moveable, cursor);
259            moveElements("ejb-local-ref", namespace, moveable, cursor);
260            moveElements("resource-ref", namespace, moveable, cursor);
261            moveElements("resource-env-ref", namespace, moveable, cursor);
262            moveElements("message-destination-ref", namespace, moveable, cursor);
263            if (cursor.toPrevSibling()) {
264                do {
265                    String name = cursor.getName().getLocalPart();
266                    if ("env-entry".equals(name)) {
267                        cursor.push();
268                        cursor.toFirstChild();
269                        convertToDescriptionGroup(namespace, cursor, moveable);
270                        convertToEnvEntryGroup(namespace, cursor, moveable);
271                        cursor.pop();
272                    }
273                } while (cursor.toPrevSibling());
274            }
275        }
276    
277        public static void convertToEnvEntryGroup(String namespace, XmlCursor cursor, XmlCursor moveable) {
278            moveElements("env-entry-name", namespace, moveable, cursor);
279            moveElements("env-entry-type", namespace, moveable, cursor);
280            moveElements("env-entry-value", namespace, moveable, cursor);
281        }
282    
283        private static void moveElements(String localName, String namespace, XmlCursor moveable, XmlCursor toHere) {
284            QName name = new QName(namespace, localName);
285            //skip elements already in the correct order.
286            while (name.equals(toHere.getName()) && toHere.toNextSibling()) {
287            }
288            moveable.toCursor(toHere);
289            while (moveable.toNextSibling(name)) {
290                moveable.moveXml(toHere);
291            }
292        }
293    
294    }