Frames | No Frames |
1: /* StyleSheet.java -- 2: Copyright (C) 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.swing.text.html; 40: 41: import gnu.javax.swing.text.html.CharacterAttributeTranslator; 42: 43: import java.awt.Color; 44: import java.awt.Font; 45: import java.awt.Graphics; 46: 47: import java.io.IOException; 48: import java.io.Reader; 49: import java.io.Serializable; 50: import java.io.StringReader; 51: 52: import java.net.MalformedURLException; 53: import java.net.URL; 54: 55: import java.util.Enumeration; 56: import java.util.Vector; 57: 58: import javax.swing.text.AttributeSet; 59: import javax.swing.text.Element; 60: import javax.swing.text.MutableAttributeSet; 61: import javax.swing.text.SimpleAttributeSet; 62: import javax.swing.text.Style; 63: import javax.swing.text.StyleContext; 64: import javax.swing.text.View; 65: 66: 67: /** 68: * This class adds support for defining the visual characteristics of HTML views 69: * being rendered. This enables views to be customized by a look-and-feel, mulitple 70: * views over the same model can be rendered differently. Each EditorPane has its 71: * own StyleSheet, but by default one sheet will be shared by all of the HTMLEditorKit 72: * instances. An HTMLDocument can also have a StyleSheet, which holds specific CSS 73: * specs. 74: * 75: * In order for Views to store less state and therefore be more lightweight, 76: * the StyleSheet can act as a factory for painters that handle some of the 77: * rendering tasks. Since the StyleSheet may be used by views over multiple 78: * documents the HTML attributes don't effect the selector being used. 79: * 80: * The rules are stored as named styles, and other information is stored to 81: * translate the context of an element to a rule. 82: * 83: * @author Lillian Angel (langel@redhat.com) 84: */ 85: public class StyleSheet extends StyleContext 86: { 87: 88: /** The base URL */ 89: URL base; 90: 91: /** Base font size (int) */ 92: int baseFontSize; 93: 94: /** The style sheets stored. */ 95: StyleSheet[] styleSheet; 96: 97: /** 98: * Constructs a StyleSheet. 99: */ 100: public StyleSheet() 101: { 102: super(); 103: baseFontSize = 4; // Default font size from CSS 104: } 105: 106: /** 107: * Gets the style used to render the given tag. The element represents the tag 108: * and can be used to determine the nesting, where the attributes will differ 109: * if there is nesting inside of elements. 110: * 111: * @param t - the tag to translate to visual attributes 112: * @param e - the element representing the tag 113: * @return the set of CSS attributes to use to render the tag. 114: */ 115: public Style getRule(HTML.Tag t, Element e) 116: { 117: // FIXME: Not implemented. 118: return null; 119: } 120: 121: /** 122: * Gets the rule that best matches the selector. selector is a space 123: * separated String of element names. The attributes of the returned 124: * Style will change as rules are added and removed. 125: * 126: * @param selector - the element names separated by spaces 127: * @return the set of CSS attributes to use to render 128: */ 129: public Style getRule(String selector) 130: { 131: // FIXME: Not implemented. 132: return null; 133: } 134: 135: /** 136: * Adds a set if rules to the sheet. The rules are expected to be in valid 137: * CSS format. This is called as a result of parsing a <style> tag 138: * 139: * @param rule - the rule to add to the sheet 140: */ 141: public void addRule(String rule) 142: { 143: CssParser cp = new CssParser(); 144: try 145: { 146: cp.parse(base, new StringReader(rule), false, false); 147: } 148: catch (IOException io) 149: { 150: // Do nothing here. 151: } 152: } 153: 154: /** 155: * Translates a CSS declaration into an AttributeSet. This is called 156: * as a result of encountering an HTML style attribute. 157: * 158: * @param decl - the declaration to get 159: * @return the AttributeSet representing the declaration 160: */ 161: public AttributeSet getDeclaration(String decl) 162: { 163: if (decl == null) 164: return SimpleAttributeSet.EMPTY; 165: // FIXME: Not implemented. 166: return null; 167: } 168: 169: /** 170: * Loads a set of rules that have been specified in terms of CSS grammar. 171: * If there are any conflicts with existing rules, the new rule is added. 172: * 173: * @param in - the stream to read the CSS grammar from. 174: * @param ref - the reference URL. It is the location of the stream, it may 175: * be null. All relative URLs specified in the stream will be based upon this 176: * parameter. 177: * @throws IOException - For any IO error while reading 178: */ 179: public void loadRules(Reader in, URL ref) throws IOException 180: { 181: CssParser cp = new CssParser(); 182: cp.parse(ref, in, false, false); 183: } 184: 185: /** 186: * Gets a set of attributes to use in the view. This is a set of 187: * attributes that can be used for View.getAttributes 188: * 189: * @param v - the view to get the set for 190: * @return the AttributeSet to use in the view. 191: */ 192: public AttributeSet getViewAttributes(View v) 193: { 194: // FIXME: Not implemented. 195: return null; 196: } 197: 198: /** 199: * Removes a style previously added. 200: * 201: * @param nm - the name of the style to remove 202: */ 203: public void removeStyle(String nm) 204: { 205: // FIXME: Not implemented. 206: super.removeStyle(nm); 207: } 208: 209: /** 210: * Adds the rules from ss to those of the receiver. ss's rules will 211: * override the old rules. An added StyleSheet will never override the rules 212: * of the receiving style sheet. 213: * 214: * @param ss - the new StyleSheet. 215: */ 216: public void addStyleSheet(StyleSheet ss) 217: { 218: if (styleSheet == null) 219: styleSheet = new StyleSheet[] {ss}; 220: else 221: System.arraycopy(new StyleSheet[] {ss}, 0, styleSheet, 222: styleSheet.length, 1); 223: } 224: 225: /** 226: * Removes ss from those of the receiver 227: * 228: * @param ss - the StyleSheet to remove. 229: */ 230: public void removeStyleSheet(StyleSheet ss) 231: { 232: if (styleSheet.length == 1 && styleSheet[0].equals(ss)) 233: styleSheet = null; 234: else 235: { 236: for (int i = 0; i < styleSheet.length; i++) 237: { 238: StyleSheet curr = styleSheet[i]; 239: if (curr.equals(ss)) 240: { 241: StyleSheet[] tmp = new StyleSheet[styleSheet.length - 1]; 242: if (i != 0 && i != (styleSheet.length - 1)) 243: { 244: System.arraycopy(styleSheet, 0, tmp, 0, i); 245: System.arraycopy(styleSheet, i + 1, tmp, i, 246: styleSheet.length - i - 1); 247: } 248: else if (i == 0) 249: System.arraycopy(styleSheet, 1, tmp, 0, styleSheet.length - 1); 250: else 251: System.arraycopy(styleSheet, 0, tmp, 0, styleSheet.length - 1); 252: 253: styleSheet = tmp; 254: break; 255: } 256: } 257: } 258: } 259: 260: /** 261: * Returns an array of the linked StyleSheets. May return null. 262: * 263: * @return - An array of the linked StyleSheets. 264: */ 265: public StyleSheet[] getStyleSheets() 266: { 267: return styleSheet; 268: } 269: 270: /** 271: * Imports a style sheet from the url. The rules are directly added to the 272: * receiver. 273: * 274: * @param url - the URL to import the StyleSheet from. 275: */ 276: public void importStyleSheet(URL url) 277: { 278: // FIXME: Not implemented 279: } 280: 281: /** 282: * Sets the base url. All import statements that are relative, will be 283: * relative to base. 284: * 285: * @param base - 286: * the base URL. 287: */ 288: public void setBase(URL base) 289: { 290: this.base = base; 291: } 292: 293: /** 294: * Gets the base url. 295: * 296: * @return - the base 297: */ 298: public URL getBase() 299: { 300: return base; 301: } 302: 303: /** 304: * Adds a CSS attribute to the given set. 305: * 306: * @param attr - the attribute set 307: * @param key - the attribute to add 308: * @param value - the value of the key 309: */ 310: public void addCSSAttribute(MutableAttributeSet attr, CSS.Attribute key, 311: String value) 312: { 313: attr.addAttribute(key, value); 314: } 315: 316: /** 317: * Adds a CSS attribute to the given set. 318: * This method parses the value argument from HTML based on key. 319: * Returns true if it finds a valid value for the given key, 320: * and false otherwise. 321: * 322: * @param attr - the attribute set 323: * @param key - the attribute to add 324: * @param value - the value of the key 325: * @return true if a valid value was found. 326: */ 327: public boolean addCSSAttributeFromHTML(MutableAttributeSet attr, CSS.Attribute key, 328: String value) 329: { 330: // FIXME: Need to parse value from HTML based on key. 331: attr.addAttribute(key, value); 332: return attr.containsAttribute(key, value); 333: } 334: 335: /** 336: * Converts a set of HTML attributes to an equivalent set of CSS attributes. 337: * 338: * @param htmlAttrSet - the set containing the HTML attributes. 339: * @return the set of CSS attributes 340: */ 341: public AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet) 342: { 343: // FIXME: Not implemented. 344: return null; 345: } 346: 347: /** 348: * Adds an attribute to the given set and returns a new set. This is implemented 349: * to convert StyleConstants attributes to CSS before forwarding them to the superclass. 350: * The StyleConstants attribute do not have corresponding CSS entry, the attribute 351: * is stored (but will likely not be used). 352: * 353: * @param old - the old set 354: * @param key - the non-null attribute key 355: * @param value - the attribute value 356: * @return the updated set 357: */ 358: public AttributeSet addAttribute(AttributeSet old, Object key, 359: Object value) 360: { 361: // FIXME: Not implemented. 362: return super.addAttribute(old, key, value); 363: } 364: 365: /** 366: * Adds a set of attributes to the element. If any of these attributes are 367: * StyleConstants, they will be converted to CSS before forwarding to the 368: * superclass. 369: * 370: * @param old - the old set 371: * @param attr - the attributes to add 372: * @return the updated attribute set 373: */ 374: public AttributeSet addAttributes(AttributeSet old, AttributeSet attr) 375: { 376: // FIXME: Not implemented. 377: return super.addAttributes(old, attr); 378: } 379: 380: /** 381: * Removes an attribute from the set. If the attribute is a 382: * StyleConstants, it will be converted to CSS before forwarding to the 383: * superclass. 384: * 385: * @param old - the old set 386: * @param key - the non-null attribute key 387: * @return the updated set 388: */ 389: public AttributeSet removeAttribute(AttributeSet old, Object key) 390: { 391: // FIXME: Not implemented. 392: return super.removeAttribute(old, key); 393: } 394: 395: /** 396: * Removes an attribute from the set. If any of the attributes are 397: * StyleConstants, they will be converted to CSS before forwarding to the 398: * superclass. 399: * 400: * @param old - the old set 401: * @param attrs - the attributes to remove 402: * @return the updated set 403: */ 404: public AttributeSet removeAttributes(AttributeSet old, AttributeSet attrs) 405: { 406: // FIXME: Not implemented. 407: return super.removeAttributes(old, attrs); 408: } 409: 410: /** 411: * Removes a set of attributes for the element. If any of the attributes is a 412: * StyleConstants, they will be converted to CSS before forwarding to the 413: * superclass. 414: * 415: * @param old - the old attribute set 416: * @param names - the attribute names 417: * @return the update attribute set 418: */ 419: public AttributeSet removeAttributes(AttributeSet old, Enumeration names) 420: { 421: // FIXME: Not implemented. 422: return super.removeAttributes(old, names); 423: } 424: 425: /** 426: * Creates a compact set of attributes that might be shared. This is a hook 427: * for subclasses that want to change the behaviour of SmallAttributeSet. 428: * 429: * @param a - the set of attributes to be represented in the compact form. 430: * @return the set of attributes created 431: */ 432: protected StyleContext.SmallAttributeSet createSmallAttributeSet(AttributeSet a) 433: { 434: return super.createSmallAttributeSet(a); 435: } 436: 437: /** 438: * Creates a large set of attributes. This set is not shared. This is a hook 439: * for subclasses that want to change the behaviour of the larger attribute 440: * storage format. 441: * 442: * @param a - the set of attributes to be represented in the larger form. 443: * @return the large set of attributes. 444: */ 445: protected MutableAttributeSet createLargeAttributeSet(AttributeSet a) 446: { 447: return super.createLargeAttributeSet(a); 448: } 449: 450: /** 451: * Gets the font to use for the given set. 452: * 453: * @param a - the set to get the font for. 454: * @return the font for the set 455: */ 456: public Font getFont(AttributeSet a) 457: { 458: return super.getFont(a); 459: } 460: 461: /** 462: * Takes a set of attributes and turns it into a foreground 463: * color specification. This is used to specify things like, brigher, more hue 464: * etc. 465: * 466: * @param a - the set to get the foreground color for 467: * @return the foreground color for the set 468: */ 469: public Color getForeground(AttributeSet a) 470: { 471: return super.getForeground(a); 472: } 473: 474: /** 475: * Takes a set of attributes and turns it into a background 476: * color specification. This is used to specify things like, brigher, more hue 477: * etc. 478: * 479: * @param a - the set to get the background color for 480: * @return the background color for the set 481: */ 482: public Color getBackground(AttributeSet a) 483: { 484: return super.getBackground(a); 485: } 486: 487: /** 488: * Gets the box formatter to use for the given set of CSS attributes. 489: * 490: * @param a - the given set 491: * @return the box formatter 492: */ 493: public BoxPainter getBoxPainter(AttributeSet a) 494: { 495: return new BoxPainter(a); 496: } 497: 498: /** 499: * Gets the list formatter to use for the given set of CSS attributes. 500: * 501: * @param a - the given set 502: * @return the list formatter 503: */ 504: public ListPainter getListPainter(AttributeSet a) 505: { 506: return new ListPainter(a); 507: } 508: 509: /** 510: * Sets the base font size between 1 and 7. 511: * 512: * @param sz - the new font size for the base. 513: */ 514: public void setBaseFontSize(int sz) 515: { 516: if (sz <= 7 && sz >= 1) 517: baseFontSize = sz; 518: } 519: 520: /** 521: * Sets the base font size from the String. It can either identify 522: * a specific font size (between 1 and 7) or identify a relative 523: * font size such as +1 or -2. 524: * 525: * @param size - the new font size as a String. 526: */ 527: public void setBaseFontSize(String size) 528: { 529: size.trim(); 530: int temp = 0; 531: try 532: { 533: if (size.length() == 2) 534: { 535: int i = new Integer(size.substring(1)).intValue(); 536: if (size.startsWith("+")) 537: temp = baseFontSize + i; 538: else if (size.startsWith("-")) 539: temp = baseFontSize - i; 540: } 541: else if (size.length() == 1) 542: temp = new Integer(size.substring(0)).intValue(); 543: 544: if (temp <= 7 && temp >= 1) 545: baseFontSize = temp; 546: } 547: catch (NumberFormatException nfe) 548: { 549: // Do nothing here 550: } 551: } 552: 553: /** 554: * TODO 555: * 556: * @param pt - TODO 557: * @return TODO 558: */ 559: public static int getIndexOfSize(float pt) 560: { 561: // FIXME: Not implemented. 562: return 0; 563: } 564: 565: /** 566: * Gets the point size, given a size index. 567: * 568: * @param index - the size index 569: * @return the point size. 570: */ 571: public float getPointSize(int index) 572: { 573: // FIXME: Not implemented. 574: return 0; 575: } 576: 577: /** 578: * Given the string of the size, returns the point size value. 579: * 580: * @param size - the string representation of the size. 581: * @return - the point size value. 582: */ 583: public float getPointSize(String size) 584: { 585: // FIXME: Not implemented. 586: return 0; 587: } 588: 589: /** 590: * Convert the color string represenation into java.awt.Color. The valid 591: * values are like "aqua" , "#00FFFF" or "rgb(1,6,44)". 592: * 593: * @param colorName the color to convert. 594: * @return the matching java.awt.color 595: */ 596: public Color stringToColor(String colorName) 597: { 598: return CharacterAttributeTranslator.getColor(colorName); 599: } 600: 601: /** 602: * This class carries out some of the duties of CSS formatting. This enables views 603: * to present the CSS formatting while not knowing how the CSS values are cached. 604: * 605: * This object is reponsible for the insets of a View and making sure 606: * the background is maintained according to the CSS attributes. 607: * 608: * @author Lillian Angel (langel@redhat.com) 609: */ 610: public static class BoxPainter extends Object implements Serializable 611: { 612: 613: /** 614: * Attribute set for painter 615: */ 616: AttributeSet as; 617: 618: /** 619: * Package-private constructor. 620: * 621: * @param as - AttributeSet for painter 622: */ 623: BoxPainter(AttributeSet as) 624: { 625: this.as = as; 626: } 627: 628: /** 629: * Gets the inset needed on a given side to account for the margin, border 630: * and padding. 631: * 632: * @param size - the size of the box to get the inset for. View.TOP, View.LEFT, 633: * View.BOTTOM or View.RIGHT. 634: * @param v - the view making the request. This is used to get the AttributeSet, 635: * amd may be used to resolve percentage arguments. 636: * @return the inset 637: * @throws IllegalArgumentException - for an invalid direction. 638: */ 639: public float getInset(int size, View v) 640: { 641: // FIXME: Not implemented. 642: return 0; 643: } 644: 645: /** 646: * Paints the CSS box according to the attributes given. This should 647: * paint the border, padding and background. 648: * 649: * @param g - the graphics configuration 650: * @param x - the x coordinate 651: * @param y - the y coordinate 652: * @param w - the width of the allocated area 653: * @param h - the height of the allocated area 654: * @param v - the view making the request 655: */ 656: public void paint(Graphics g, float x, float y, float w, float h, View v) 657: { 658: // FIXME: Not implemented. 659: } 660: } 661: 662: /** 663: * This class carries out some of the CSS list formatting duties. Implementations 664: * of this class enable views to present the CSS formatting while not knowing anything 665: * about how the CSS values are being cached. 666: * 667: * @author Lillian Angel (langel@redhat.com) 668: */ 669: public static class ListPainter extends Object implements Serializable 670: { 671: 672: /** 673: * Attribute set for painter 674: */ 675: AttributeSet as; 676: 677: /** 678: * Package-private constructor. 679: * 680: * @param as - AttributeSet for painter 681: */ 682: ListPainter(AttributeSet as) 683: { 684: this.as = as; 685: } 686: 687: /** 688: * Paints the CSS list decoration according to the attributes given. 689: * 690: * @param g - the graphics configuration 691: * @param x - the x coordinate 692: * @param y - the y coordinate 693: * @param w - the width of the allocated area 694: * @param h - the height of the allocated area 695: * @param v - the view making the request 696: * @param item - the list item to be painted >=0. 697: */ 698: public void paint(Graphics g, float x, float y, float w, float h, View v, 699: int item) 700: { 701: // FIXME: Not implemented. 702: } 703: } 704: 705: /** 706: * The parser callback for the CSSParser. 707: */ 708: class CssParser implements CSSParser.CSSParserCallback 709: { 710: /** 711: * A vector of all the selectors. 712: * Each element is an array of all the selector tokens 713: * in a single rule. 714: */ 715: Vector selectors; 716: 717: /** A vector of all the selector tokens in a rule. */ 718: Vector selectorTokens; 719: 720: /** Name of the current property. */ 721: String propertyName; 722: 723: /** The set of CSS declarations */ 724: MutableAttributeSet declaration; 725: 726: /** 727: * True if parsing a declaration, that is the Reader will not 728: * contain a selector. 729: */ 730: boolean parsingDeclaration; 731: 732: /** True if the attributes are coming from a linked/imported style. */ 733: boolean isLink; 734: 735: /** The base URL */ 736: URL base; 737: 738: /** The parser */ 739: CSSParser parser; 740: 741: /** 742: * Constructor 743: */ 744: CssParser() 745: { 746: selectors = new Vector(); 747: selectorTokens = new Vector(); 748: parser = new CSSParser(); 749: base = StyleSheet.this.base; 750: declaration = new SimpleAttributeSet(); 751: } 752: 753: /** 754: * Parses the passed in CSS declaration into an AttributeSet. 755: * 756: * @param s - the declaration 757: * @return the set of attributes containing the property and value. 758: */ 759: public AttributeSet parseDeclaration(String s) 760: { 761: try 762: { 763: return parseDeclaration(new StringReader(s)); 764: } 765: catch (IOException e) 766: { 767: // Do nothing here. 768: } 769: return null; 770: } 771: 772: /** 773: * Parses the passed in CSS declaration into an AttributeSet. 774: * 775: * @param r - the reader 776: * @return the attribute set 777: * @throws IOException from the reader 778: */ 779: public AttributeSet parseDeclaration(Reader r) throws IOException 780: { 781: parse(base, r, true, false); 782: return declaration; 783: } 784: 785: /** 786: * Parse the given CSS stream 787: * 788: * @param base - the url 789: * @param r - the reader 790: * @param parseDec - True if parsing a declaration 791: * @param isLink - True if parsing a link 792: */ 793: public void parse(URL base, Reader r, boolean parseDec, boolean isLink) throws IOException 794: { 795: parsingDeclaration = parseDec; 796: this.isLink = isLink; 797: this.base = base; 798: 799: // flush out all storage 800: propertyName = null; 801: selectors.clear(); 802: selectorTokens.clear(); 803: declaration.removeAttributes(declaration); 804: 805: parser.parse(r, this, parseDec); 806: } 807: 808: /** 809: * Invoked when a valid @import is encountered, 810: * will call importStyleSheet if a MalformedURLException 811: * is not thrown in creating the URL. 812: * 813: * @param s - the string after @import 814: */ 815: public void handleImport(String s) 816: { 817: if (s != null) 818: { 819: try 820: { 821: if (s.startsWith("url(") && s.endsWith(")")) 822: s = s.substring(4, s.length() - 1); 823: if (s.indexOf("\"") >= 0) 824: s = s.replaceAll("\"",""); 825: 826: URL url = new URL(s); 827: if (url == null && base != null) 828: url = new URL(base, s); 829: 830: importStyleSheet(url); 831: } 832: catch (MalformedURLException e) 833: { 834: // Do nothing here. 835: } 836: } 837: } 838: 839: /** 840: * A selector has been encountered. 841: * 842: * @param s - a selector (e.g. P or UL or even P,) 843: */ 844: public void handleSelector(String s) 845: { 846: if (s.endsWith(",")) 847: s = s.substring(0, s.length() - 1); 848: 849: selectorTokens.addElement(s); 850: addSelector(); 851: } 852: 853: /** 854: * Invoked when the start of a rule is encountered. 855: */ 856: public void startRule() 857: { 858: addSelector(); 859: } 860: 861: /** 862: * Invoked when a property name is encountered. 863: * 864: * @param s - the property 865: */ 866: public void handleProperty(String s) 867: { 868: propertyName = s; 869: } 870: 871: /** 872: * Invoked when a property value is encountered. 873: * 874: * @param s - the value 875: */ 876: public void handleValue(String s) 877: { 878: // call addCSSAttribute 879: // FIXME: Not implemented 880: } 881: 882: /** 883: * Invoked when the end of a rule is encountered. 884: */ 885: public void endRule() 886: { 887: // FIXME: Not implemented 888: // add rules 889: propertyName = null; 890: } 891: 892: /** 893: * Adds the selector to the vector. 894: */ 895: private void addSelector() 896: { 897: int length = selectorTokens.size(); 898: if (length > 0) 899: { 900: Object[] sel = new Object[length]; 901: System.arraycopy(selectorTokens.toArray(), 0, sel, 0, length); 902: selectors.add(sel); 903: selectorTokens.clear(); 904: } 905: } 906: } 907: }