| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.jasper.util; |
| |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import org.apache.jasper.compiler.Localizer; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.helpers.AttributesImpl; |
| |
| /** |
| * Wraps the default attributes implementation and ensures that each attribute |
| * has a unique qname as required by the JSP specification. |
| */ |
| public class UniqueAttributesImpl extends AttributesImpl { |
| |
| private static final String IMPORT = "import"; |
| private static final String PAGE_ENCODING = "pageEncoding"; |
| |
| private final boolean pageDirective; |
| private final Set<String> qNames = new HashSet<>(); |
| |
| public UniqueAttributesImpl() { |
| this.pageDirective = false; |
| } |
| |
| public UniqueAttributesImpl(boolean pageDirective) { |
| this.pageDirective = pageDirective; |
| } |
| |
| @Override |
| public void clear() { |
| qNames.clear(); |
| super.clear(); |
| } |
| |
| @Override |
| public void setAttributes(Attributes atts) { |
| for (int i = 0; i < atts.getLength(); i++) { |
| if (!qNames.add(atts.getQName(i))) { |
| handleDuplicate(atts.getQName(i), atts.getValue(i)); |
| } |
| } |
| super.setAttributes(atts); |
| } |
| |
| @Override |
| public void addAttribute(String uri, String localName, String qName, |
| String type, String value) { |
| if (qNames.add(qName)) { |
| super.addAttribute(uri, localName, qName, type, value); |
| } else { |
| handleDuplicate(qName, value); |
| } |
| } |
| |
| @Override |
| public void setAttribute(int index, String uri, String localName, |
| String qName, String type, String value) { |
| qNames.remove(super.getQName(index)); |
| if (qNames.add(qName)) { |
| super.setAttribute(index, uri, localName, qName, type, value); |
| } else { |
| handleDuplicate(qName, value); |
| } |
| } |
| |
| @Override |
| public void removeAttribute(int index) { |
| qNames.remove(super.getQName(index)); |
| super.removeAttribute(index); |
| } |
| |
| @Override |
| public void setQName(int index, String qName) { |
| qNames.remove(super.getQName(index)); |
| super.setQName(index, qName); |
| } |
| |
| private void handleDuplicate(String qName, String value) { |
| if (pageDirective) { |
| if (IMPORT.equalsIgnoreCase(qName)) { |
| // Always merge imports |
| int i = super.getIndex(IMPORT); |
| String v = super.getValue(i); |
| super.setValue(i, v + "," + value); |
| return; |
| } else if (PAGE_ENCODING.equalsIgnoreCase(qName)) { |
| // Page encoding can only occur once per file so a second |
| // attribute - even one with a duplicate value - is an error |
| } else { |
| // Other attributes can be repeated if and only if the values |
| // are identical |
| String v = super.getValue(qName); |
| if (v.equals(value)) { |
| return; |
| } |
| } |
| } |
| |
| // Ordinary tag attributes can't be repeated, even with identical values |
| throw new IllegalArgumentException( |
| Localizer.getMessage("jsp.error.duplicateqname", qName)); |
| } |
| } |