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