10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7298SMark.J.Nelson@Sun.COM * Common Development and Distribution License (the "License"). 6*7298SMark.J.Nelson@Sun.COM * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 220Sstevel@tonic-gate * Copyright 2001,2003 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate * 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate // DATable.java: Interface for DATables. 280Sstevel@tonic-gate // Author: James Kempf 290Sstevel@tonic-gate // Created On: Mon May 11 13:46:02 1998 300Sstevel@tonic-gate // Last Modified By: James Kempf 310Sstevel@tonic-gate // Last Modified On: Mon Feb 22 15:47:37 1999 320Sstevel@tonic-gate // Update Count: 53 330Sstevel@tonic-gate // 340Sstevel@tonic-gate 350Sstevel@tonic-gate 360Sstevel@tonic-gate package com.sun.slp; 370Sstevel@tonic-gate 380Sstevel@tonic-gate /** 390Sstevel@tonic-gate * DATable is an abstract class that provides the interface for DA 400Sstevel@tonic-gate * and scope discovery. A variety of implementations are possible. 410Sstevel@tonic-gate * The getDATable() method creates the right one from a subclass. 420Sstevel@tonic-gate * 430Sstevel@tonic-gate * @author James Kempf 440Sstevel@tonic-gate */ 450Sstevel@tonic-gate 460Sstevel@tonic-gate import java.util.*; 470Sstevel@tonic-gate import java.net.*; 480Sstevel@tonic-gate 490Sstevel@tonic-gate abstract class DATable extends Object { 500Sstevel@tonic-gate 510Sstevel@tonic-gate protected static DATable daTable; 520Sstevel@tonic-gate protected static SLPConfig conf; 530Sstevel@tonic-gate 540Sstevel@tonic-gate // System property naming the DATable implementation class to use. 550Sstevel@tonic-gate 560Sstevel@tonic-gate final static String DA_TABLE_CLASS_PROP = "sun.net.slp.DATableClass"; 570Sstevel@tonic-gate 580Sstevel@tonic-gate // SA only scopes property. 590Sstevel@tonic-gate 600Sstevel@tonic-gate final static String SA_ONLY_SCOPES_PROP = "sun.net.slp.SAOnlyScopes"; 610Sstevel@tonic-gate 620Sstevel@tonic-gate // Hashtable key for multicast scopes. 630Sstevel@tonic-gate 640Sstevel@tonic-gate final static String MULTICAST_KEY = "&&**^^MULTICASTxxxKEY^^**&&"; 650Sstevel@tonic-gate 660Sstevel@tonic-gate // Hashtable key for DA equivalence classes. 670Sstevel@tonic-gate 680Sstevel@tonic-gate final static String UNICAST_KEY = "&&**^^UNICASTxxxKEY^^**&&"; 690Sstevel@tonic-gate 700Sstevel@tonic-gate /** 710Sstevel@tonic-gate * A record for all DAs supporting exactly the same set of scopes. 720Sstevel@tonic-gate * 730Sstevel@tonic-gate * @author James Kempf 740Sstevel@tonic-gate */ 750Sstevel@tonic-gate 760Sstevel@tonic-gate 770Sstevel@tonic-gate public static class DARecord extends Object { 780Sstevel@tonic-gate 790Sstevel@tonic-gate // The scopes supported. 800Sstevel@tonic-gate 810Sstevel@tonic-gate Vector scopes = null; // String scope names 820Sstevel@tonic-gate 830Sstevel@tonic-gate Vector daAddresses = new Vector(); // InetAddress DA addresses 840Sstevel@tonic-gate 850Sstevel@tonic-gate } 860Sstevel@tonic-gate 870Sstevel@tonic-gate /** 880Sstevel@tonic-gate * Return a hashtable containing two entries: 890Sstevel@tonic-gate * 900Sstevel@tonic-gate * MULTICAST_KEY - Vector of scopes from the incoming vector that are not 910Sstevel@tonic-gate * supported by any known DA. 920Sstevel@tonic-gate * 930Sstevel@tonic-gate * UNICAST_KEY - Vector of DATable.DARecord objects containing 940Sstevel@tonic-gate * equivalence classes of DAs that all support the same set of scopes. 950Sstevel@tonic-gate * Only DAs supporting one or more scopes in the incoming vector 960Sstevel@tonic-gate * are returned. 970Sstevel@tonic-gate * 980Sstevel@tonic-gate * Note that the equivalence classes don't necessarily mean that the 990Sstevel@tonic-gate * set of scopes are mutually exclusive. For example, if DA1 supports 1000Sstevel@tonic-gate * scopes A, B, and C; and DA2 supports scopes C and D, then they 1010Sstevel@tonic-gate * are in separate equivalence classes even though they both support 1020Sstevel@tonic-gate * C. But if DA2 supports A, B, and C; then it is in the same equivalence 1030Sstevel@tonic-gate * class. 1040Sstevel@tonic-gate * 1050Sstevel@tonic-gate * @param scopes The scopes for which DAs are required. 1060Sstevel@tonic-gate * @return A Hashtable with the multicast scopes and DAAddresses. 1070Sstevel@tonic-gate */ 1080Sstevel@tonic-gate findDAScopes(Vector scopes)1090Sstevel@tonic-gate abstract Hashtable findDAScopes(Vector scopes) 1100Sstevel@tonic-gate throws ServiceLocationException; 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate /** 1130Sstevel@tonic-gate * Remove a DA by address. 1140Sstevel@tonic-gate * 1150Sstevel@tonic-gate * @param address The host address of the DA. 1160Sstevel@tonic-gate * @param scopes The scopes. 1170Sstevel@tonic-gate * @return True if removed, false if not. 1180Sstevel@tonic-gate */ 1190Sstevel@tonic-gate removeDA(InetAddress address, Vector scopes)1200Sstevel@tonic-gate abstract boolean removeDA(InetAddress address, Vector scopes); 1210Sstevel@tonic-gate 1220Sstevel@tonic-gate /** 1230Sstevel@tonic-gate * Return a vector of scopes that the SA or UA client should use. 1240Sstevel@tonic-gate * Note that if no DAs are around, SA adverts must be used to 1250Sstevel@tonic-gate * find SAs. We must sort through the returned DAs and apply 1260Sstevel@tonic-gate * the scope prioritization algorithm to them. 1270Sstevel@tonic-gate * 1280Sstevel@tonic-gate * @return Vector of scopes for the SA or UA client to use. 1290Sstevel@tonic-gate */ 1300Sstevel@tonic-gate findScopes()1310Sstevel@tonic-gate synchronized Vector findScopes() throws ServiceLocationException { 1320Sstevel@tonic-gate 1330Sstevel@tonic-gate // First, get the DA addresses v.s. scopes table from the DAtable. 1340Sstevel@tonic-gate // This will also include DA addresses from the configuration file, 1350Sstevel@tonic-gate // if any. We don't filter on any scopes, since we want all of 1360Sstevel@tonic-gate // them. We are only interested in v2 scopes here. 1370Sstevel@tonic-gate 1380Sstevel@tonic-gate Vector scopes = new Vector(); 1390Sstevel@tonic-gate Hashtable daRec = daTable.findDAScopes(scopes); 1400Sstevel@tonic-gate Vector daEquivClasses = (Vector)daRec.get(UNICAST_KEY); 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate if (daEquivClasses != null) { 1430Sstevel@tonic-gate 1440Sstevel@tonic-gate // Go through the equivalence classes and pull out scopes. 1450Sstevel@tonic-gate 1460Sstevel@tonic-gate int i, n = daEquivClasses.size(); 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate for (i = 0; i < n; i++) { 1490Sstevel@tonic-gate DARecord rec = (DARecord)daEquivClasses.elementAt(i); 1500Sstevel@tonic-gate Vector v = rec.scopes; 1510Sstevel@tonic-gate 1520Sstevel@tonic-gate int j, m = v.size(); 1530Sstevel@tonic-gate 1540Sstevel@tonic-gate for (j = 0; j < m; j++) { 1550Sstevel@tonic-gate Object s = v.elementAt(j); 1560Sstevel@tonic-gate 1570Sstevel@tonic-gate // Unicast scopes take precedence over multicast scopes, 1580Sstevel@tonic-gate // so insert them at the beginning of the vector. 1590Sstevel@tonic-gate 1600Sstevel@tonic-gate if (!scopes.contains(s)) { 1610Sstevel@tonic-gate scopes.addElement(s); 1620Sstevel@tonic-gate 1630Sstevel@tonic-gate } 1640Sstevel@tonic-gate } 1650Sstevel@tonic-gate } 1660Sstevel@tonic-gate } 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate return scopes; 1690Sstevel@tonic-gate } 1700Sstevel@tonic-gate 1710Sstevel@tonic-gate /** 1720Sstevel@tonic-gate * Get the right DA table implementation. The property 1730Sstevel@tonic-gate * sun.net.slp.DATableClass determines the class. 1740Sstevel@tonic-gate * 1750Sstevel@tonic-gate * @return The DATable object for this process' SLP requests. 1760Sstevel@tonic-gate */ 1770Sstevel@tonic-gate 1780Sstevel@tonic-gate getDATable()1790Sstevel@tonic-gate static DATable getDATable() { 1800Sstevel@tonic-gate 1810Sstevel@tonic-gate // Return it right up front if we have it. 1820Sstevel@tonic-gate 1830Sstevel@tonic-gate if (daTable != null) { 1840Sstevel@tonic-gate return daTable; 1850Sstevel@tonic-gate 1860Sstevel@tonic-gate } 1870Sstevel@tonic-gate 1880Sstevel@tonic-gate conf = SLPConfig.getSLPConfig(); 1890Sstevel@tonic-gate 1900Sstevel@tonic-gate // Link and instantiate it. 1910Sstevel@tonic-gate 1920Sstevel@tonic-gate daTable = linkAndInstantiateFromProp(); 1930Sstevel@tonic-gate 1940Sstevel@tonic-gate return daTable; 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate } 1970Sstevel@tonic-gate 1980Sstevel@tonic-gate // Link and instantiate the class in the property. 1990Sstevel@tonic-gate linkAndInstantiateFromProp()2000Sstevel@tonic-gate static protected DATable linkAndInstantiateFromProp() { 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate // Get the property. 2030Sstevel@tonic-gate 2040Sstevel@tonic-gate String className = System.getProperty(DA_TABLE_CLASS_PROP); 2050Sstevel@tonic-gate 2060Sstevel@tonic-gate if (className == null) { 2070Sstevel@tonic-gate Assert.slpassert(false, 2080Sstevel@tonic-gate "no_da_table", 2090Sstevel@tonic-gate new Object[] {DA_TABLE_CLASS_PROP}); 2100Sstevel@tonic-gate } 2110Sstevel@tonic-gate 2120Sstevel@tonic-gate Class tclass = null; 2130Sstevel@tonic-gate 2140Sstevel@tonic-gate // Link the class and instantiate the object. 2150Sstevel@tonic-gate 2160Sstevel@tonic-gate try { 2170Sstevel@tonic-gate 2180Sstevel@tonic-gate tclass = Class.forName(className); 2190Sstevel@tonic-gate daTable = (DATable)tclass.newInstance(); 2200Sstevel@tonic-gate return daTable; 2210Sstevel@tonic-gate 2220Sstevel@tonic-gate } catch (ClassNotFoundException ex) { 2230Sstevel@tonic-gate 2240Sstevel@tonic-gate Assert.slpassert(false, 2250Sstevel@tonic-gate "no_da_table_class", 2260Sstevel@tonic-gate new Object[] {className}); 2270Sstevel@tonic-gate 2280Sstevel@tonic-gate } catch (InstantiationException ex) { 2290Sstevel@tonic-gate 2300Sstevel@tonic-gate Assert.slpassert(false, 2310Sstevel@tonic-gate "instantiation_exception", 2320Sstevel@tonic-gate new Object[] {className}); 2330Sstevel@tonic-gate 2340Sstevel@tonic-gate } catch (IllegalAccessException ex) { 2350Sstevel@tonic-gate 2360Sstevel@tonic-gate Assert.slpassert(false, 2370Sstevel@tonic-gate "access_exception", 2380Sstevel@tonic-gate new Object[] {className}); 2390Sstevel@tonic-gate 2400Sstevel@tonic-gate } 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate // We won't reach this point, since the assertions will capture 2430Sstevel@tonic-gate // any errors and kill the program. 2440Sstevel@tonic-gate 2450Sstevel@tonic-gate return null; 2460Sstevel@tonic-gate } 2470Sstevel@tonic-gate 2480Sstevel@tonic-gate // 2490Sstevel@tonic-gate // Utility functions for DA filtering and handling scopes. 2500Sstevel@tonic-gate // 2510Sstevel@tonic-gate 2520Sstevel@tonic-gate // Filter scopes, removing any not on the filter list if inVector is 2530Sstevel@tonic-gate // false and removing any in the filter list if inVector is true. 2540Sstevel@tonic-gate 2550Sstevel@tonic-gate public static void filterScopes(Vector scopes, Vector filter, boolean inVector)2560Sstevel@tonic-gate filterScopes(Vector scopes, Vector filter, boolean inVector) { 2570Sstevel@tonic-gate 2580Sstevel@tonic-gate int i = 0; 2590Sstevel@tonic-gate 2600Sstevel@tonic-gate // Null or empty filter vector means that all should be accepted. 2610Sstevel@tonic-gate 2620Sstevel@tonic-gate if (filter != null && !(filter.size() <= 0)) { 2630Sstevel@tonic-gate 2640Sstevel@tonic-gate while (i < scopes.size()) { 2650Sstevel@tonic-gate String scope = (String)scopes.elementAt(i); 2660Sstevel@tonic-gate 2670Sstevel@tonic-gate if ((!inVector && !filter.contains(scope)) || 2680Sstevel@tonic-gate (inVector && filter.contains(scope))) { 2690Sstevel@tonic-gate scopes.removeElementAt(i); 2700Sstevel@tonic-gate 2710Sstevel@tonic-gate } else { 2720Sstevel@tonic-gate i++; 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate } 2750Sstevel@tonic-gate } 2760Sstevel@tonic-gate } 2770Sstevel@tonic-gate } 2780Sstevel@tonic-gate 2790Sstevel@tonic-gate // Add a new address to the equivalence class. 2800Sstevel@tonic-gate addToEquivClass(String daaddr, Vector scopes, Vector ret)2810Sstevel@tonic-gate static boolean addToEquivClass(String daaddr, Vector scopes, Vector ret) { 2820Sstevel@tonic-gate 2830Sstevel@tonic-gate // Create the InetAddress object. 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate InetAddress addr = null; 2860Sstevel@tonic-gate 2870Sstevel@tonic-gate try { 2880Sstevel@tonic-gate 2890Sstevel@tonic-gate addr = InetAddress.getByName(daaddr); 2900Sstevel@tonic-gate 2910Sstevel@tonic-gate } catch (UnknownHostException ex) { 2920Sstevel@tonic-gate 2930Sstevel@tonic-gate if (conf.traceAll()) { 2940Sstevel@tonic-gate conf.writeLog("unknown_da_address", 2950Sstevel@tonic-gate new Object[] {daaddr}); 2960Sstevel@tonic-gate 2970Sstevel@tonic-gate } 2980Sstevel@tonic-gate 2990Sstevel@tonic-gate return false; 3000Sstevel@tonic-gate } 3010Sstevel@tonic-gate 3020Sstevel@tonic-gate // Go through the existing vector. 3030Sstevel@tonic-gate 3040Sstevel@tonic-gate int i, n = ret.size(); 3050Sstevel@tonic-gate boolean equivalent = false; 3060Sstevel@tonic-gate DARecord rec = null; 3070Sstevel@tonic-gate 3080Sstevel@tonic-gate outer: for (i = 0; i < n && equivalent == false; i++) { 3090Sstevel@tonic-gate rec = (DARecord)ret.elementAt(i); 3100Sstevel@tonic-gate Vector dascopes = rec.scopes; 3110Sstevel@tonic-gate 3120Sstevel@tonic-gate int j, m = dascopes.size(); 3130Sstevel@tonic-gate 3140Sstevel@tonic-gate for (j = 0; j < m; j++) { 3150Sstevel@tonic-gate String scope = (String)dascopes.elementAt(j); 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate if (!scopes.contains(scope)) { 3180Sstevel@tonic-gate continue outer; 3190Sstevel@tonic-gate 3200Sstevel@tonic-gate } 3210Sstevel@tonic-gate } 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate equivalent = true; 3240Sstevel@tonic-gate } 3250Sstevel@tonic-gate 3260Sstevel@tonic-gate // Make a new record if not equivalent. 3270Sstevel@tonic-gate 3280Sstevel@tonic-gate if (!equivalent) { 3290Sstevel@tonic-gate rec = new DATable.DARecord(); 3300Sstevel@tonic-gate rec.scopes = (Vector)scopes.clone(); 3310Sstevel@tonic-gate 3320Sstevel@tonic-gate ret.addElement(rec); 3330Sstevel@tonic-gate 3340Sstevel@tonic-gate } 3350Sstevel@tonic-gate 3360Sstevel@tonic-gate 3370Sstevel@tonic-gate // Add to record. Optimize, by putting the local address at the 3380Sstevel@tonic-gate // beginning of the vector. 3390Sstevel@tonic-gate 3400Sstevel@tonic-gate Vector interfaces = conf.getInterfaces(); 3410Sstevel@tonic-gate 3420Sstevel@tonic-gate if (interfaces.contains(addr)) { 3430Sstevel@tonic-gate rec.daAddresses.insertElementAt(addr, 0); 3440Sstevel@tonic-gate 3450Sstevel@tonic-gate } else { 3460Sstevel@tonic-gate rec.daAddresses.addElement(addr); 3470Sstevel@tonic-gate 3480Sstevel@tonic-gate } 3490Sstevel@tonic-gate 3500Sstevel@tonic-gate return true; 3510Sstevel@tonic-gate } 3520Sstevel@tonic-gate 3530Sstevel@tonic-gate /** 3540Sstevel@tonic-gate * Validate the scope names. We check that they are all strings, 3550Sstevel@tonic-gate * that none are the empty string. In addition, we collate to 3560Sstevel@tonic-gate * remove duplicates, and lower case. 3570Sstevel@tonic-gate */ 3580Sstevel@tonic-gate validateScopes(Vector scopes, Locale locale)3590Sstevel@tonic-gate static void validateScopes(Vector scopes, Locale locale) 3600Sstevel@tonic-gate throws ServiceLocationException { 3610Sstevel@tonic-gate 3620Sstevel@tonic-gate // Check for empty vector. 3630Sstevel@tonic-gate 3640Sstevel@tonic-gate if (scopes == null || scopes.size() <= 0) { 3650Sstevel@tonic-gate throw 3660Sstevel@tonic-gate new ServiceLocationException( 3670Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 3680Sstevel@tonic-gate "no_scope_vector", 3690Sstevel@tonic-gate new Object[0]); 3700Sstevel@tonic-gate } 3710Sstevel@tonic-gate 3720Sstevel@tonic-gate // Check for all strings and none empty. 3730Sstevel@tonic-gate 3740Sstevel@tonic-gate int i; 3750Sstevel@tonic-gate Hashtable ht = new Hashtable(); 3760Sstevel@tonic-gate 3770Sstevel@tonic-gate for (i = 0; i < scopes.size(); i++) { 3780Sstevel@tonic-gate Object o = scopes.elementAt(i); 3790Sstevel@tonic-gate 3800Sstevel@tonic-gate if (!(o instanceof String)) { 3810Sstevel@tonic-gate throw 3820Sstevel@tonic-gate new ServiceLocationException( 3830Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 3840Sstevel@tonic-gate "non_string_element", 3850Sstevel@tonic-gate new Object[] {scopes}); 3860Sstevel@tonic-gate } 3870Sstevel@tonic-gate 3880Sstevel@tonic-gate String str = (String)o; 3890Sstevel@tonic-gate 3900Sstevel@tonic-gate if (str.length() <= 0) { 3910Sstevel@tonic-gate throw 3920Sstevel@tonic-gate new ServiceLocationException( 3930Sstevel@tonic-gate ServiceLocationException.PARSE_ERROR, 3940Sstevel@tonic-gate "null_element", 3950Sstevel@tonic-gate new Object[] {scopes}); 3960Sstevel@tonic-gate } 3970Sstevel@tonic-gate 3980Sstevel@tonic-gate // Lower case, trim. 3990Sstevel@tonic-gate 4000Sstevel@tonic-gate str = str.toLowerCase(locale).trim(); 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate // Squeeze out spaces. 4030Sstevel@tonic-gate 4040Sstevel@tonic-gate StringBuffer buf = new StringBuffer(); 4050Sstevel@tonic-gate StringTokenizer tk = 4060Sstevel@tonic-gate new StringTokenizer(str, ServiceLocationAttribute.WHITESPACE); 4070Sstevel@tonic-gate String tok = null; 4080Sstevel@tonic-gate 4090Sstevel@tonic-gate while (tk.hasMoreTokens()) { 4100Sstevel@tonic-gate 4110Sstevel@tonic-gate // Add a single embedded whitespace for each group found. 4120Sstevel@tonic-gate 4130Sstevel@tonic-gate if (tok != null) { 4140Sstevel@tonic-gate buf.append(" "); 4150Sstevel@tonic-gate 4160Sstevel@tonic-gate } 4170Sstevel@tonic-gate 4180Sstevel@tonic-gate tok = tk.nextToken(); 4190Sstevel@tonic-gate buf.append(tok); 4200Sstevel@tonic-gate } 4210Sstevel@tonic-gate 4220Sstevel@tonic-gate str = buf.toString(); 4230Sstevel@tonic-gate 4240Sstevel@tonic-gate // If it wasn't already seen, put it into the hashtable. 4250Sstevel@tonic-gate 4260Sstevel@tonic-gate if (ht.get(str) == null) { 4270Sstevel@tonic-gate ht.put(str, str); 4280Sstevel@tonic-gate scopes.setElementAt(str, i); 4290Sstevel@tonic-gate 4300Sstevel@tonic-gate } else { 4310Sstevel@tonic-gate /* 4320Sstevel@tonic-gate * Must decrement the index 'i' otherwise the next iteration 4330Sstevel@tonic-gate * around the loop will miss the element immediately after 4340Sstevel@tonic-gate * the element removed. 4350Sstevel@tonic-gate * 4360Sstevel@tonic-gate * WARNING: Do not use 'i' again until the loop has 4370Sstevel@tonic-gate * iterated as it may, after decrementing, 4380Sstevel@tonic-gate * be negative. 4390Sstevel@tonic-gate */ 4400Sstevel@tonic-gate scopes.removeElementAt(i); 4410Sstevel@tonic-gate i--; 4420Sstevel@tonic-gate continue; 4430Sstevel@tonic-gate } 4440Sstevel@tonic-gate } 4450Sstevel@tonic-gate } 4460Sstevel@tonic-gate 4470Sstevel@tonic-gate } 448