1/*
2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package javax.xml.catalog;
26
27import java.io.StringReader;
28import javax.xml.catalog.BaseEntry.CatalogEntryType;
29import javax.xml.parsers.SAXParser;
30import javax.xml.transform.Source;
31import javax.xml.transform.TransformerException;
32import javax.xml.transform.URIResolver;
33import javax.xml.transform.sax.SAXSource;
34import org.xml.sax.Attributes;
35import org.xml.sax.EntityResolver;
36import org.xml.sax.InputSource;
37import org.xml.sax.SAXException;
38import org.xml.sax.helpers.DefaultHandler;
39
40/**
41 * CatalogReader handles SAX events while parsing through a catalog file to
42 * create a catalog object.
43 *
44 * @since 9
45 */
46class CatalogReader extends DefaultHandler implements EntityResolver, URIResolver {
47    /** URI of the W3C XML Schema for OASIS XML Catalog files. */
48    public static final String xmlCatalogXSD = "http://www.oasis-open.org/committees/entity/release/1.0/catalog.xsd";
49
50    /** Public identifier for OASIS XML Catalog files. */
51    public static final String xmlCatalogPubId = "-//OASIS//DTD XML Catalogs V1.0//EN";
52
53    /**
54     * The namespace name defined by the OASIS Standard
55     */
56    public static final String NAMESPACE_OASIS = "urn:oasis:names:tc:entity:xmlns:xml:catalog";
57
58    //Indicate whether the root element has been found
59    boolean seenRoot;
60
61    //Indicate that the parser is in a group entry
62    boolean inGroup;
63
64    //The Catalog instance
65    CatalogImpl catalog;
66
67    //The parser for reading the catalog
68    SAXParser parser;
69
70    //The current catalog entry
71    CatalogEntry catalogEntry;
72
73    //The current group
74    GroupEntry group;
75
76    //The current entry
77    BaseEntry entry;
78
79    //remove this variable once 8136778 is committed
80    boolean ignoreTheCatalog = false;
81
82    /**
83     * Constructs an instance with a Catalog object and parser.
84     *
85     * @param catalog The Catalog object that represents a catalog
86     */
87    @SuppressWarnings("unchecked")
88    public CatalogReader(Catalog catalog, SAXParser parser) {
89        this.catalog = (CatalogImpl) catalog;
90        this.parser = parser;
91    }
92
93    @Override
94    public void startElement(String namespaceURI,
95            String localName,
96            String qName,
97            Attributes atts)
98            throws SAXException {
99
100        //ignore the catalog if it's not compliant. See section 8, item 3 of the spec.
101        if (ignoreTheCatalog) return;
102        if (!NAMESPACE_OASIS.equals(namespaceURI)) {
103//wait till 8136778 is committed
104//            parser.stop();
105            ignoreTheCatalog = true;
106            return;
107        }
108
109
110        CatalogEntryType type = CatalogEntryType.getType(localName);
111        if (type == null) {
112            CatalogMessages.reportError(CatalogMessages.ERR_INVALID_ENTRY_TYPE,
113                    new Object[]{localName});
114        }
115        if (type != CatalogEntryType.CATALOGENTRY) {
116            if (!seenRoot) {
117                CatalogMessages.reportError(CatalogMessages.ERR_INVALID_CATALOG);
118            }
119        }
120
121        String base = atts.getValue("xml:base");
122        if (base == null) {
123            if (inGroup) {
124                base = group.getBaseURI().toString();
125            } else {
126                if (type == CatalogEntryType.CATALOGENTRY) {
127                    base = catalog.getBaseURI().toString();
128                } else {
129                    base = catalogEntry.getBaseURI().toString();
130                }
131            }
132        } else {
133            base = Normalizer.normalizeURI(base);
134        }
135
136        //parse the catalog and group entries
137        if (type == CatalogEntryType.CATALOGENTRY
138                || type == CatalogEntryType.GROUP) {
139            String prefer = atts.getValue("prefer");
140            if (prefer == null) {
141                if (type == CatalogEntryType.CATALOGENTRY) {
142                    //use the general setting
143                    prefer = catalog.isPreferPublic() ?
144                            CatalogFeatures.PREFER_PUBLIC : CatalogFeatures.PREFER_SYSTEM;
145                } else {
146                    //Group inherit from the catalog entry
147                    prefer = catalogEntry.isPreferPublic() ?
148                            CatalogFeatures.PREFER_PUBLIC : CatalogFeatures.PREFER_SYSTEM;
149                }
150            }
151
152            if (type == CatalogEntryType.CATALOGENTRY) {
153                seenRoot = true;
154                if (catalog.isTop()) {
155                    String defer = atts.getValue("defer");
156                    String resolve = atts.getValue("resolve");
157                    if (defer == null) {
158                        defer = catalog.isDeferred() ?
159                                CatalogFeatures.DEFER_TRUE : CatalogFeatures.DEFER_FALSE;
160                    }
161                    if (resolve == null) {
162                        resolve = catalog.getResolve().literal;
163                    }
164                    //override property settings with those from the catalog file
165                    catalog.setResolve(resolve);
166                    catalog.setDeferred(defer);
167                    catalogEntry = new CatalogEntry(base, prefer, defer, resolve);
168                } else {
169                    catalogEntry = new CatalogEntry(base, prefer);
170                }
171                catalog.setPrefer(prefer);
172                return;
173            } else {
174                inGroup = true;
175                group = new GroupEntry(catalog, base, prefer);
176                catalog.addEntry(group);
177                return;
178            }
179        }
180
181        //parse entries other than the catalog and group entries
182        switch (type) {
183            case PUBLIC:
184                entry = new PublicEntry(base, atts.getValue("publicId"), atts.getValue("uri"));
185                break;
186            case SYSTEM:
187                entry = new SystemEntry(base, atts.getValue("systemId"), atts.getValue("uri"));
188                break;
189            case REWRITESYSTEM:
190                entry = new RewriteSystem(base, atts.getValue("systemIdStartString"), atts.getValue("rewritePrefix"));
191                break;
192            case SYSTEMSUFFIX:
193                entry = new SystemSuffix(base, atts.getValue("systemIdSuffix"), atts.getValue("uri"));
194                break;
195            case DELEGATEPUBLIC:
196                entry = new DelegatePublic(base, atts.getValue("publicIdStartString"), atts.getValue("catalog"));
197                break;
198            case DELEGATESYSTEM:
199                entry = new DelegateSystem(base, atts.getValue("systemIdStartString"), atts.getValue("catalog"));
200                break;
201            case URI:
202                entry = new UriEntry(base, atts.getValue("name"), atts.getValue("uri"));
203                break;
204            case REWRITEURI:
205                entry = new RewriteUri(base, atts.getValue("uriStartString"), atts.getValue("rewritePrefix"));
206                break;
207            case URISUFFIX:
208                entry = new UriSuffix(base, atts.getValue("uriSuffix"), atts.getValue("uri"));
209                break;
210            case DELEGATEURI:
211                entry = new DelegateUri(base, atts.getValue("uriStartString"), atts.getValue("catalog"));
212                break;
213            case NEXTCATALOG:
214                entry = new NextCatalog(base, atts.getValue("catalog"));
215                break;
216        }
217
218        if (type == CatalogEntryType.NEXTCATALOG) {
219            catalog.addNextCatalog((NextCatalog) entry);
220        } else if (inGroup) {
221            group.addEntry(entry);
222        } else {
223            catalog.addEntry(entry);
224        }
225
226    }
227
228    /**
229     * Handles endElement event
230     */
231    @Override
232    public void endElement(String namespaceURI, String localName, String qName)
233            throws SAXException {
234        if (ignoreTheCatalog) return;
235
236        CatalogEntryType type = CatalogEntryType.getType(localName);
237        if (type == CatalogEntryType.GROUP) {
238            inGroup = false;
239        }
240    }
241
242
243    /**
244     * Skips external DTD since resolving external DTD is not required
245     * by the specification.
246     */
247    @Override
248    public InputSource resolveEntity(String publicId, String systemId) {
249        return new InputSource(new StringReader(""));
250    }
251
252    /**
253     * Skips external references since resolution is not required
254     * by the specification.
255     */
256    @Override
257    public Source resolve(String href, String base)
258            throws TransformerException {
259        return new SAXSource(new InputSource(new StringReader("")));
260    }
261
262}
263