1// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
2
3package org.xbill.DNS;
4
5import java.io.Serializable;
6import 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
17public class RRset implements Serializable {
18
19private static final long serialVersionUID = -3270249290171239695L;
20
21/*
22 * rrs contains both normal and RRSIG records, with the RRSIG records
23 * at the end.
24 */
25private List rrs;
26private short nsigs;
27private short position;
28
29/** Creates an empty RRset */
30public
31RRset() {
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 */
38public
39RRset(Record record) {
40	this();
41	safeAddRR(record);
42}
43
44/** Creates an RRset with the contents of an existing RRset */
45public
46RRset(RRset rrset) {
47	synchronized (rrset) {
48		rrs = (List) ((ArrayList)rrset.rrs).clone();
49		nsigs = rrset.nsigs;
50		position = rrset.position;
51	}
52}
53
54private void
55safeAddRR(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 */
68public synchronized void
69addRR(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 */
98public synchronized void
99deleteRR(Record r) {
100	if (rrs.remove(r) && (r instanceof RRSIGRecord))
101		nsigs--;
102}
103
104/** Deletes all Records from an RRset */
105public synchronized void
106clear() {
107	rrs.clear();
108	position = 0;
109	nsigs = 0;
110}
111
112private synchronized Iterator
113iterator(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 */
154public synchronized Iterator
155rrs(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 */
163public synchronized Iterator
164rrs() {
165	return iterator(true, true);
166}
167
168/** Returns an Iterator listing all signature records */
169public synchronized Iterator
170sigs() {
171	return iterator(false, false);
172}
173
174/** Returns the number of (data) records */
175public synchronized int
176size() {
177	return rrs.size() - nsigs;
178}
179
180/**
181 * Returns the name of the records
182 * @see Name
183 */
184public Name
185getName() {
186	return first().getName();
187}
188
189/**
190 * Returns the type of the records
191 * @see Type
192 */
193public int
194getType() {
195	return first().getRRsetType();
196}
197
198/**
199 * Returns the class of the records
200 * @see DClass
201 */
202public int
203getDClass() {
204	return first().getDClass();
205}
206
207/** Returns the ttl of the records */
208public synchronized long
209getTTL() {
210	return first().getTTL();
211}
212
213/**
214 * Returns the first record
215 * @throws IllegalStateException if the rrset is empty
216 */
217public synchronized Record
218first() {
219	if (rrs.size() == 0)
220		throw new IllegalStateException("rrset is empty");
221	return (Record) rrs.get(0);
222}
223
224private String
225iteratorToString(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 */
239public String
240toString() {
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