net.harawata.mybatipse.mybatis.TypeAliasCache.java Source code

Java tutorial

Introduction

Here is the source code for net.harawata.mybatipse.mybatis.TypeAliasCache.java

Source

/*-******************************************************************************
 * Copyright (c) 2014 Iwao AVE!.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Iwao AVE! - initial API and implementation and/or initial documentation
 *******************************************************************************/

package net.harawata.mybatipse.mybatis;

import static net.harawata.mybatipse.MybatipseConstants.*;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.XPathExpressionException;

import net.harawata.mybatipse.Activator;
import net.harawata.mybatipse.mybatis.TypeAliasMap.TypeAliasInfo;
import net.harawata.mybatipse.util.NameUtil;
import net.harawata.mybatipse.util.XpathUtil;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMemberValuePair;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jdt.core.search.TypeNameRequestor;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.validation.internal.provisional.core.IReporter;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;

/**
 * @author Iwao AVE!
 */
@SuppressWarnings("restriction")
public class TypeAliasCache {
    private static final TypeAliasCache INSTANCE = new TypeAliasCache();

    private static final String GUICE_MODULE_FQN = "org.mybatis.guice.MyBatisModule";

    private static final String SPRING_BEAN_FQN = "org.mybatis.spring.SqlSessionFactoryBean";

    private static final List<String> declaredTypes = Arrays.asList(GUICE_MODULE_FQN, SPRING_BEAN_FQN);

    private final Map<String, TypeAliasMap> projectCache = new ConcurrentHashMap<String, TypeAliasMap>();

    private final Map<String, Set<String>> packageCache = new ConcurrentHashMap<String, Set<String>>();

    private final Map<String, Set<String>> superTypeCache = new ConcurrentHashMap<String, Set<String>>();

    public String resolveAlias(IJavaProject javaProject, String alias, IReporter reporter) {
        Map<String, TypeAliasInfo> aliasMap = getTypeAliasMap(javaProject, reporter);
        TypeAliasInfo typeAliasInfo = aliasMap.get(alias.toLowerCase(Locale.ENGLISH));
        return typeAliasInfo == null ? null : typeAliasInfo.getQualifiedName();
    }

