com.alibaba.citrus.springext.util.ConvertToUnqualifiedStyle.java Source code

Java tutorial

Introduction

Here is the source code for com.alibaba.citrus.springext.util.ConvertToUnqualifiedStyle.java

Source

/*
 * Copyright (c) 2002-2013 Alibaba Group Holding Limited.
 * All rights reserved.
 *
 * Licensed 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 com.alibaba.citrus.springext.util;

import static com.alibaba.citrus.springext.support.SchemaUtil.*;
import static com.alibaba.citrus.util.ArrayUtil.*;
import static com.alibaba.citrus.util.Assert.*;
import static com.alibaba.citrus.util.CollectionUtil.*;
import static com.alibaba.citrus.util.StringUtil.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import com.alibaba.citrus.logconfig.LogConfigurator;
import com.alibaba.citrus.springext.ConfigurationPoint;
import com.alibaba.citrus.springext.ContributionType;
import com.alibaba.citrus.springext.Schema;
import com.alibaba.citrus.springext.support.SchemaUtil;
import com.alibaba.citrus.springext.support.SpringExtSchemaSet;
import com.alibaba.citrus.util.FileUtil;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * webx???unqualifed
 *
 * @author Michael Zhou
 */
public class ConvertToUnqualifiedStyle {
    private final Logger log = LoggerFactory.getLogger(getClass());
    private final File[] sources;
    private final SpringExtSchemaSet schemas;
    private final boolean forceConvert;
    private final boolean backup;
    private int convertedCount;

    /** ?classpath???configuration points */
    public static void main(String[] args) {
        File[] sources = new File[args.length];

        for (int i = 0; i < args.length; i++) {
            sources[i] = new File(args[i]).getAbsoluteFile();
        }

        new ConvertToUnqualifiedStyle(sources).convert();
    }

    public ConvertToUnqualifiedStyle(File[] sources) {
        this(sources, false, true);
    }

    public ConvertToUnqualifiedStyle(File[] sources, boolean forceConvert, boolean backup) {
        LogConfigurator.getConfigurator().configureDefault();

        this.sources = sources;
        this.schemas = new SpringExtSchemaSet();
        this.forceConvert = forceConvert;
        this.backup = backup;
    }

    public void convert() {
        if (!isEmptyArray(sources)) {
            for (File source : sources) {
                convert(source);
            }
        }

        log.info("Converted {} files.", convertedCount);
    }

    private final Pattern backupFilePattern = Pattern.compile("\\.bak$");

    private void convert(File source) {
        if (backupFilePattern.matcher(source.getName()).find()) {
            return; // skip backup file
        }

        Document doc;

        try {
            doc = SchemaUtil.readDocument(new FileInputStream(source), source.getAbsolutePath(), true);
        } catch (Exception e) {
            log.warn("Failed to read file {}: {}", getRelativePath(source), e.getMessage());
            return;
        }

        if (!isSpringConfigurationFile(doc)) {
            return;
        }

        log.info("Converting file: {}", getRelativePath(source));

        boolean modified = new Converter(doc, schemas).convert();

        if (modified || forceConvert) {
            File dir = source.getParentFile();
            String fileName = source.getName();
            int index = fileName.lastIndexOf(".");
            String ext = "";

            if (index >= 0) {
                ext = fileName.substring(index);
                fileName = fileName.substring(0, index);
            }

            File tmpFile = null;
            FileOutputStream fos = null;
            boolean failed = false;

            try {
                tmpFile = File.createTempFile(fileName + "_tmp_", ext, dir);
                fos = new FileOutputStream(tmpFile);

                writeDocument(doc, fos);
            } catch (IOException e) {
                log.warn("Failed to write to {}: {}", getRelativePath(tmpFile), e.getMessage());
                failed = true;
            } finally {
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException ignored) {
                    }
                }
            }

