001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.ken.util;
017
018import javax.xml.XMLConstants;
019
020import org.apache.log4j.Logger;
021
022/**
023 * Utility base class for Entity and LSResource Resolvers that should resolve
024 * arguments to resources in the ClassLoader.
025 * @author Kuali Rice Team (rice.collab@kuali.org)
026 */
027public class ClassLoaderResourceResolver {
028    protected final Logger LOG = Logger.getLogger(getClass());
029
030    /**
031     * This contains definitions for items in the core "xml" schema, i.e. base, id, lang, and space attributes. 
032     */
033    private static final String XML_NAMESPACE_SCHEMA = "http://www.w3.org/2001/xml.xsd";
034    private static final String XSD_NAMESPACE_SCHEMA = XMLConstants.W3C_XML_SCHEMA_NS_URI;
035
036    /**
037     * Root path in class loader.  Defaults to "schema".
038     */
039    protected final String base;
040    /**
041     * Prefix of resources to honor.  Defaults to "" (no specific prefix).
042     */
043    protected final String prefix;
044
045    /**
046     * Constructs a ClassLoaderResourceResolver.java.
047     */
048    public ClassLoaderResourceResolver() {
049        this.base = "schema";
050        this.prefix = "";
051    }
052    
053    /**
054     * Constructs a ClassLoaderResourceResolver.java.
055     * @param base
056     * @param prefix
057     */
058    public ClassLoaderResourceResolver(String base, String prefix) {
059        this.base = base;
060        this.prefix = prefix;
061    }
062
063    /**
064     * @param systemId
065     * @return String
066     */
067    protected String resolveSystemId(String systemId) {
068        if (systemId.equals(XML_NAMESPACE_SCHEMA)) {
069            return base + "/xml.xsd";
070        } else if (systemId.equals(XSD_NAMESPACE_SCHEMA)) {
071            return base + "/XMLSchema.xsd";
072        } else if (systemId.startsWith("resource:" + prefix +"/")) {
073            /* It turns out that the stock XMLSchema.xsd refers to XMLSchema.dtd in a relative
074               fashion which results in the parser qualifying it to some local file:// path
075               which breaks our detection here.
076               So I have made a small mod to the stock XMLSchema.xsd so that it instead refers to
077               resource:XMLSchema.dtd which can be looked up locally.
078               The same is true for XMLSchema.dtd with regard to datatypes.dtd, so I have also
079               modified XMLSchema.dtd to refer to resource:datatypes.dtd.
080               An alternative would be to rely on publicId, however that would essentially hard code
081               the lookup to always be in the classpath and rule out being able to redirect the location
082               of the physical resource through the systemId, which is useful.
083            */
084
085            String path = base + "/" + systemId.substring(("resource:" + prefix + "/").length());
086            // ok, if the path does not itself end in .xsd or .dtd, it is bare/abstract
087            // so realize it by appending .xsd
088            // this allows us to support looking up files ending with ".dtd" through resource: without
089            // having extra logic to attempt to look up both suffixes for every single resource:
090            // (all of which except XMLSchema.dtd and datatypes.dtd at this point are .xsd files)
091            if (!(systemId.endsWith(".xsd") || systemId.endsWith(".dtd"))) {
092                path += ".xsd";
093            }
094            return path;
095        } else {
096            return null;
097        }
098    }
099}