1 // Copyright (c) 1999-2004 Brian Wellington (bwelling (at) 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 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 39 RRset(Record record) { 40 this(); 41 safeAddRR(record); 42 } 43 44 /** Creates an RRset with the contents of an existing RRset */ 45 public 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 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 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 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 106 clear() { 107 rrs.clear(); 108 position = 0; 109 nsigs = 0; 110 } 111 112 private synchronized Iterator 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 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 164 rrs() { 165 return iterator(true, true); 166 } 167 168 /** Returns an Iterator listing all signature records */ 169 public synchronized Iterator 170 sigs() { 171 return iterator(false, false); 172 } 173 174 /** Returns the number of (data) records */ 175 public synchronized int 176 size() { 177 return rrs.size() - nsigs; 178 } 179 180 /** 181 * Returns the name of the records 182 * @see Name 183 */ 184 public Name 185 getName() { 186 return first().getName(); 187 } 188 189 /** 190 * Returns the type of the records 191 * @see Type 192 */ 193 public int 194 getType() { 195 return first().getRRsetType(); 196 } 197 198 /** 199 * Returns the class of the records 200 * @see DClass 201 */ 202 public int 203 getDClass() { 204 return first().getDClass(); 205 } 206 207 /** Returns the ttl of the records */ 208 public synchronized long 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 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 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 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