            if (failed) {
                tmpFile.delete();
            } else {
                File backupFile = null;

                if (backup) {
                    for (int i = 0;; i++) {
                        backupFile = new File(dir, fileName + (i == 0 ? "" : "_" + i) + ext + ".bak");

                        if (!backupFile.exists()) {
                            break;
                        }
                    }

                    source.renameTo(backupFile);
                    log.info("  ... converted, original content saved as {}", getRelativePath(backupFile));
                } else {
                    source.delete();
                    log.info("  ... converted");
                }

                tmpFile.renameTo(source);

                convertedCount++;
            }
        } else {
            log.info("  ... skipped");
        }
    }

    private static void writeDocument(Document doc, OutputStream stream) throws IOException {
        String charset = "UTF-8";
        Writer writer = new OutputStreamWriter(stream, charset);

        OutputFormat format = new OutputFormat();

        format.setEncoding(charset);

        XMLWriter xmlWriter = new XMLWriter(writer, format);
        xmlWriter.write(doc);
        xmlWriter.flush();
    }

    /** Root element?<code>&lt;beans:bean&gt;</code> */
    private boolean isSpringConfigurationFile(Document doc) {
        Element root = doc.getRootElement();
        return "http://www.springframework.org/schema/beans".equals(root.getNamespaceURI())
                && "beans".equals(root.getName());
    }

    private String getRelativePath(File f) {
        return FileUtil.getRelativePath(new File("").getAbsolutePath(), f.getAbsolutePath());
    }

    public static class Converter {
        private final SpringExtSchemaSet schemas;
        private final Element root;
        private final LinkedList<String> namespaceURIStack = createLinkedList();
        private final Map<String, Namespace> configurationPointNamespaces = createTreeMap();
        private final Map<String, Namespace> allSchemaNamespaces = createTreeMap();
        private boolean modified = false;

        public Converter(Document doc, SpringExtSchemaSet schemas) {
            this.root = doc.getRootElement();
            this.schemas = schemas;
        }

        public boolean convert() {
            visit(root);
            return modified;
        }

        private void visit(Element element) {
            String namespaceURI = trimToNull(element.getNamespaceURI());
            List<Namespace> nsToBeRemoved = createLinkedList();

            for (Iterator<?> i = element.declaredNamespaces().iterator(); i.hasNext();) {
                Namespace ns = (Namespace) i.next();

                if (isConfigurationPointNamespace(ns.getURI())) {
                    nsToBeRemoved.add(ns);

                    if (element != root) {
                        modified = true;
                    }

                    if (!configurationPointNamespaces.containsKey(ns.getURI())) {
                        if (isEmpty(ns.getPrefix())) {
                            String prefix = getNamespacePrefix(schemas.getConfigurationPoints()
                                    .getConfigurationPointByNamespaceUri(ns.getURI()).getPreferredNsPrefix(),
                                    ns.getURI());

                            ns = Namespace.get(prefix, ns.getURI());
                            modified = true;
                        }

                        configurationPointNamespaces.put(ns.getURI(), ns);
                        allSchemaNamespaces.put(ns.getURI(), ns);
                    }
                } else if (schemas.getNamespaceMappings().containsKey(ns.getURI())) {
                    if (!allSchemaNamespaces.containsKey(ns.getURI())) {
                        allSchemaNamespaces.put(ns.getURI(), ns);
                    }
                }
            }

            // ?ns?configuration point ns
            if (!isConfigurationPointNamespace(namespaceURI)) {
                visitSubElements(element);
            }

            // ns??nullconfiguration point ns
            // ?nscontext ns??
            else if (!namespaceURI.equals(getContextNamespaceURI())
                    || isContributionElement(namespaceURI, element.getName())) {
                // namespaceURI??namespaces.get(namespaceURI)?
                Namespace ns = assertNotNull(configurationPointNamespaces.get(namespaceURI),
                        "xmlns not defined for %s", namespaceURI);

                try {
                    namespaceURIStack.push(namespaceURI);
                    setNamespacePrefix(element, ns.getPrefix());
                    visitSubElements(element);
                } finally {
                    namespaceURIStack.pop();
                }
            }

            // ?nscontext ns?
            else {
                removeNamespace(element);
                visitSubElements(element);
                modified = true;
            }

            // ns?attributes???
            for (Namespace ns : nsToBeRemoved) {
                element.remove(ns);
            }

            // nsroot element
            if (element == root) {
                Map<String, Namespace> otherNamespaces = createTreeMap();
                Map<String, Attribute> otherAttrs = createTreeMap();
                Namespace xsi = null;
                String schemaLocation = null;

                for (Iterator<?> i = element.declaredNamespaces().iterator(); i.hasNext();) {
                    Namespace ns = (Namespace) i.next();

                    if ("http://www.w3.org/2001/XMLSchema-instance".equals(ns.getURI())) {
                        xsi = ns;
                    } else if (!allSchemaNamespaces.containsKey(ns.getURI())) {
                        otherNamespaces.put(ns.getURI(), ns);
                    }

                    i.remove();
                }

                QName schemaLocationQName = QName.get("schemaLocation", xsi);

                for (Iterator<?> i = element.attributes().iterator(); i.hasNext();) {
                    Attribute attr = (Attribute) i.next();

                    if (schemaLocationQName.equals(attr.getQName())) {
                        schemaLocation = attr.getText();
                    } else {
                        otherAttrs.put(attr.getQualifiedName(), attr);
                    }

                    i.remove();
                }

                // xmlns:xsi
                if (xsi == null) {
                    xsi = DocumentHelper.createNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
                }

                element.add(xsi);

                // namespaces in defined schemas
                for (Namespace ns : allSchemaNamespaces.values()) {
                    element.add(ns);
                }

                // other xmlns attrs
                for (Namespace ns : otherNamespaces.values()) {
                    element.add(ns);
                }

                // schema location
                element.addAttribute(schemaLocationQName, reformatSchemaLocations(schemaLocation));

                // other attrs
                for (Attribute attr : otherAttrs.values()) {
                    element.add(attr);
                }
            }
        }

        private boolean isContributionElement(String uri, String name) {
            ConfigurationPoint cp = schemas.getConfigurationPoints().getConfigurationPointByNamespaceUri(uri);

            if (cp != null) {
                return name.equals(cp.getDefaultElementName()) // default element
                        || cp.getContribution(name, ContributionType.BEAN_DEFINITION_PARSER) != null // contribution
                        || cp.getContribution(name, ContributionType.BEAN_DEFINITION_DECORATOR) != null; // contribution
            }

            return false;
        }

        private boolean isConfigurationPointNamespace(String namespaceURI) {
            return schemas.getConfigurationPoints().getConfigurationPointByNamespaceUri(namespaceURI) != null;
        }

        private String reformatSchemaLocations(String schemaLocation) {
            Map<String, String> schemaLocations = parseSchemaLocation(schemaLocation);
            String locationPrefix = guessLocationPrefix(schemaLocations, schemas);

            // schema location
            for (String namespaceURI : allSchemaNamespaces.keySet()) {
                if (!schemaLocations.containsKey(namespaceURI)) {
                    try {
                        Set<Schema> schemaSet = schemas.getNamespaceMappings().get(namespaceURI);

                        if (schemaSet != null && schemaSet.size() > 0) {
                            schemaLocations.put(namespaceURI,
                                    locationPrefix + schemaSet.iterator().next().getName());
                            modified = true;
                        }
                    } catch (Exception ignored) {
                    }
                }
            }

            return formatSchemaLocations(schemaLocations, root.getQualifiedName());
        }

        /** elementprefix???namespace */
        private void setNamespacePrefix(Element element, String prefix) {
            assertNotNull(prefix, "prefix is null");

            if (!prefix.equals(element.getNamespacePrefix())) {
                element.setQName(QName.get(element.getName(), prefix, element.getNamespaceURI()));
                modified = true;
            }
        }

        /** element??unqualified */
        private void removeNamespace(Element element) {
            if (!isEmpty(element.getNamespaceURI())) {
                element.setQName(QName.get(element.getName()));
                modified = true;
            }
        }

        private void visitSubElements(Element element) {
            for (Object subElement : element.elements()) {
                visit((Element) subElement);
            }
        }

        private String getContextNamespaceURI() {
            if (namespaceURIStack.isEmpty()) {
                return null;
            }

            return namespaceURIStack.peek();
        }
    }
}