001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.solr.util;
017:
018: import java.util.*;
019: import java.io.Serializable;
020:
021: /**
022: * A simple container class for modeling an ordered list of name/value pairs.
023: *
024: * <p>
025: * Unlike Maps:
026: * </p>
027: * <ul>
028: * <li>Names may be repeated</li>
029: * <li>Order of elements is maintained</li>
030: * <li>Elements may be accessed by numeric index</li>
031: * <li>Names and Values can both be null</li>
032: * </ul>
033: *
034: * <p>
035: * A NamedList provides fast access by element number, but not by name.
036: * </p>
037: * <p>
038: * When a NamedList is serialized, order is considered more important than access
039: * by key, so ResponseWriters that output to a format such as JSON will normally
040: * choose a data structure that allows order to be easily preserved in various
041: * clients (i.e. not a straight map).
042: * If access by key is more important, see {@link SimpleOrderedMap},
043: * or simply use a regular {@link Map}
044: * </p>
045: *
046: * @author yonik
047: * @version $Id: NamedList.java 501512 2007-01-30 18:36:32Z yonik $
048: */
049: public class NamedList<T> implements Cloneable, Serializable,
050: Iterable<Map.Entry<String, T>> {
051: protected final List nvPairs;
052:
053: /** Creates an empty instance */
054: public NamedList() {
055: nvPairs = new ArrayList();
056: }
057:
058: /**
059: * Creates an instance backed by an explicitly specified list of
060: * pairwise names/values.
061: *
062: * @param nameValuePairs underlying List which should be used to implement a NamedList; modifying this List will affect the NamedList.
063: */
064: public NamedList(List nameValuePairs) {
065: nvPairs = nameValuePairs;
066: }
067:
068: /** The total number of name/value pairs */
069: public int size() {
070: return nvPairs.size() >> 1;
071: }
072:
073: /**
074: * The name of the pair at the specified List index
075: *
076: * @return null if no name exists
077: */
078: public String getName(int idx) {
079: return (String) nvPairs.get(idx << 1);
080: }
081:
082: /**
083: * The value of the pair at the specified List index
084: *
085: * @return may be null
086: */
087: @SuppressWarnings("unchecked")
088: public T getVal(int idx) {
089: return (T) nvPairs.get((idx << 1) + 1);
090: }
091:
092: /**
093: * Adds a name/value pair to the end of the list.
094: */
095: public void add(String name, T val) {
096: nvPairs.add(name);
097: nvPairs.add(val);
098: }
099:
100: /**
101: * Modifies the name of the pair at the specified index.
102: */
103: public void setName(int idx, String name) {
104: nvPairs.set(idx << 1, name);
105: }
106:
107: /**
108: * Modifies the value of the pair at the specified index.
109: * @return the value that used to be at index
110: */
111: public T setVal(int idx, T val) {
112: int index = (idx << 1) + 1;
113: T old = (T) nvPairs.get(index);
114: nvPairs.set(index, val);
115: return old;
116: }
117:
118: /**
119: * Scans the list sequentially beginning at the specified index and
120: * returns the index of the first pair with the specified name.
121: *
122: * @param name name to look for, may be null
123: * @param start index to begin searching from
124: * @return The index of the first matching pair, -1 if no match
125: */
126: public int indexOf(String name, int start) {
127: int sz = size();
128: for (int i = start; i < sz; i++) {
129: String n = getName(i);
130: if (name == null) {
131: if (n == null)
132: return i; // matched null
133: } else if (name.equals(n)) {
134: return i;
135: }
136: }
137: return -1;
138: }
139:
140: /**
141: * Gets the value for the first instance of the specified name
142: * found.
143: *
144: * @return null if not found or if the value stored was null.
145: * @see #indexOf
146: * @see #get(String,int)
147: */
148: public T get(String name) {
149: return get(name, 0);
150: }
151:
152: /**
153: * Gets the value for the first instance of the specified name
154: * found starting at the specified index.
155: *
156: * @return null if not found or if the value stored was null.
157: * @see #indexOf
158: */
159: public T get(String name, int start) {
160: int sz = size();
161: for (int i = start; i < sz; i++) {
162: String n = getName(i);
163: if (name == null) {
164: if (n == null)
165: return getVal(i);
166: } else if (name.equals(n)) {
167: return getVal(i);
168: }
169: }
170: return null;
171: }
172:
173: public String toString() {
174: StringBuffer sb = new StringBuffer();
175: sb.append('{');
176: int sz = size();
177: for (int i = 0; i < sz; i++) {
178: if (i != 0)
179: sb.append(',');
180: sb.append(getName(i));
181: sb.append('=');
182: sb.append(getVal(i));
183: }
184: sb.append('}');
185:
186: return sb.toString();
187: }
188:
189: /**
190: * Iterates over the Map and sequentially adds it's key/value pairs
191: */
192: public boolean addAll(Map<String, T> args) {
193: for (Map.Entry<String, T> entry : args.entrySet()) {
194: add(entry.getKey(), entry.getValue());
195: }
196: return args.size() > 0;
197: }
198:
199: /** Appends the elements of the given NamedList to this one. */
200: public boolean addAll(NamedList<T> nl) {
201: nvPairs.addAll(nl.nvPairs);
202: return nl.size() > 0;
203: }
204:
205: /**
206: * Makes a <i>shallow copy</i> of the named list.
207: */
208: public NamedList<T> clone() {
209: ArrayList newList = new ArrayList(nvPairs.size());
210: newList.addAll(nvPairs);
211: return new NamedList<T>(newList);
212: }
213:
214: //----------------------------------------------------------------------------
215: // Iterable interface
216: //----------------------------------------------------------------------------
217:
218: /**
219: * Support the Iterable interface
220: */
221: public Iterator<Map.Entry<String, T>> iterator() {
222:
223: final NamedList list = this ;
224:
225: Iterator<Map.Entry<String, T>> iter = new Iterator<Map.Entry<String, T>>() {
226:
227: int idx = 0;
228:
229: public boolean hasNext() {
230: return idx < list.size();
231: }
232:
233: public Map.Entry<String, T> next() {
234: final int index = idx++;
235: Map.Entry<String, T> nv = new Map.Entry<String, T>() {
236: public String getKey() {
237: return list.getName(index);
238: }
239:
240: @SuppressWarnings("unchecked")
241: public T getValue() {
242: return (T) list.getVal(index);
243: }
244:
245: public String toString() {
246: return getKey() + "=" + getValue();
247: }
248:
249: public T setValue(T value) {
250: return (T) list.setVal(index, value);
251: }
252: };
253: return nv;
254: }
255:
256: public void remove() {
257: throw new UnsupportedOperationException();
258: }
259: };
260: return iter;
261: }
262: }
|