    public void removeType(String projectName, String qualifiedName) {
        TypeAliasMap aliasMap = projectCache.get(projectName);
        if (aliasMap == null)
            return;
        Iterator<Entry<String, TypeAliasInfo>> iterator = aliasMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Entry<String, TypeAliasInfo> entry = iterator.next();
            if (qualifiedName.equals(entry.getValue().getQualifiedName())) {
                iterator.remove();
            }
        }
    }

    public void put(String projectName, IType type, String simpleTypeName) {
        String qualifiedName = type.getFullyQualifiedName();
        removeType(projectName, qualifiedName);
        try {
            TypeAliasMap aliasMap = projectCache.get(projectName);
            if (aliasMap == null)
                return;
            String alias = getAliasAnnotationValue(type);
            if (alias == null) {
                alias = simpleTypeName;
            }
            aliasMap.put(alias, qualifiedName);
        } catch (JavaModelException e) {
            Activator.log(Status.ERROR, "Error while resolving alias for type " + qualifiedName, e);
        }
    }

    public void remove(IProject project) {
        String projectName = project.getName();
        projectCache.remove(projectName);
    }

    public void clear() {
        projectCache.clear();
    }

    public boolean isInPackage(String projectName, String packageName) {
        Set<String> packages = packageCache.get(projectName);
        if (packages == null)
            return false;
        for (String pkg : packages) {
            if (packageName.startsWith(pkg)) {
                return true;
            }
        }
        return false;
    }

    public TypeAliasMap getTypeAliasMap(IJavaProject javaProject, IReporter reporter) {
        String projectName = javaProject.getElementName();
        TypeAliasMap aliasMap = projectCache.get(projectName);
        if (aliasMap == null) {
            Map<IFile, IContentType> configFiles = ConfigRegistry.getInstance().get(javaProject);
            aliasMap = new TypeAliasMap();
            projectCache.put(projectName, aliasMap);
            packageCache.remove(projectName);
            superTypeCache.remove(projectName);
            Set<String> packages = new TreeSet<String>();
            packageCache.put(projectName, packages);
            Set<String> superTypes = new HashSet<String>();
            superTypeCache.put(projectName, superTypes);

            // Lookup the settings.
            IPreferenceStore store = Activator.getPreferenceStore(javaProject.getProject());
            String storedValue = store.getString(PREF_CUSTOM_TYPE_ALIASES);
            StringTokenizer tokenizer = new StringTokenizer(storedValue, "\t");
            while (tokenizer.hasMoreElements()) {
                String token = (String) tokenizer.nextElement();
                if (token == null)
                    continue;
                token = token.trim();
                if (token.length() == 0)
                    continue;
                try {
                    int colonIdx = token.indexOf(':');
                    if (colonIdx == -1) {
                        IType type = javaProject.findType(token);
                        if (type == null)
                            packages.add(token);
                        else {
                            String alias = getAliasAnnotationValue(type);
                            aliasMap.put(alias, token);
                        }
                    } else if (colonIdx > 0 && colonIdx < token.length() - 2) {
                        String qualifiedName = token.substring(0, colonIdx);
                        IType type = javaProject.findType(qualifiedName);
                        if (type == null) {
                            Activator.log(Status.WARNING,
                                    "Missing '" + qualifiedName + "' specified in the custom type alias setting.");
                        } else {
                            // Look for @Alias first.
                            String alias = getAliasAnnotationValue(type);
                            if (alias == null)
                                alias = token.substring(colonIdx + 1);
                            aliasMap.put(alias, qualifiedName);
                        }
                    }
                } catch (JavaModelException e) {
                    Activator.log(Status.ERROR, e.getMessage(), e);
                }
            }

            // Parse config xml files (mybatis and spring).
            for (Entry<IFile, IContentType> configFile : configFiles.entrySet()) {
                parseConfigFiles(javaProject, configFile.getKey(), configFile.getValue(), aliasMap, packages,
                        superTypes, reporter);
            }

            // Search calls registering type aliases in java code.
            // scanJavaConfig(javaProject, aliasMap, packages, reporter);

            // Scan classes in the packages.
            if (!packages.isEmpty()) {
                collectTypesInPackages(javaProject, packages, aliasMap, superTypes, reporter);
            }
        }
        return aliasMap;
    }

    /**
     * <p>
     * Trying to find calls registering type aliases.<br>
     * But only simple calls can be supported.
     * </p>
     */
    private void scanJavaConfig(IJavaProject project, final TypeAliasMap aliasMap, final Set<String> packages,
            IReporter reporter) {
        try {
            IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaProject[] { project },
                    IJavaSearchScope.SOURCES | IJavaSearchScope.REFERENCED_PROJECTS
                            | IJavaSearchScope.APPLICATION_LIBRARIES);
            SearchParticipant[] participants = new SearchParticipant[] {
                    SearchEngine.getDefaultSearchParticipant() };
            SearchEngine searchEngine = new SearchEngine();

            final Set<ITypeRoot> typeRoots = new HashSet<ITypeRoot>();

            // Collect classes that contain mybatis-guice calls.
            IType mybatisModuleType = project.findType(GUICE_MODULE_FQN);
            if (mybatisModuleType == null || !mybatisModuleType.exists())
                return;

            IMethod addSimpleAliasMethod = mybatisModuleType.getMethod("addSimpleAlias",
                    new String[] { "Ljava.lang.Class;" });
            SearchPattern pattern = SearchPattern.createPattern(addSimpleAliasMethod,
                    IJavaSearchConstants.REFERENCES | IJavaSearchConstants.IGNORE_DECLARING_TYPE
                            | IJavaSearchConstants.IMPLEMENTORS);
            searchEngine.search(pattern, participants, scope, new MethodSearchRequestor(typeRoots), null);

            IMethod addSimpleAliasesMethodWithPackage = mybatisModuleType.getMethod("addSimpleAliases",
                    new String[] { "Ljava.lang.String;" });
            pattern = SearchPattern.createPattern(addSimpleAliasesMethodWithPackage,
                    IJavaSearchConstants.REFERENCES | IJavaSearchConstants.IGNORE_DECLARING_TYPE);
            searchEngine.search(pattern, participants, scope, new MethodSearchRequestor(typeRoots), null);

            // IMethod addSimpleAliasesMethodWithPackageAndTest = mybatisModuleType.getMethod(
            // "addSimpleAliases", new String[]{
            // "Ljava.util.Collection<Ljava.lang.Class<*>;>;"
            // });
            // pattern = SearchPattern.createPattern(addSimpleAliasesMethodWithPackageAndTest,
            // IJavaSearchConstants.REFERENCES | IJavaSearchConstants.IGNORE_DECLARING_TYPE);
            // searchEngine.search(pattern, participants, scope, new MethodSearchRequestor(typeRoots),
            // null);

            // Searches Spring java config if necessary.
            // if (typeRoots.isEmpty())
            // {
            // IType sqlSessionFactoryBeanType = project.findType(SPRING_BEAN_FQN);
            // if (sqlSessionFactoryBeanType == null || !sqlSessionFactoryBeanType.exists())
            // return;
            //
            // IMethod setTypeAliasesPackageMethod = sqlSessionFactoryBeanType.getMethod(
            // "setTypeAliasesPackage", new String[]{
            // "Ljava.lang.String;"
            // });
            // pattern = SearchPattern.createPattern(setTypeAliasesPackageMethod,
            // IJavaSearchConstants.REFERENCES | IJavaSearchConstants.IGNORE_DECLARING_TYPE
            // | IJavaSearchConstants.IMPLEMENTORS);
            // searchEngine.search(pattern, participants, scope, new MethodSearchRequestor(typeRoots),
            // null);
            // }

            for (ITypeRoot typeRoot : typeRoots) {
                ASTParser parser = ASTParser.newParser(AST.JLS4);
                parser.setSource(typeRoot);
                parser.setResolveBindings(true);
                CompilationUnit astUnit = (CompilationUnit) parser.createAST(null);
                astUnit.accept(new JavaConfigVisitor(aliasMap, packages));
            }
        } catch (JavaModelException e) {
            Activator.log(Status.ERROR, e.getMessage(), e);
        } catch (CoreException e) {
            Activator.log(Status.ERROR, e.getMessage(), e);
        }
    }

    private void parseConfigFiles(IJavaProject project, IFile configFile, IContentType configType,
            final TypeAliasMap aliasMap, Set<String> packages, Set<String> superTypeList, IReporter reporter) {
        IStructuredModel model = null;
        try {
            model = StructuredModelManager.getModelManager().getModelForRead(configFile);
            IDOMModel domModel = (IDOMModel) model;
            IDOMDocument domDoc = domModel.getDocument();

            if (reporter != null && reporter.isCancelled()) {
                throw new OperationCanceledException();
            }

            if (configContentType.equals(configType)) {
                // Parse <typeAlias /> tags.
                parseTypeAliasElements(domDoc, aliasMap);

                if (reporter != null && reporter.isCancelled()) {
                    throw new OperationCanceledException();
                }

                // Parse <packags /> tags.
                parsePackageElements(domDoc, packages);
            } else if (springConfigContentType.equals(configType)) {
                parseTypeAliasesPackage(packages, domDoc);

                if (reporter != null && reporter.isCancelled()) {
                    throw new OperationCanceledException();
                }

                parseTypeAliasesSuperType(superTypeList, domDoc);
            }
        } catch (XPathExpressionException e) {
            Activator.log(Status.ERROR, e.getMessage(), e);
        } catch (IOException e) {
            Activator.log(Status.ERROR, e.getMessage(), e);
        } catch (CoreException e) {
            Activator.log(Status.ERROR, e.getMessage(), e);
        } finally {
            // Activator.log(Status.WARNING, "Error occurred during validation.", e);
            if (model != null) {
                model.releaseFromRead();
            }
        }
    }

    private void parseTypeAliasesPackage(Set<String> packages, IDOMDocument domDoc)
            throws XPathExpressionException {
        // There can be multiple SqlSessionFactoryBeans.
        NodeList nodes = XpathUtil.xpathNodes(domDoc,
                "//beans:bean/beans:property[@name='typeAliasesPackage']/@value",
                new SpringConfigNamespaceContext());
        // NodeList nodes = XpathUtil.xpathNodes(domDoc,
        // "//*[namespace-uri() = 'http://www.springframework.org/schema/beans']"
        // + "[local-name() = 'property'][@name='typeAliasesPackage']/@value");
        for (int i = 0; i < nodes.getLength();) {
            String value = nodes.item(i).getNodeValue();
            String[] arr = value.split(",; \t\n");
            for (String pkg : arr) {
                if (pkg != null && pkg.length() > 0)
                    packages.add(pkg.trim());
            }
            // Only the first bean is parsed because
            // the plugin cannot support inconsistent
            // type aliases settings anyway.
            break;
        }
    }

    private void parseTypeAliasesSuperType(Set<String> superTypes, IDOMDocument domDoc)
            throws XPathExpressionException {
        // There can be multiple SqlSessionFactoryBeans.
        NodeList nodes = XpathUtil.xpathNodes(domDoc,
                "//beans:bean/beans:property[@name='typeAliasesSuperType']/@value",
                new SpringConfigNamespaceContext());
        // NodeList nodes = XpathUtil.xpathNodes(domDoc,
        // "//*[namespace-uri() = 'http://www.springframework.org/schema/beans']"
        // + "[local-name() = 'property'][@name='typeAliasesSuperType']/@value");
        for (int i = 0; i < nodes.getLength();) {
            String value = nodes.item(i).getNodeValue();
            superTypes.add(value.trim());
            // Only the first bean is parsed because
            // the plugin cannot support inconsistent
            // type aliases settings anyway.
            break;
        }
    }

    private void collectTypesInPackages(final IJavaProject project, Set<String> packages,
            final TypeAliasMap aliasMap, Set<String> superTypes, IReporter reporter) {
        final Set<IType> superTypeSet = new HashSet<IType>();
        try {
            for (String supType : superTypes) {
                superTypeSet.add(project.findType(supType));
            }
        } catch (JavaModelException e) {
            Activator.log(Status.ERROR, e.getMessage(), e);
        }

        int includeMask = IJavaSearchScope.SOURCES | IJavaSearchScope.REFERENCED_PROJECTS
                | IJavaSearchScope.APPLICATION_LIBRARIES | IJavaSearchScope.SYSTEM_LIBRARIES;
        IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaProject[] { project }, includeMask);
        TypeNameRequestor requestor = new TypeNameRequestor() {
            @Override
            public void acceptType(int modifiers, char[] packageName, char[] simpleTypeName,
                    char[][] enclosingTypeNames, String path) {
                // Ignore abstract classes.
                if (Flags.isAbstract(modifiers))
                    return;

                String qualifiedName = NameUtil.buildQualifiedName(packageName, simpleTypeName, enclosingTypeNames,
                        false);
                try {
                    IType foundType = project.findType(qualifiedName);
                    if (superTypeSet.isEmpty() || isSuperTypeMatched(foundType, superTypeSet)) {
                        String alias = getAliasAnnotationValue(foundType);
                        if (alias == null) {
                            alias = new String(simpleTypeName);
                        }
                        aliasMap.put(alias, qualifiedName);
                    }
                } catch (JavaModelException e) {
                    Activator.log(Status.WARNING, "Error occurred while searching type alias.", e);
                }
            }

            private boolean isSuperTypeMatched(IType foundType, Set<IType> superTypeSet) throws JavaModelException {
                for (IType superType : superTypeSet) {
                    if (isAssignable(foundType, superType)) {
                        return true;
                    }
                }
                return false;
            }
        };

        SearchEngine searchEngine = new SearchEngine();
        for (String pkg : packages) {
            if (reporter != null && reporter.isCancelled()) {
                throw new OperationCanceledException();
            }
            try {
                searchEngine.searchAllTypeNames(pkg.toCharArray(), SearchPattern.R_EXACT_MATCH, null,
                        SearchPattern.R_CAMELCASE_MATCH, IJavaSearchConstants.CLASS, scope, requestor,
                        IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, null);
            } catch (JavaModelException e) {
                Activator.log(Status.ERROR, e.getMessage(), e);
            }
        }
    }

    private String getAliasAnnotationValue(IType foundType) throws JavaModelException {
        String alias = null;
        IAnnotation[] annotations = foundType.getAnnotations();
        for (IAnnotation annotation : annotations) {
            if ("Alias".equals(annotation.getElementName())) {
                IMemberValuePair[] params = annotation.getMemberValuePairs();
                if (params.length > 0) {
                    alias = (String) params[0].getValue();
                }
            }
        }
        return alias;
    }

    private void parsePackageElements(IDOMDocument domDoc, Set<String> packages) throws XPathExpressionException {
        NodeList pkgNameNodes = XpathUtil.xpathNodes(domDoc, "//typeAliases/package/@name");
        for (int i = 0; i < pkgNameNodes.getLength(); i++) {
            String pkg = pkgNameNodes.item(i).getNodeValue();
            packages.add(pkg);
        }
    }

    private void parseTypeAliasElements(IDOMDocument domDoc, final TypeAliasMap aliasMap)
            throws XPathExpressionException {
        NodeList nodes = XpathUtil.xpathNodes(domDoc, "//typeAliases/typeAlias");
        for (int i = 0; i < nodes.getLength(); i++) {
            String type = null;
            String alias = null;
            NamedNodeMap attrs = nodes.item(i).getAttributes();
            for (int j = 0; j < attrs.getLength(); j++) {
                IDOMAttr attr = (IDOMAttr) attrs.item(j);
                String attrName = attr.getName();
                if ("type".equals(attrName))
                    type = attr.getValue();
                else if ("alias".equals(attrName))
                    alias = attr.getValue();
            }
            aliasMap.put(alias, type);
        }
    }

    private boolean isAssignable(final IType type, final IType targetType) throws JavaModelException {
        final ITypeHierarchy supertypes = type.newSupertypeHierarchy(new NullProgressMonitor());
        return supertypes.contains(targetType);
    }

    public static TypeAliasCache getInstance() {
        return INSTANCE;
    }

    private TypeAliasCache() {
        super();
    }

    private class JavaConfigVisitor extends ASTVisitor {
        private TypeAliasMap aliasMap;

        private Set<String> packages;

        private JavaConfigVisitor(TypeAliasMap aliasMap, Set<String> packages) {
            this.aliasMap = aliasMap;
            this.packages = packages;
        }

        @Override
        public boolean visit(MethodInvocation node) {
            String invokedMethod = node.getName().getIdentifier();
            if ("addSimpleAlias".equals(invokedMethod)) {
                if (!declaredIn(node, GUICE_MODULE_FQN))
                    return false;

                @SuppressWarnings("rawtypes")
                List args = node.arguments();
                if (args.size() != 1) {
                    Activator.log(Status.WARNING,
                            "Unexpected parameter count (possible API change): " + invokedMethod);
                } else {
                    Expression expression = (Expression) args.get(0);
                    ITypeBinding argType = expression.resolveTypeBinding();
                    ITypeBinding[] classTypes = argType.getTypeArguments();
                    String qualifiedName = classTypes[0].getQualifiedName();
                    String simpleName = classTypes[0].getName();
                    aliasMap.put(simpleName, qualifiedName);
                }
            } else if ("addSimpleAliases".equals(invokedMethod)) {
                if (!declaredIn(node, GUICE_MODULE_FQN))
                    return false;

                @SuppressWarnings("rawtypes")
                List args = node.arguments();
                if (args.size() == 1) {
                    Expression expression = (Expression) args.get(0);
                    ITypeBinding argType = expression.resolveTypeBinding();
                    if ("java.lang.String".equals(argType.getQualifiedName())) {
                        Object constantArg = expression.resolveConstantExpressionValue();
                        if (constantArg != null) {
                            packages.add((String) constantArg);
                        } else {
                            Activator.log(Status.INFO, "TypeAlias detection failed: " + node.toString()
                                    + " Only a literal parameter is supported for addSimpleAliases(String).");
                        }
                    } else if ("java.util.Collection<java.lang.Class<?>>".equals(argType.getQualifiedName())) {
                        Activator.log(Status.INFO, "TypeAlias detection failed. "
                                + "addSimpleAliases(Collection<Class<?>>) is not supported.");
                    }
                } else if (args.size() == 2) {
                    Activator.log(Status.INFO,
                            "TypeAlias detection failed. " + "addSimpleAliases(String, Test) is not supported.");
                }
            }
            return true;
        }

        private boolean declaredIn(MethodInvocation node, String qualifiedName) {
            IMethodBinding methodBinding = node.resolveMethodBinding();
            return methodBinding != null
                    && qualifiedName.equals(methodBinding.getDeclaringClass().getQualifiedName());
        }
    }

    private class MethodSearchRequestor extends SearchRequestor {
        private Set<ITypeRoot> typeRoots;

        private MethodSearchRequestor(Set<ITypeRoot> typeRoots) {
            this.typeRoots = typeRoots;
        }

        @Override
        public void acceptSearchMatch(SearchMatch match) throws CoreException {
            if (match.getAccuracy() == SearchMatch.A_INACCURATE)
                return;

            IMethod element = (IMethod) match.getElement();
            ITypeRoot typeRoot = element.isBinary() ? element.getClassFile() : element.getCompilationUnit();
            if (declaredTypes.contains(typeRoot.findPrimaryType().getFullyQualifiedName()))
                return;
            typeRoots.add(typeRoot);
        }
    }

    private class SpringConfigNamespaceContext implements NamespaceContext {
        @Override
        public String getNamespaceURI(String prefix) {
            if (prefix == null)
                throw new NullPointerException("Prefix cannot be null.");
            else if ("beans".equals(prefix))
                return "http://www.springframework.org/schema/beans";
            else if ("xml".equals(prefix))
                return XMLConstants.XML_NS_URI;
            return XMLConstants.NULL_NS_URI;
        }

        @Override
        public String getPrefix(String namespaceURI) {
            throw new UnsupportedOperationException();
        }

        @SuppressWarnings("rawtypes")
        @Override
        public Iterator getPrefixes(String namespaceURI) {
            throw new UnsupportedOperationException();
        }
    }
}