1 // Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
2
3 package org.xbill.DNS;
4
5 import java.io.Serializable;
6 import java.util.*;
7
8 /**
9 * A set of Records with the same name, type, and class. Also included
10 * are all RRSIG records signing the data records.
11 * @see Record
12 * @see RRSIGRecord
13 *
14 * @author Brian Wellington
15 */
16
17 public class RRset implements Serializable {
18
19 private static final long serialVersionUID = -3270249290171239695L;
20
21 /*
22 * rrs contains both normal and RRSIG records, with the RRSIG records
23 * at the end.
24 */
25 private List rrs;
26 private short nsigs;
27 private short position;
28
29 /** Creates an empty RRset */
30 public
RRset()31 RRset() {
32 rrs = new ArrayList(1);
33 nsigs = 0;
34 position = 0;
35 }
36
37 /** Creates an RRset and sets its contents to the specified record */
38 public
RRset(Record record)39 RRset(Record record) {
40 this();
41 safeAddRR(record);
42 }
43
44 /** Creates an RRset with the contents of an existing RRset */
45 public
RRset(RRset rrset)46 RRset(RRset rrset) {
47 synchronized (rrset) {
48 rrs = (List) ((ArrayList)rrset.rrs).clone();
49 nsigs = rrset.nsigs;
50 position = rrset.position;
51 }
52 }
53
54 private void
safeAddRR(Record r)55 safeAddRR(Record r) {
56 if (!(r instanceof RRSIGRecord)) {
57 if (nsigs == 0)
58 rrs.add(r);
59 else
60 rrs.add(rrs.size() - nsigs, r);
61 } else {
62 rrs.add(r);
63 nsigs++;
64 }
65 }
66
67 /** Adds a Record to an RRset */
68 public synchronized void
addRR(Record r)69 addRR(Record r) {
70 if (rrs.size() == 0) {
71 safeAddRR(r);
72 return;
73 }
74 Record first = first();
75 if (!r.sameRRset(first))
76 throw new IllegalArgumentException("record does not match " +
77 "rrset");
78
79 if (r.getTTL() != first.getTTL()) {
80 if (r.getTTL() > first.getTTL()) {
81 r = r.cloneRecord();
82 r.setTTL(first.getTTL());
83 } else {
84 for (int i = 0; i < rrs.size(); i++) {
85 Record tmp = (Record) rrs.get(i);
86 tmp = tmp.cloneRecord();
87 tmp.setTTL(r.getTTL());
88 rrs.set(i, tmp);
89 }
90 }
91 }
92
93 if (!rrs.contains(r))
94 safeAddRR(r);
95 }
96
97 /** Deletes a Record from an RRset */
98 public synchronized void
deleteRR(Record r)99 deleteRR(Record r) {
100 if (rrs.remove(r) && (r instanceof RRSIGRecord))
101 nsigs--;
102 }
103
104 /** Deletes all Records from an RRset */
105 public synchronized void
clear()106 clear() {
107 rrs.clear();
108 position = 0;
109 nsigs = 0;
110 }
111
112 private synchronized Iterator
iterator(boolean data, boolean cycle)113 iterator(boolean data, boolean cycle) {
114 int size, start, total;
115
116 total = rrs.size();
117
118 if (data)
119 size = total - nsigs;
120 else
121 size = nsigs;
122 if (size == 0)
123 return Collections.EMPTY_LIST.iterator();
124
125 if (data) {
126 if (!cycle)
127 start = 0;
128 else {
129 if (position >= size)
130 position = 0;
131 start = position++;
132 }
133 } else {
134 start = total - nsigs;
135 }
136
137 List list = new ArrayList(size);
138 if (data) {
139 list.addAll(rrs.subList(start, size));
140 if (start != 0)
141 list.addAll(rrs.subList(0, start));
142 } else {
143 list.addAll(rrs.subList(start, total));
144 }
145
146 return list.iterator();
147 }
148
149 /**
150 * Returns an Iterator listing all (data) records.
151 * @param cycle If true, cycle through the records so that each Iterator will
152 * start with a different record.
153 */
154 public synchronized Iterator
rrs(boolean cycle)155 rrs(boolean cycle) {
156 return iterator(true, cycle);
157 }
158
159 /**
160 * Returns an Iterator listing all (data) records. This cycles through
161 * the records, so each Iterator will start with a different record.
162 */
163 public synchronized Iterator
rrs()164 rrs() {
165 return iterator(true, true);
166 }
167
168 /** Returns an Iterator listing all signature records */
169 public synchronized Iterator
sigs()170 sigs() {
171 return iterator(false, false);
172 }
173
174 /** Returns the number of (data) records */
175 public synchronized int
size()176 size() {
177 return rrs.size() - nsigs;
178 }
179
180 /**
181 * Returns the name of the records
182 * @see Name
183 */
184 public Name
getName()185 getName() {
186 return first().getName();
187 }
188
189 /**
190 * Returns the type of the records
191 * @see Type
192 */
193 public int
getType()194 getType() {
195 return first().getRRsetType();
196 }
197
198 /**
199 * Returns the class of the records
200 * @see DClass
201 */
202 public int
getDClass()203 getDClass() {
204 return first().getDClass();
205 }
206
207 /** Returns the ttl of the records */
208 public synchronized long
getTTL()209 getTTL() {
210 return first().getTTL();
211 }
212
213 /**
214 * Returns the first record
215 * @throws IllegalStateException if the rrset is empty
216 */
217 public synchronized Record
first()218 first() {
219 if (rrs.size() == 0)
220 throw new IllegalStateException("rrset is empty");
221 return (Record) rrs.get(0);
222 }
223
224 private String
iteratorToString(Iterator it)225 iteratorToString(Iterator it) {
226 StringBuffer sb = new StringBuffer();
227 while (it.hasNext()) {
228 Record rr = (Record) it.next();
229 sb.append("[");
230 sb.append(rr.rdataToString());
231 sb.append("]");
232 if (it.hasNext())
233 sb.append(" ");
234 }
235 return sb.toString();
236 }
237
238 /** Converts the RRset to a String */
239 public String
toString()240 toString() {
241 if (rrs == null)
242 return ("{empty}");
243 StringBuffer sb = new StringBuffer();
244 sb.append("{ ");
245 sb.append(getName() + " ");
246 sb.append(getTTL() + " ");
247 sb.append(DClass.string(getDClass()) + " ");
248 sb.append(Type.string(getType()) + " ");
249 sb.append(iteratorToString(iterator(true, false)));
250 if (nsigs > 0) {
251 sb.append(" sigs: ");
252 sb.append(iteratorToString(iterator(false, false)));
253 }
254 sb.append(" }");
255 return sb.toString();
256 }
257
258 }
259