001: /*
002: * Copyright 2001 Sun Microsystems, Inc. All rights reserved.
003: * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to license terms.
004: */
005:
006: package com.sun.portal.search.soif;
007:
008: import java.util.*;
009:
010: /**
011: * AVPair - a class used to represent a SOIF attribute-value pair.
012: * <p>
013: * SOIF AVPairs may be text or binary. Other types should be encoded by convention.<br>
014: * SOIF AVPairs may be multivalued.
015: * <p>
016: * This implementation will save data as either strings or byte arrays.
017: * If data is saved as a byte array, but requested as a string, an implicit
018: * conversion from binary to text is performed using the current character encoding.
019: * A conversion is also performed when an attribute is written in one
020: * encoding, but read in another.
021: * <p>
022: * NB: byte array entries are stored by reference and shared with caller.
023: * String entries are never shared.
024: * @see SOIF
025: */
026: public class AVPair {
027:
028: /**
029: * An AVPair entry. Can be either byte[] or String depending on usage.
030: * Conversion b/w byte[] and String is automatic and cached.
031: * (which doubles memory usage in the worst case - oh well...)
032: */
033: class AVValue {
034:
035: String value; // directly set or cached conversion of bvalue
036: byte[] bvalue; // directly set or cached conversion of value
037:
038: public AVValue(String v) {
039: value = v;
040: }
041:
042: public AVValue(byte[] b) {
043: bvalue = b;
044: }
045:
046: String getValue() {
047: if (value == null && bvalue != null) {
048: try {
049: value = new String(bvalue, encoding);
050: } catch (java.io.UnsupportedEncodingException e) {
051: } // XXX throw this?
052: }
053: return value;
054: }
055:
056: byte[] getBytes() {
057: if (bvalue == null && value != null) {
058: try {
059: bvalue = value.getBytes(encoding);
060: } catch (java.io.UnsupportedEncodingException e) {
061: } // XXX throw this?
062: }
063: return bvalue;
064: }
065:
066: void setValue(String v) {
067: value = v;
068: bvalue = null;
069: }
070:
071: void setValue(byte[] b) {
072: bvalue = b;
073: value = null;
074: }
075:
076: }
077:
078: /**
079: * Attribute name. Multivalued attr bob-1, bob-2, bob-4 have the name bob.
080: * Case is preserved, although SOIF lookups are case insensitive.
081: */
082: public String attribute; // XXX Always lower case this?
083:
084: /**
085: * Attribute value. Can be binary (assumed when reading from stream)
086: * For internal use, directly supports String values for efficiency.
087: */
088: public AVValue[] value = new AVValue[1];
089:
090: private static String encoding = SOIF.defaultEncoding;
091:
092: /**
093: * Constructs an empty, unnamed AVPair instance (mainly for testing)
094: */
095: AVPair() {
096: }
097:
098: /**
099: * Constructs an empty, named AVPair instance
100: * @param att the attribute
101: */
102: public AVPair(String att) {
103: attribute = att;
104: }
105:
106: /**
107: * Constructs a new AVPairs instance with a String value.
108: * @param att the attribute
109: * @param val the value
110: */
111: public AVPair(String att, String val) {
112: attribute = att;
113: insert(val, 0);
114: }
115:
116: /**
117: * Constructs a new AVPairs instance with a byte array value.
118: * @param att the attribute
119: * @param val the value
120: */
121: public AVPair(String att, byte[] bval) {
122: attribute = att;
123: insert(bval, 0);
124: }
125:
126: /**
127: * Constructs a new AVPairs instance.
128: * @param att the attribute
129: * @param val the value
130: */
131: /*public AVPair(String att, String[] val) {
132: attribute = att;
133: value = val;
134: }
135:
136: public AVPair(String att, byte[][] bval) {
137: attribute = att;
138: bvalue = bval;
139: }*/
140:
141: /** Constructs an AVPair consisting of a String value with the given index. */
142: public AVPair(String att, String val, int index) {
143: attribute = att;
144: insert(val, index);
145: }
146:
147: /** Constructs an AVPair consisting of a byte array value with the given index. */
148: public AVPair(String att, byte[] bval, int index) {
149: this (att, bval, index, SOIF.defaultEncoding);
150: }
151:
152: /** Constructs an AVPair consisting of a String value with the given index and encoding. */
153: public AVPair(String att, byte[] bval, int index, String encoding) {
154: attribute = att;
155: if (encoding != null && encoding.equalsIgnoreCase("UTF-16"))
156: encoding = "UTF-16BE";
157: this .encoding = encoding;
158: insert(bval, index);
159: }
160:
161: /**
162: * Inserts a String at the given index, no duplicates.
163: * @return false if slot already taken
164: */
165: public boolean insert(String v, int index) {
166: ensureCapacity(index + 1);
167: if (value[index] == null) {
168: value[index] = new AVValue(v);
169: return true;
170: }
171: return false;
172: }
173:
174: /**
175: * Inserts a byte array at the given index, no duplicates.
176: * @return false if slot already taken
177: */
178: public boolean insert(byte[] b, int index) {
179: ensureCapacity(index + 1);
180: if (value[index] == null) {
181: value[index] = new AVValue(b);
182: return true;
183: }
184: return false;
185: }
186:
187: /**
188: * Replaces the string at the given index.
189: * @return false if slot already taken
190: */
191: public boolean replace(String v, int index) {
192: boolean res = remove(index);
193: insert(v, index);
194: return res;
195: }
196:
197: /**
198: * Replaces the byte array at the given index.
199: * @return false if slot already taken
200: */
201: public boolean replace(byte[] b, int index) {
202: boolean res = remove(index);
203: insert(b, index);
204: return res;
205: }
206:
207: /**
208: * Removes a mutivalued entry by index.
209: * @return false if index was not occupied
210: */
211: public boolean remove(int index) {
212: if (value.length > index && value[index] != null) {
213: value[index] = null;
214: return true;
215: }
216: return false;
217: }
218:
219: /**
220: * Packs the index array by closing all holes towards the index zero.
221: */
222: public void squeeze() {
223: for (int i = 0; i < value.length; ++i) {
224: if (value[i] != null)
225: continue;
226: // Found a hole - fill it with the contents of the next occupied slot.
227: for (int j = i + 1; j < value.length; ++j) {
228: if (value[j] != null) {
229: value[i] = value[j];
230: value[j] = null;
231: break;
232: }
233: }
234: }
235: }
236:
237: /**
238: * @return the attribute name for this AVPair.
239: */
240: public String getAttribute() {
241: return attribute;
242: }
243:
244: /**
245: * (Re)names an AVPair.
246: */
247: public void setAttribute(String s) {
248: attribute = s;
249: }
250:
251: /**
252: * @return the value of this AVPair (uses the first occupied slot if multivalued)
253: */
254: public String getValue() {
255: for (int i = 0; i < value.length; ++i) {
256: if (value[i] != null)
257: return value[i].getValue();
258: }
259: return null;
260: }
261:
262: /**
263: * @return the value of this AVPair (uses the first occupied slot if multivalued)
264: */
265: public byte[] getBytes() {
266: for (int i = 0; i < value.length; ++i) {
267: if (value[i] != null)
268: return value[i].getBytes();
269: }
270: return null;
271: }
272:
273: /**
274: * @param i multivalue index
275: * @return String value at index i
276: */
277: public String getValue(int i) {
278: if (value.length > i && value[i] != null)
279: return value[i].getValue();
280: return null;
281: }
282:
283: /**
284: * @param i multivalue index
285: * @return byte array value at index i
286: */
287: public byte[] getBytes(int i) {
288: if (value.length > i && value[i] != null)
289: return value[i].getBytes();
290: return null;
291: }
292:
293: /**
294: * Ensure arrays are big enough
295: * - blocking factor?
296: * - use a single Object array?
297: */
298: protected void ensureCapacity(int size) {
299: if (value == null)
300: value = new AVValue[size + 4]; // allow some extra
301: else if (value.length < size) {
302: AVValue[] old = value;
303: value = new AVValue[size + 4];
304: System.arraycopy(old, 0, value, 0, old.length);
305: }
306: }
307:
308: /**
309: * @return the number of non-null values for this attribute.
310: */
311: public int valueCount() { // XXX rename?
312: int count = 0;
313: for (int i = 0; i < value.length; ++i) {
314: if (value[i] != null)
315: count++;
316: }
317: return count;
318: }
319:
320: /**
321: * @return the maximum occupied index.
322: */
323: public int getMaxIndex() {
324: int i;
325: for (i = value.length - 1; i > 0 && value[i] == null; --i)
326: ;
327: return i;
328: }
329:
330: /**
331: * @return the total size - some values may be null
332: */
333: public int size() {
334: return value.length;
335: }
336:
337: /**
338: * Gets all valid values of this attribute.
339: * @return an array of Strings for
340: * multiple values for an attribute, e.g., for Bob, return
341: * values for Bob-1 and Bob-2.
342: */
343: public String[] getStringValues() {
344: int count = valueCount();
345: String[] vals = new String[count];
346: for (int i = 0, j = 0; i < value.length; ++i) {
347: String v = getValue(i);
348: if (v != null)
349: vals[j++] = v;
350: }
351: return vals;
352: }
353:
354: /**
355: * Gets all valid values of this attribute.
356: * @return an array of byte arrays for
357: * multiple values for an attribute, e.g., for image, return
358: * values for image-1 and image-2.
359: */
360: public byte[][] getByteValues() {
361: int count = valueCount();
362: byte[][] bvals = new byte[count][];
363: for (int i = 0, j = 0; i < value.length; ++i) {
364: byte[] b = getBytes(i);
365: if (b != null)
366: bvals[j++] = b;
367: }
368: return bvals;
369: }
370:
371: /** @return true if the value at index n exists and is non-null */
372: public boolean nthValid(int n) {
373: if (value == null || value.length <= n)
374: return false;
375: return value[n] != null;
376: }
377:
378: /** @return size of AVPair contents in bytes */
379: public int contentSize() {
380: int size = 0;
381: for (int i = 0; i < value.length; ++i) {
382: if (value[i] != null) {
383: if (value[i].bvalue != null) {
384: size += value[i].bvalue.length;
385: } else if (value[i].value != null) {
386: size += value[i].value.length() * 2;
387: }
388: }
389: }
390: return size;
391: }
392:
393: /** @return true if APVair is multivalued */
394: public boolean isMV() {
395: return getMaxIndex() != 0;
396: }
397:
398: /** @return a String representation of this AVPair - mainly for debugging */
399: public String toString() {
400: try {
401: int i;
402: StringBuffer sb = new StringBuffer(1000);
403: sb.append("AVPairs instance:\n" + "\tattribute\t= ["
404: + attribute + "]\n");
405: for (i = 0; i < value.length; ++i) {
406: sb.append("\tvalue[" + i + "]\t= [");
407: if (value[i] == null) {
408: sb.append("empty");
409: } else {
410: sb.append(value[i].value + "] / [");
411: if (value[i].bvalue == null)
412: sb.append("null");
413: else
414: sb
415: .append(new String(value[i].bvalue,
416: encoding));
417: }
418: sb.append("]\n");
419: }
420: return sb.toString();
421: } catch (java.io.UnsupportedEncodingException e) {
422: }
423: return "error";
424: }
425:
426: }
|