1/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/*
6 * Licensed to the Apache Software Foundation (ASF) under one or more
7 * contributor license agreements.  See the NOTICE file distributed with
8 * this work for additional information regarding copyright ownership.
9 * The ASF licenses this file to You under the Apache License, Version 2.0
10 * (the "License"); you may not use this file except in compliance with
11 * the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22package com.sun.org.apache.xerces.internal.impl.dtd;
23
24import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
25import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
26import com.sun.org.apache.xerces.internal.util.XMLSymbols;
27import com.sun.org.apache.xerces.internal.xni.Augmentations;
28import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
29import com.sun.org.apache.xerces.internal.xni.QName;
30import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
31import com.sun.org.apache.xerces.internal.xni.XNIException;
32
33/**
34 * The DTD validator. The validator implements a document
35 * filter: receiving document events from the scanner; validating
36 * the content and structure; augmenting the InfoSet, if applicable;
37 * and notifying the parser of the information resulting from the
38 * validation process.
39 * <p> Formerly, this component also handled DTD events and grammar construction.
40 * To facilitate the development of a meaningful DTD grammar caching/preparsing
41 * framework, this functionality has been moved into the XMLDTDLoader
42 * class.  Therefore, this class no longer implements the DTDFilter
43 * or DTDContentModelFilter interfaces.
44 * <p>
45 * This component requires the following features and properties from the
46 * component manager that uses it:
47 * <ul>
48 *  <li>http://xml.org/sax/features/namespaces</li>
49 *  <li>http://xml.org/sax/features/validation</li>
50 *  <li>http://apache.org/xml/features/validation/dynamic</li>
51 *  <li>http://apache.org/xml/properties/internal/symbol-table</li>
52 *  <li>http://apache.org/xml/properties/internal/error-reporter</li>
53 *  <li>http://apache.org/xml/properties/internal/grammar-pool</li>
54 *  <li>http://apache.org/xml/properties/internal/datatype-validator-factory</li>
55 * </ul>
56 *
57 * @xerces.internal
58 *
59 * @author Elena Litani, IBM
60 * @author Michael Glavassevich, IBM
61 *
62
63 */
64public class XML11NSDTDValidator extends XML11DTDValidator {
65
66    /** Attribute QName. */
67    private QName fAttributeQName = new QName();
68
69    /** Bind namespaces */
70    protected final void startNamespaceScope(QName element, XMLAttributes attributes, Augmentations augs)
71        throws XNIException {
72
73        // add new namespace context
74        fNamespaceContext.pushContext();
75
76        if (element.prefix == XMLSymbols.PREFIX_XMLNS) {
77            fErrorReporter.reportError(
78                XMLMessageFormatter.XMLNS_DOMAIN,
79                "ElementXMLNSPrefix",
80                new Object[] { element.rawname },
81                XMLErrorReporter.SEVERITY_FATAL_ERROR);
82        }
83
84        // search for new namespace bindings
85        int length = attributes.getLength();
86        for (int i = 0; i < length; i++) {
87            String localpart = attributes.getLocalName(i);
88            String prefix = attributes.getPrefix(i);
89            // when it's of form xmlns="..." or xmlns:prefix="...",
90            // it's a namespace declaration. but prefix:xmlns="..." isn't.
91            if (prefix == XMLSymbols.PREFIX_XMLNS || prefix == XMLSymbols.EMPTY_STRING
92                && localpart == XMLSymbols.PREFIX_XMLNS) {
93
94                // get the internalized value of this attribute
95                String uri = fSymbolTable.addSymbol(attributes.getValue(i));
96
97                // 1. "xmlns" can't be bound to any namespace
98                if (prefix == XMLSymbols.PREFIX_XMLNS && localpart == XMLSymbols.PREFIX_XMLNS) {
99                    fErrorReporter.reportError(
100                        XMLMessageFormatter.XMLNS_DOMAIN,
101                        "CantBindXMLNS",
102                        new Object[] { attributes.getQName(i)},
103                        XMLErrorReporter.SEVERITY_FATAL_ERROR);
104                }
105
106                // 2. the namespace for "xmlns" can't be bound to any prefix
107                if (uri == NamespaceContext.XMLNS_URI) {
108                    fErrorReporter.reportError(
109                        XMLMessageFormatter.XMLNS_DOMAIN,
110                        "CantBindXMLNS",
111                        new Object[] { attributes.getQName(i)},
112                        XMLErrorReporter.SEVERITY_FATAL_ERROR);
113                }
114
115                // 3. "xml" can't be bound to any other namespace than it's own
116                if (localpart == XMLSymbols.PREFIX_XML) {
117                    if (uri != NamespaceContext.XML_URI) {
118                        fErrorReporter.reportError(
119                            XMLMessageFormatter.XMLNS_DOMAIN,
120                            "CantBindXML",
121                            new Object[] { attributes.getQName(i)},
122                            XMLErrorReporter.SEVERITY_FATAL_ERROR);
123                    }
124                }
125                // 4. the namespace for "xml" can't be bound to any other prefix
126                else {
127                    if (uri == NamespaceContext.XML_URI) {
128                        fErrorReporter.reportError(
129                            XMLMessageFormatter.XMLNS_DOMAIN,
130                            "CantBindXML",
131                            new Object[] { attributes.getQName(i)},
132                            XMLErrorReporter.SEVERITY_FATAL_ERROR);
133                    }
134                }
135
136                prefix = localpart != XMLSymbols.PREFIX_XMLNS ? localpart : XMLSymbols.EMPTY_STRING;
137
138                                // Declare prefix in context. Removing the association between a prefix and a
139                                // namespace name is permitted in XML 1.1, so if the uri value is the empty string,
140                                // the prefix is being unbound. -- mrglavas
141                fNamespaceContext.declarePrefix(prefix, uri.length() != 0 ? uri : null);
142            }
143        }
144
145        // bind the element
146        String prefix = element.prefix != null ? element.prefix : XMLSymbols.EMPTY_STRING;
147        element.uri = fNamespaceContext.getURI(prefix);
148        if (element.prefix == null && element.uri != null) {
149            element.prefix = XMLSymbols.EMPTY_STRING;
150        }
151        if (element.prefix != null && element.uri == null) {
152            fErrorReporter.reportError(
153                XMLMessageFormatter.XMLNS_DOMAIN,
154                "ElementPrefixUnbound",
155                new Object[] { element.prefix, element.rawname },
156                XMLErrorReporter.SEVERITY_FATAL_ERROR);
157        }
158
159        // bind the attributes
160        for (int i = 0; i < length; i++) {
161            attributes.getName(i, fAttributeQName);
162            String aprefix = fAttributeQName.prefix != null ? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING;
163            String arawname = fAttributeQName.rawname;
164            if (arawname == XMLSymbols.PREFIX_XMLNS) {
165                fAttributeQName.uri = fNamespaceContext.getURI(XMLSymbols.PREFIX_XMLNS);
166                attributes.setName(i, fAttributeQName);
167            } else if (aprefix != XMLSymbols.EMPTY_STRING) {
168                fAttributeQName.uri = fNamespaceContext.getURI(aprefix);
169                if (fAttributeQName.uri == null) {
170                    fErrorReporter.reportError(
171                        XMLMessageFormatter.XMLNS_DOMAIN,
172                        "AttributePrefixUnbound",
173                        new Object[] { element.rawname, arawname, aprefix },
174                        XMLErrorReporter.SEVERITY_FATAL_ERROR);
175                }
176                attributes.setName(i, fAttributeQName);
177            }
178        }
179
180        // verify that duplicate attributes don't exist
181        // Example: <foo xmlns:a='NS' xmlns:b='NS' a:attr='v1' b:attr='v2'/>
182        int attrCount = attributes.getLength();
183        for (int i = 0; i < attrCount - 1; i++) {
184            String auri = attributes.getURI(i);
185            if (auri == null || auri == NamespaceContext.XMLNS_URI) {
186                continue;
187            }
188            String alocalpart = attributes.getLocalName(i);
189            for (int j = i + 1; j < attrCount; j++) {
190                String blocalpart = attributes.getLocalName(j);
191                String buri = attributes.getURI(j);
192                if (alocalpart == blocalpart && auri == buri) {
193                    fErrorReporter.reportError(
194                        XMLMessageFormatter.XMLNS_DOMAIN,
195                        "AttributeNSNotUnique",
196                        new Object[] { element.rawname, alocalpart, auri },
197                        XMLErrorReporter.SEVERITY_FATAL_ERROR);
198                }
199            }
200        }
201
202    } // startNamespaceScope(QName,XMLAttributes)
203
204    /** Handles end element. */
205    protected void endNamespaceScope(QName element, Augmentations augs, boolean isEmpty)
206        throws XNIException {
207
208        // bind element
209        String eprefix = element.prefix != null ? element.prefix : XMLSymbols.EMPTY_STRING;
210        element.uri = fNamespaceContext.getURI(eprefix);
211        if (element.uri != null) {
212            element.prefix = eprefix;
213        }
214
215        // call handlers
216        if (fDocumentHandler != null) {
217            if (!isEmpty) {
218                fDocumentHandler.endElement(element, augs);
219            }
220        }
221
222        // pop context
223        fNamespaceContext.popContext();
224
225    } // endNamespaceScope(QName,boolean)
226}
227