1 /*
2  * Copyright (c) 2001, 2018, 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  */
25 package javax.swing.text.html;
26 
27 import javax.swing.text.*;
28 import java.io.Serializable;
29 import java.util.*;
30 
31 /**
32  * An implementation of <code>AttributeSet</code> that can multiplex
33  * across a set of <code>AttributeSet</code>s.
34  *
35  */
36 @SuppressWarnings("serial") // Same-version serialization only
37 class MuxingAttributeSet implements AttributeSet, Serializable {
38     /**
39      * Creates a <code>MuxingAttributeSet</code> with the passed in
40      * attributes.
41      */
MuxingAttributeSet(AttributeSet[] attrs)42     public MuxingAttributeSet(AttributeSet[] attrs) {
43         this.attrs = attrs;
44     }
45 
46     /**
47      * Creates an empty <code>MuxingAttributeSet</code>. This is intended for
48      * use by subclasses only, and it is also intended that subclasses will
49      * set the constituent <code>AttributeSet</code>s before invoking any
50      * of the <code>AttributeSet</code> methods.
51      */
MuxingAttributeSet()52     protected MuxingAttributeSet() {
53     }
54 
55     /**
56      * Directly sets the <code>AttributeSet</code>s that comprise this
57      * <code>MuxingAttributeSet</code>.
58      */
setAttributes(AttributeSet[] attrs)59     protected synchronized void setAttributes(AttributeSet[] attrs) {
60         this.attrs = attrs;
61     }
62 
63     /**
64      * Returns the <code>AttributeSet</code>s multiplexing too. When the
65      * <code>AttributeSet</code>s need to be referenced, this should be called.
66      */
getAttributes()67     protected synchronized AttributeSet[] getAttributes() {
68         return attrs;
69     }
70 
71     /**
72      * Inserts <code>as</code> at <code>index</code>. This assumes
73      * the value of <code>index</code> is between 0 and attrs.length,
74      * inclusive.
75      */
insertAttributeSetAt(AttributeSet as, int index)76     protected synchronized void insertAttributeSetAt(AttributeSet as,
77                                                      int index) {
78         int numAttrs = attrs.length;
79         AttributeSet[] newAttrs = new AttributeSet[numAttrs + 1];
80         if (index < numAttrs) {
81             if (index > 0) {
82                 System.arraycopy(attrs, 0, newAttrs, 0, index);
83                 System.arraycopy(attrs, index, newAttrs, index + 1,
84                                  numAttrs - index);
85             }
86             else {
87                 System.arraycopy(attrs, 0, newAttrs, 1, numAttrs);
88             }
89         }
90         else {
91             System.arraycopy(attrs, 0, newAttrs, 0, numAttrs);
92         }
93         newAttrs[index] = as;
94         attrs = newAttrs;
95     }
96 
97     /**
98      * Removes the AttributeSet at <code>index</code>. This assumes
99      * the value of <code>index</code> is greater than or equal to 0,
100      * and less than attrs.length.
101      */
removeAttributeSetAt(int index)102     protected synchronized void removeAttributeSetAt(int index) {
103         int numAttrs = attrs.length;
104         AttributeSet[] newAttrs = new AttributeSet[numAttrs - 1];
105         if (numAttrs > 0) {
106             if (index == 0) {
107                 // FIRST
108                 System.arraycopy(attrs, 1, newAttrs, 0, numAttrs - 1);
109             }
110             else if (index < (numAttrs - 1)) {
111                 // MIDDLE
112                 System.arraycopy(attrs, 0, newAttrs, 0, index);
113                 System.arraycopy(attrs, index + 1, newAttrs, index,
114                                  numAttrs - index - 1);
115             }
116             else {
117                 // END
118                 System.arraycopy(attrs, 0, newAttrs, 0, numAttrs - 1);
119             }
120         }
121         attrs = newAttrs;
122     }
123 
124     //  --- AttributeSet methods ----------------------------
125 
126     /**
127      * Gets the number of attributes that are defined.
128      *
129      * @return the number of attributes
130      * @see AttributeSet#getAttributeCount
131      */
getAttributeCount()132     public int getAttributeCount() {
133         AttributeSet[] as = getAttributes();
134         int n = 0;
135         for (int i = 0; i < as.length; i++) {
136             n += as[i].getAttributeCount();
137         }
138         return n;
139     }
140 
141     /**
142      * Checks whether a given attribute is defined.
143      * This will convert the key over to CSS if the
144      * key is a StyleConstants key that has a CSS
145      * mapping.
146      *
147      * @param key the attribute key
148      * @return true if the attribute is defined
149      * @see AttributeSet#isDefined
150      */
isDefined(Object key)151     public boolean isDefined(Object key) {
152         AttributeSet[] as = getAttributes();
153         for (int i = 0; i < as.length; i++) {
154             if (as[i].isDefined(key)) {
155                 return true;
156             }
157         }
158         return false;
159     }
160 
161     /**
162      * Checks whether two attribute sets are equal.
163      *
164      * @param attr the attribute set to check against
165      * @return true if the same
166      * @see AttributeSet#isEqual
167      */
isEqual(AttributeSet attr)168     public boolean isEqual(AttributeSet attr) {
169         return ((getAttributeCount() == attr.getAttributeCount()) &&
170                 containsAttributes(attr));
171     }
172 
173     /**
174      * Copies a set of attributes.
175      *
176      * @return the copy
177      * @see AttributeSet#copyAttributes
178      */
copyAttributes()179     public AttributeSet copyAttributes() {
180         AttributeSet[] as = getAttributes();
181         MutableAttributeSet a = new SimpleAttributeSet();
182         int n = 0;
183         for (int i = as.length - 1; i >= 0; i--) {
184             a.addAttributes(as[i]);
185         }
186         return a;
187     }
188 
189     /**
190      * Gets the value of an attribute.  If the requested
191      * attribute is a StyleConstants attribute that has
192      * a CSS mapping, the request will be converted.
193      *
194      * @param key the attribute name
195      * @return the attribute value
196      * @see AttributeSet#getAttribute
197      */
getAttribute(Object key)198     public Object getAttribute(Object key) {
199         AttributeSet[] as = getAttributes();
200         int n = as.length;
201         for (int i = 0; i < n; i++) {
202             Object o = as[i].getAttribute(key);
203             if (o != null) {
204                 return o;
205             }
206         }
207         return null;
208     }
209 
210     /**
211      * Gets the names of all attributes.
212      *
213      * @return the attribute names
214      * @see AttributeSet#getAttributeNames
215      */
getAttributeNames()216     public Enumeration<?> getAttributeNames() {
217         return new MuxingAttributeNameEnumeration();
218     }
219 
220     /**
221      * Checks whether a given attribute name/value is defined.
222      *
223      * @param name the attribute name
224      * @param value the attribute value
225      * @return true if the name/value is defined
226      * @see AttributeSet#containsAttribute
227      */
containsAttribute(Object name, Object value)228     public boolean containsAttribute(Object name, Object value) {
229         return value.equals(getAttribute(name));
230     }
231 
232     /**
233      * Checks whether the attribute set contains all of
234      * the given attributes.
235      *
236      * @param attrs the attributes to check
237      * @return true if the element contains all the attributes
238      * @see AttributeSet#containsAttributes
239      */
containsAttributes(AttributeSet attrs)240     public boolean containsAttributes(AttributeSet attrs) {
241         boolean result = true;
242 
243         Enumeration<?> names = attrs.getAttributeNames();
244         while (result && names.hasMoreElements()) {
245             Object name = names.nextElement();
246             result = attrs.getAttribute(name).equals(getAttribute(name));
247         }
248 
249         return result;
250     }
251 
252     /**
253      * Returns null, subclasses may wish to do something more
254      * intelligent with this.
255      */
getResolveParent()256     public AttributeSet getResolveParent() {
257         return null;
258     }
259 
260     /**
261      * The <code>AttributeSet</code>s that make up the resulting
262      * <code>AttributeSet</code>.
263      */
264     private AttributeSet[] attrs;
265 
266 
267     /**
268      * An Enumeration of the Attribute names in a MuxingAttributeSet.
269      * This may return the same name more than once.
270      */
271     private class MuxingAttributeNameEnumeration implements Enumeration<Object> {
272 
MuxingAttributeNameEnumeration()273         MuxingAttributeNameEnumeration() {
274             updateEnum();
275         }
276 
hasMoreElements()277         public boolean hasMoreElements() {
278             if (currentEnum == null) {
279                 return false;
280             }
281             return currentEnum.hasMoreElements();
282         }
283 
nextElement()284         public Object nextElement() {
285             if (currentEnum == null) {
286                 throw new NoSuchElementException("No more names");
287             }
288             Object retObject = currentEnum.nextElement();
289             if (!currentEnum.hasMoreElements()) {
290                 updateEnum();
291             }
292             return retObject;
293         }
294 
updateEnum()295         void updateEnum() {
296             AttributeSet[] as = getAttributes();
297             currentEnum = null;
298             while (currentEnum == null && attrIndex < as.length) {
299                 currentEnum = as[attrIndex++].getAttributeNames();
300                 if (!currentEnum.hasMoreElements()) {
301                     currentEnum = null;
302                 }
303             }
304         }
305 
306 
307         /** Index into attrs the current Enumeration came from. */
308         private int attrIndex;
309         /** Enumeration from attrs. */
310         private Enumeration<?> currentEnum;
311     }
312 }
313