001: /*
002: * $RCSfile: NegotiableCapabilitySet.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:57:51 $
010: * $State: Exp $
011: */package javax.media.jai.remote;
012:
013: import java.io.Serializable;
014: import java.util.Enumeration;
015: import java.util.Hashtable;
016: import java.util.Iterator;
017: import java.util.List;
018: import java.util.Vector;
019: import javax.media.jai.util.CaselessStringKey;
020:
021: /**
022: * This class is an aggregation of <code>NegotiableCapability</code> objects.
023: * This class can be used to represent all the capabilities of a machine.
024: *
025: * <p> This class can be classified as either a preference or a non-preference.
026: * For an explanation of the concept of preference, refer to the class
027: * comments on <code>javax.media.jai.remote.NegotiableCapability</code>.
028: *
029: * <p> If multiple <code>NegotiableCapability</code> objects with the
030: * same category and capability name are added to this class, the
031: * <code>NegotiableCapability</code> added earliest has the highest
032: * preference.
033: *
034: * <p>All names are treated in a case-retentive and case-insensitive manner.
035: *
036: * @since JAI 1.1
037: */
038: public class NegotiableCapabilitySet implements Serializable {
039:
040: // Implementation specific data structures. Each entry into this
041: // Hashtable is a SequentialMap object hashed by the category of the
042: // NegotiableCapability. The SequentialMap stores NegotiableCapability
043: // objects for the same category but different capabilityNames in
044: // separate bins. Within each bin, the NegotiableCapability that was
045: // added first will always be the first one to be accessed.
046: private Hashtable categories = new Hashtable();
047:
048: // Whether this NegotiableCapabilitySet is a preference or not.
049: private boolean isPreference = false;
050:
051: /**
052: * Creates a <code>NegotiableCapabilitySet</code>. The
053: * <code>isPreference</code> argument specifies whether this
054: * <code>NegotiableCapabilitySet</code> should be treated as a preference
055: * or non-preference. If this <code>NegotiableCapabilitySet</code> is
056: * specified to be a non-preference, only non-preference
057: * <code>NegotiableCapability</code>'s will be allowed to be added to it,
058: * otherwise an <code>IllegalArgumentException</code> will be thrown
059: * at the time of addition. Similarly only preference
060: * <code>NegotiableCapability</code> objects can be added to this
061: * <code>NegotiableCapabilitySet</code> if <code>isPreference</code>
062: * is true.
063: *
064: * @param isPreference a boolean that specifies whether the component
065: * <code>NegotiableCapability</code>'s are preferences.
066: */
067: public NegotiableCapabilitySet(boolean isPreference) {
068: this .isPreference = isPreference;
069: }
070:
071: /**
072: * Returns true if this <code>NegotiableCapabilitySet</code> is an
073: * aggregation of preference <code>NegotiableCapability</code>'s,
074: * false otherwise.
075: */
076: public boolean isPreference() {
077: return isPreference;
078: }
079:
080: /**
081: * Adds a new <code>NegotiableCapability</code> to this
082: * <code>NegotiableCapabilitySet</code>. If a
083: * <code>NegotiableCapability</code> with the same category and same
084: * capability name as the one currently being added has been added
085: * previously, the previous one will have a higher preference.
086: *
087: * @param capability The <code>NegotiableCapability</code> to be added.
088: * @throws IllegalArgumentException if capability is null.
089: * @throws IllegalArgumentException if <code>isPreference()</code>
090: * returns false, and capability is a preference
091: * <code>NegotiableCapability</code>.
092: * @throws IllegalArgumentException if <code>isPreference()</code>
093: * returns true, and capability is a non-preference
094: * <code>NegotiableCapability</code>.
095: */
096: public void add(NegotiableCapability capability) {
097:
098: if (capability == null) {
099: throw new IllegalArgumentException(JaiI18N
100: .getString("NegotiableCapabilitySet0"));
101: }
102:
103: if (isPreference != capability.isPreference()) {
104: throw new IllegalArgumentException(JaiI18N
105: .getString("NegotiableCapabilitySet1"));
106: }
107:
108: SequentialMap map = getCategoryMap(capability.getCategory());
109: map.put(capability);
110: }
111:
112: /**
113: * Removes the specified <code>NegotiableCapability</code> from this
114: * <code>NegotiableCapabilitySet</code>. If the specified
115: * <code>NegotiableCapability</code> was not added previously, an
116: * <code>IllegalArgumentException</code> will be thrown.
117: *
118: * @param capability The <code>NegotiableCapability</code> to be removed.
119: * @throws IllegalArgumentException if capability is null.
120: * @throws IllegalArgumentException if the specified
121: * <code>NegotiableCapabilitySet</code> was not added previously.
122: */
123: public void remove(NegotiableCapability capability) {
124:
125: if (capability == null) {
126: throw new IllegalArgumentException(JaiI18N
127: .getString("NegotiableCapabilitySet0"));
128: }
129:
130: SequentialMap map = getCategoryMap(capability.getCategory());
131: map.remove(capability);
132: }
133:
134: /**
135: * Returns all the <code>NegotiableCapability</code>s with the given
136: * category and capability name added previously, as a <code>List</code>.
137: * If none were added, returns an empty <code>List</code>.
138: *
139: * @param category The category of the <code>NegotiableCapability</code>.
140: * @param capabilityName The name of the <code>NegotiableCapability</code>.
141: *
142: * @throws IllegalArgumentException if category is null.
143: * @throws IllegalArgumentException if capabilityName is null.
144: */
145: public List get(String category, String capabilityName) {
146:
147: if (category == null) {
148: throw new IllegalArgumentException(JaiI18N
149: .getString("NegotiableCapabilitySet3"));
150: }
151:
152: if (capabilityName == null) {
153: throw new IllegalArgumentException(JaiI18N
154: .getString("NegotiableCapabilitySet4"));
155: }
156:
157: SequentialMap map = getCategoryMap(category);
158: return map.getNCList(capabilityName);
159: }
160:
161: /**
162: * Returns all the <code>NegotiableCapability</code>s with the given
163: * category as a <code>List</code>. Returns an empty <code>List</code>
164: * if no such <code>NegotiableCapability</code>s were added.
165: *
166: * @param category The category of the <code>NegotiableCapability</code>s
167: * to return.
168: * @throws IllegalArgumentException if category is null.
169: */
170: public List get(String category) {
171:
172: if (category == null) {
173: throw new IllegalArgumentException(JaiI18N
174: .getString("NegotiableCapabilitySet3"));
175: }
176:
177: SequentialMap map = getCategoryMap(category);
178: Vector capNames = map.getCapabilityNames();
179:
180: Vector curr, allNC = new Vector();
181: Object obj;
182: for (Iterator e = capNames.iterator(); e.hasNext();) {
183: // Get the next vector of NCs
184: curr = (Vector) map.getNCList((String) e.next());
185: for (Iterator i = curr.iterator(); i.hasNext();) {
186: // Get the elements of the Vector
187: obj = i.next();
188:
189: // If it isn't already present in the resultant Vector, add it
190: if (!(allNC.contains(obj))) {
191: allNC.add(obj);
192: }
193: }
194: }
195:
196: return (List) allNC;
197: }
198:
199: /**
200: * Returns the category of all the <code>NegotiableCapability</code>
201: * objects that have been added previously, as a <code>List</code>.
202: * Returns an empty <code>List</code>, if no
203: * <code>NegotiableCapability</code> objects have been added.
204: *
205: * <p> The returned <code>List</code> does not contain any
206: * duplicates.
207: */
208: public List getCategories() {
209:
210: CaselessStringKey key;
211: Vector v = new Vector();
212: for (Enumeration e = categories.keys(); e.hasMoreElements();) {
213: key = (CaselessStringKey) e.nextElement();
214: v.add(key.toString());
215: }
216:
217: return (List) v;
218: }
219:
220: /**
221: * Returns the capability names of all the
222: * <code>NegotiableCapability</code> objects that have been added
223: * previously, as a <code>List</code>. Returns an empty
224: * <code>List</code> if no such <code>NegotiableCapability</code>
225: * objects have been added.
226: *
227: * <p> The returned <code>List</code> does not contain any
228: * duplicates.
229: *
230: * @param category The category of the <code>NegotiableCapability</code>.
231: * @throws IllegalArgumentException if category is null.
232: */
233: public List getCapabilityNames(String category) {
234:
235: if (category == null) {
236: throw new IllegalArgumentException(JaiI18N
237: .getString("NegotiableCapabilitySet3"));
238: }
239:
240: SequentialMap map = getCategoryMap(category);
241: Vector names = map.getCapabilityNames();
242: return (List) names;
243: }
244:
245: /**
246: * Returns the common subset supported by this
247: * <code>NegotiableCapabilitySet</code> and the given
248: * <code>NegotiableCapabilitySet</code>, if the negotiation succeeds.
249: * This method returns null if negotiation fails for all categories.
250: *
251: * <p> For those categories that are common to both the
252: * <code>NegotiableCapabilitySet</code> objects, negotiation is
253: * performed on a per category basis. Within each category, negotiation
254: * is performed on a per capabilityName basis. The categories which exist
255: * only in one or the other <code>NegotiableCapabilitySet</code> are
256: * not negotiated upon and do not show up in the resultant
257: * <code>NegotiableCapabilitySet</code>, if the negotiation is successful.
258: * If this class contains 'm' <code>NegotiableCapability</code> objects
259: * for the same category and capabilityName for which the
260: * <code>NegotiableCapabilitySet</code> being negotiated with contains
261: * 'n' <code>NegotiableCapability</code> objects, then the negotiation
262: * for this category and capabilityName will require m x n number of
263: * negotiations between two <code>NegotiableCapability</code> objects.
264: * The ones that succeed will produce new <code>NegotiableCapability</code>
265: * objects which will be added to the returned
266: * <code>NegotiableCapabilitySet</code>.
267: *
268: * <p> If the supplied <code>NegotiableCapabilitySet</code> is null,
269: * then the negotiation will fail, and null will be returned.
270: *
271: * @param other The <code>NegotiableCapabilitySet</code> to negotiate with.
272: */
273: public NegotiableCapabilitySet negotiate(
274: NegotiableCapabilitySet other) {
275:
276: if (other == null)
277: return null;
278:
279: NegotiableCapabilitySet negotiated = new NegotiableCapabilitySet(
280: isPreference & other.isPreference());
281:
282: // Get only the common categories
283: Vector commonCategories = new Vector(getCategories());
284: commonCategories.retainAll(other.getCategories());
285:
286: String capName, otherCapName;
287: NegotiableCapability this Cap, otherCap, negCap;
288: List this Capabilities, otherCapabilities;
289:
290: for (Iterator c = commonCategories.iterator(); c.hasNext();) {
291: String currCategory = (String) c.next();
292:
293: this Capabilities = get(currCategory);
294: otherCapabilities = other.get(currCategory);
295:
296: for (Iterator t = this Capabilities.iterator(); t.hasNext();) {
297:
298: this Cap = (NegotiableCapability) t.next();
299:
300: for (Iterator o = otherCapabilities.iterator(); o
301: .hasNext();) {
302:
303: otherCap = (NegotiableCapability) o.next();
304: negCap = this Cap.negotiate(otherCap);
305: if (negCap != null)
306: negotiated.add(negCap);
307: }
308: }
309: }
310:
311: if (negotiated.isEmpty()) {
312: return null;
313: }
314:
315: return negotiated;
316: }
317:
318: /**
319: * Returns the single <code>NegotiableCapability</code> that is the
320: * negotiated result for the given category from the current class.
321: * Returns null if negotiation for this category failed. If the
322: * negotiation is successful, then this method will return the most
323: * prefered (the one that was added first i.e. the one that is first
324: * in the list) <code>NegotiableCapability</code> from the list of
325: * <code>NegotiableCapability</code> that are valid for this category.
326: *
327: * @param category The category to find the negotiated result for.
328: * @throws IllegalArgumentException if category is null.
329: */
330: public NegotiableCapability getNegotiatedValue(String category) {
331:
332: if (category == null) {
333: throw new IllegalArgumentException(JaiI18N
334: .getString("NegotiableCapabilitySet3"));
335: }
336:
337: List this Capabilities = get(category);
338: if (this Capabilities.isEmpty())
339: return null;
340: else
341: return (NegotiableCapability) (this Capabilities.get(0));
342: }
343:
344: /**
345: * Performs negotiation with the given <code>NegotiableCapabilitySet</code>
346: * for the specified category and returns the single
347: * <code>NegotiableCapability</code> that is the negotiated result for
348: * the given category, if the negotiation succeeds, null
349: * otherwise.
350: *
351: * <p> Negotiation is only performed for the specified category. For
352: * the specified category, if there are 'm'
353: * <code>NegotiableCapability</code> objects for which the
354: * <code>NegotiableCapabilitySet</code> being negotiated with contains
355: * 'n' <code>NegotiableCapability</code> objects, then the negotiation
356: * for this category may require m x n number of negotiations at a
357: * maximum and one negotiation at a minimum as the negotiation process
358: * stops as soon as a negotiation is successful. The results of this
359: * successful negotiation are then returned. If all the m x n
360: * negotiations fail, null is returned.
361: *
362: * <p> If the supplied <code>NegotiableCapabilitySet</code> is null,
363: * then the negotiation will fail and null will be returned.
364: *
365: * @param other The <code>NegotiableCapabilitySet</code> to negotiate with.
366: * @param category The category to negotiate for.
367: * @throws IllegalArgumentException if category is null.
368: */
369: public NegotiableCapability getNegotiatedValue(
370: NegotiableCapabilitySet other, String category) {
371: if (other == null)
372: return null;
373:
374: if (category == null) {
375: throw new IllegalArgumentException(JaiI18N
376: .getString("NegotiableCapabilitySet3"));
377: }
378:
379: List this Capabilities = get(category);
380: List otherCapabilities = other.get(category);
381:
382: NegotiableCapability this Cap, otherCap, negCap;
383:
384: for (Iterator t = this Capabilities.iterator(); t.hasNext();) {
385:
386: this Cap = (NegotiableCapability) t.next();
387:
388: for (Iterator o = otherCapabilities.iterator(); o.hasNext();) {
389:
390: otherCap = (NegotiableCapability) o.next();
391: negCap = this Cap.negotiate(otherCap);
392:
393: // The first negotiation to succeed is returned
394: if (negCap != null)
395: return negCap;
396: }
397: }
398:
399: return null;
400: }
401:
402: /**
403: * Returns true if no <code>NegotiableCapability</code> objects have been
404: * added.
405: */
406: public boolean isEmpty() {
407: return categories.isEmpty();
408: }
409:
410: // Method to get the SequentialMap for a particular category, creating
411: // one if necessary
412: private SequentialMap getCategoryMap(String category) {
413:
414: CaselessStringKey categoryKey = new CaselessStringKey(category);
415: SequentialMap map = (SequentialMap) categories.get(categoryKey);
416:
417: if (map == null) {
418: map = new SequentialMap();
419: categories.put(categoryKey, map);
420: }
421:
422: return map;
423: }
424:
425: /**
426: * A Class to manage storage of NegotiableCapability objects by category
427: * and within that by capabilityName. This class also maintains the
428: * order of NegotiableCapability in which they were added under a
429: * particular category and capabilityName.
430: */
431: class SequentialMap implements Serializable {
432:
433: // Vector of CaselessStringKey objects, will be the capabilityNames.
434: Vector keys;
435: // Vector of vectors, each vector containing all the NCs for a
436: // particular capabilityName.
437: Vector values;
438:
439: /**
440: * Creates a new SequentialMap.
441: */
442: SequentialMap() {
443: keys = new Vector();
444: values = new Vector();
445: }
446:
447: /**
448: * Add a capability to this SequentialMap.
449: */
450: void put(NegotiableCapability capability) {
451:
452: CaselessStringKey capNameKey = new CaselessStringKey(
453: capability.getCapabilityName());
454:
455: int index = keys.indexOf(capNameKey);
456:
457: Vector v;
458: if (index == -1) {
459: keys.add(capNameKey);
460: v = new Vector();
461: v.add(capability);
462: values.add(v);
463: } else {
464: v = (Vector) values.elementAt(index);
465: if (v == null)
466: v = new Vector();
467: v.add(capability);
468: }
469: }
470:
471: /**
472: * Let a List of all NegotiableCapability objects stored for the
473: * given capabilityName.
474: */
475: List getNCList(String capabilityName) {
476:
477: CaselessStringKey capNameKey = new CaselessStringKey(
478: capabilityName);
479:
480: int index = keys.indexOf(capNameKey);
481:
482: Vector v;
483: if (index == -1) {
484: v = new Vector();
485: return (List) v;
486: } else {
487: v = (Vector) values.elementAt(index);
488: return (List) v;
489: }
490: }
491:
492: /**
493: * Remove a NegotiableCapability from this SequentialMap.
494: */
495: void remove(NegotiableCapability capability) {
496:
497: CaselessStringKey capNameKey = new CaselessStringKey(
498: capability.getCapabilityName());
499:
500: int index = keys.indexOf(capNameKey);
501:
502: if (index == -1) {
503: throw new IllegalArgumentException(JaiI18N
504: .getString("NegotiableCapabilitySet2"));
505: }
506:
507: Vector v = (Vector) values.elementAt(index);
508: if (v.remove(capability) == false) {
509: throw new IllegalArgumentException(JaiI18N
510: .getString("NegotiableCapabilitySet2"));
511: }
512:
513: // If this was the only element in the capabilityName Vector
514: if (v.isEmpty()) {
515: keys.remove(capNameKey);
516: values.remove(index);
517: }
518:
519: if (keys.isEmpty())
520: categories.remove(new CaselessStringKey(capability
521: .getCategory()));
522: }
523:
524: /**
525: * Get capability names of all NegotiableCapabilitySets in this
526: * SequentialMap.
527: */
528: Vector getCapabilityNames() {
529:
530: Vector v = new Vector();
531: CaselessStringKey name;
532: for (Iterator i = keys.iterator(); i.hasNext();) {
533: name = (CaselessStringKey) i.next();
534: v.add(name.getName());
535: }
536:
537: return v;
538: }
539: }
540: }
|