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