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 */
017package org.apache.wicket.util.xml;
018
019import java.io.IOException;
020import java.io.InputStream;
021import java.util.HashMap;
022import java.util.Map;
023
024import javax.servlet.Filter;
025
026import org.apache.wicket.util.lang.Args;
027import org.xml.sax.EntityResolver;
028import org.xml.sax.InputSource;
029import org.xml.sax.SAXException;
030
031/**
032 * entity resolver that tries to locate a document type definition (DTD) using a set of custom
033 * entity resolvers
034 * 
035 * @author pete
036 */
037public class CustomEntityResolver implements EntityResolver
038{
039        private final Map<EntityKey, EntityLocator> entities = new HashMap<>(3);
040
041        /**
042         * get default instances of custom entity resolver with preloaded well-known entities
043         * 
044         * @return instance of resolver
045         */
046        public static CustomEntityResolver getPreloaded()
047        {
048                CustomEntityResolver resolver = new CustomEntityResolver();
049
050                resolver.put(new EntityKey("-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",
051                        "http://java.sun.com/dtd/web-app_2_3.dtd"), new ServletApiEntityLocator(
052                        "web-app_2_3.dtd"));
053
054                return resolver;
055        }
056
057        /**
058         * add custom entity resolver
059         * 
060         * @param key
061         *            key for lookup (contains id and url)
062         * @param locator
063         *            locator for looking up entity
064         */
065        public void put(final EntityKey key, final EntityLocator locator)
066        {
067                Args.notNull(key, "key");
068                Args.notNull(locator, "locator");
069                entities.put(key, locator);
070        }
071
072        @Override
073        public InputSource resolveEntity(final String id, final String url) throws SAXException,
074                IOException
075        {
076                for (Map.Entry<EntityKey, EntityLocator> entry : entities.entrySet())
077                {
078                        if (entry.getKey().id.equals(id) || entry.getKey().url.equals(url))
079                        {
080                                return entry.getValue().locateInputSource();
081                        }
082                }
083
084                return null;
085        }
086
087        /**
088         * key for entity
089         * <p/>
090         * consists of id + url
091         */
092        public static class EntityKey
093        {
094                private final String id;
095                private final String url;
096
097                private EntityKey(final String id, final String url)
098                {
099                        Args.notEmpty(id, "id");
100                        Args.notEmpty(url, "url");
101                        this.id = id;
102                        this.url = url;
103                }
104
105                @Override
106                public boolean equals(final Object o)
107                {
108                        if (this == o)
109                        {
110                                return true;
111                        }
112                        if (!(o instanceof EntityKey))
113                        {
114                                return false;
115                        }
116
117                        EntityKey key = (EntityKey)o;
118
119                        if (!id.equals(key.id))
120                        {
121                                return false;
122                        }
123
124                        return url.equals(key.url);
125                }
126
127                @Override
128                public int hashCode()
129                {
130                        int result = id.hashCode();
131                        result = 31 * result + url.hashCode();
132                        return result;
133                }
134        }
135
136        /**
137         * entity locator
138         * <p/>
139         * manages locating an entity
140         */
141        public static interface EntityLocator
142        {
143                /**
144                 * @return input source
145                 * @throws SAXException
146                 * @throws IOException
147                 */
148                InputSource locateInputSource() throws SAXException, IOException;
149        }
150
151        /**
152         * entity locator for resources inside servlet-api.jar
153         */
154        public static class ServletApiEntityLocator implements EntityLocator
155        {
156                private final String name;
157
158                private ServletApiEntityLocator(final String name)
159                {
160                        this.name = name;
161                }
162
163                /**
164                 * resolve servlet api resource, where e.g. 'web-app_2_3.dtd' is located
165                 * 
166                 * @return input source
167                 */
168                @Override
169                public InputSource locateInputSource()
170                {
171                        InputStream stream = Filter.class.getResourceAsStream("resources/" + name);
172
173                        if (stream == null)
174                        {
175                                return null;
176                        }
177
178                        return new InputSource(stream);
179                }
180        }
181}