001: // Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
002:
003: package org.xbill.DNS;
004:
005: import java.util.*;
006:
007: /**
008: * A set of Records with the same name, type, and class. Also included
009: * are all RRSIG records signing the data records.
010: * @see Record
011: * @see RRSIGRecord
012: *
013: * @author Brian Wellington
014: */
015:
016: public class RRset {
017:
018: /*
019: * rrs contains both normal and RRSIG records, with the RRSIG records
020: * at the end.
021: */
022: private List rrs;
023: private short nsigs;
024: private short position;
025:
026: /** Creates an empty RRset */
027: public RRset() {
028: rrs = new ArrayList(1);
029: nsigs = 0;
030: position = 0;
031: }
032:
033: /** Creates an RRset and sets its contents to the specified record */
034: public RRset(Record record) {
035: this ();
036: safeAddRR(record);
037: }
038:
039: /** Creates an RRset with the contents of an existing RRset */
040: public RRset(RRset rrset) {
041: synchronized (rrset) {
042: rrs = (List) ((ArrayList) rrset.rrs).clone();
043: nsigs = rrset.nsigs;
044: position = rrset.position;
045: }
046: }
047:
048: private void safeAddRR(Record r) {
049: if (!(r instanceof RRSIGRecord)) {
050: if (nsigs == 0)
051: rrs.add(r);
052: else
053: rrs.add(rrs.size() - nsigs, r);
054: } else {
055: rrs.add(r);
056: nsigs++;
057: }
058: }
059:
060: /** Adds a Record to an RRset */
061: public synchronized void addRR(Record r) {
062: if (rrs.size() == 0) {
063: safeAddRR(r);
064: return;
065: }
066: Record first = first();
067: if (!r.sameRRset(first))
068: throw new IllegalArgumentException("record does not match "
069: + "rrset");
070:
071: if (r.getTTL() != first.getTTL()) {
072: if (r.getTTL() > first.getTTL()) {
073: r = r.cloneRecord();
074: r.setTTL(first.getTTL());
075: } else {
076: for (int i = 0; i < rrs.size(); i++) {
077: Record tmp = (Record) rrs.get(i);
078: tmp = tmp.cloneRecord();
079: tmp.setTTL(r.getTTL());
080: rrs.set(i, tmp);
081: }
082: }
083: }
084:
085: if (!rrs.contains(r))
086: safeAddRR(r);
087: }
088:
089: /** Deletes a Record from an RRset */
090: public synchronized void deleteRR(Record r) {
091: if (rrs.remove(r) && (r instanceof RRSIGRecord))
092: nsigs--;
093: }
094:
095: /** Deletes all Records from an RRset */
096: public synchronized void clear() {
097: rrs.clear();
098: position = 0;
099: nsigs = 0;
100: }
101:
102: private synchronized Iterator iterator(boolean data, boolean cycle) {
103: int size, start, total;
104:
105: total = rrs.size();
106:
107: if (data)
108: size = total - nsigs;
109: else
110: size = nsigs;
111: if (size == 0)
112: return Collections.EMPTY_LIST.iterator();
113:
114: if (data) {
115: if (!cycle)
116: start = 0;
117: else {
118: if (position >= size)
119: position = 0;
120: start = position++;
121: }
122: } else {
123: start = total - nsigs;
124: }
125:
126: List list = new ArrayList(size);
127: if (data) {
128: list.addAll(rrs.subList(start, size));
129: if (start != 0)
130: list.addAll(rrs.subList(0, start));
131: } else {
132: list.addAll(rrs.subList(start, total));
133: }
134:
135: return list.iterator();
136: }
137:
138: /**
139: * Returns an Iterator listing all (data) records.
140: * @param cycle If true, cycle through the records so that each Iterator will
141: * start with a different record.
142: */
143: public synchronized Iterator rrs(boolean cycle) {
144: return iterator(true, cycle);
145: }
146:
147: /**
148: * Returns an Iterator listing all (data) records. This cycles through
149: * the records, so each Iterator will start with a different record.
150: */
151: public synchronized Iterator rrs() {
152: return iterator(true, true);
153: }
154:
155: /** Returns an Iterator listing all signature records */
156: public synchronized Iterator sigs() {
157: return iterator(false, false);
158: }
159:
160: /** Returns the number of (data) records */
161: public int size() {
162: return rrs.size() - nsigs;
163: }
164:
165: /**
166: * Returns the name of the records
167: * @see Name
168: */
169: public Name getName() {
170: return first().getName();
171: }
172:
173: /**
174: * Returns the type of the records
175: * @see Type
176: */
177: public int getType() {
178: return first().getRRsetType();
179: }
180:
181: /**
182: * Returns the class of the records
183: * @see DClass
184: */
185: public int getDClass() {
186: return first().getDClass();
187: }
188:
189: /** Returns the ttl of the records */
190: public synchronized long getTTL() {
191: return first().getTTL();
192: }
193:
194: /**
195: * Returns the first record
196: * @throws IllegalStateException if the rrset is empty
197: */
198: public synchronized Record first() {
199: if (rrs.size() == 0)
200: throw new IllegalStateException("rrset is empty");
201: return (Record) rrs.get(0);
202: }
203:
204: private String iteratorToString(Iterator it) {
205: StringBuffer sb = new StringBuffer();
206: while (it.hasNext()) {
207: Record rr = (Record) it.next();
208: sb.append("[");
209: sb.append(rr.rdataToString());
210: sb.append("]");
211: if (it.hasNext())
212: sb.append(" ");
213: }
214: return sb.toString();
215: }
216:
217: /** Converts the RRset to a String */
218: public String toString() {
219: if (rrs == null)
220: return ("{empty}");
221: StringBuffer sb = new StringBuffer();
222: sb.append("{ ");
223: sb.append(getName() + " ");
224: sb.append(getTTL() + " ");
225: sb.append(DClass.string(getDClass()) + " ");
226: sb.append(Type.string(getType()) + " ");
227: sb.append(iteratorToString(iterator(true, false)));
228: if (nsigs > 0) {
229: sb.append(" sigs: ");
230: sb.append(iteratorToString(iterator(false, false)));
231: }
232: sb.append(" }");
233: return sb.toString();
234: }
235:
236: }
|