001 package edu.nrao.sss.model.resource; 002 003 import java.util.List; 004 import java.util.Map; 005 import java.util.Set; 006 import java.util.SortedMap; 007 import java.util.SortedSet; 008 import java.util.TreeMap; 009 import java.util.TreeSet; 010 import java.util.UUID; 011 012 import javax.xml.bind.annotation.XmlAttribute; 013 import javax.xml.bind.annotation.XmlElement; 014 import javax.xml.bind.annotation.XmlElementWrapper; 015 import javax.xml.bind.annotation.XmlID; 016 017 import edu.nrao.sss.astronomy.PolarizationType; 018 import edu.nrao.sss.astronomy.StokesParameter; 019 import edu.nrao.sss.util.Identifiable; 020 021 /** 022 * Partial implementation of a 023 * {@link CorrelationProductGroup correlation product group}. 024 * <p> 025 * <b>Version Info:</b> 026 * <table style="margin-left:2em"> 027 * <tr><td>$Revision: 2289 $</td></tr> 028 * <tr><td>$Date: 2009-05-07 16:13:41 -0600 (Thu, 07 May 2009) $</td></tr> 029 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 030 * </table></p> 031 * 032 * @author David M. Harland 033 * @since 2008-11-25 034 */ 035 public abstract class CorrelationProductGroupAbs 036 implements CorrelationProductGroup 037 { 038 private Long id; //A unique identifier for the persistence layer. 039 040 @XmlAttribute(required=true) 041 @XmlID 042 private String uuid; 043 044 /** 045 * The subband that holds this group. 046 * Under normal circumstances this value is not <i>null</i>. However, most 047 * uses of this variable protect against <i>null</i> because when this object 048 * is first reconstructed from XML or a database, this variable may be 049 * <i>null</i> momentarily. 050 * <p> 051 * Subclass authors should try to prevent this value from being <i>null</i>.</p> 052 */ 053 private CorrelatorSubbandAbs subband; 054 055 /** 056 * A sorted map whose keys are correlation types and whose values are 057 * spectral channel counts. 058 * Subclasses must not set this value to <i>null</i>. 059 */ 060 protected SortedMap<StokesParameter, Integer> channels; 061 062 /** 063 * Set to <i>true</i> when the previously calculated channels is a stale 064 * value. 065 */ 066 protected boolean needToRecalcChannels; 067 068 /** Helps create a new group. */ 069 protected CorrelationProductGroupAbs() 070 { 071 id = Identifiable.UNIDENTIFIED; 072 073 uuid = "CPG-"+UUID.randomUUID().toString(); 074 075 channels = new TreeMap<StokesParameter, Integer>(); 076 077 needToRecalcChannels = true; 078 } 079 080 //============================================================================ 081 // IDENTIFICATION 082 //============================================================================ 083 084 /* (non-Javadoc) 085 * @see edu.nrao.sss.util.Identifiable#getId() 086 */ 087 public Long getId() { return id; } 088 089 @SuppressWarnings("unused") 090 private void setId(Long id) { this.id = id; } 091 092 public void clearId() 093 { 094 id = Identifiable.UNIDENTIFIED; 095 } 096 097 public String getUUID() { return uuid; } 098 099 //============================================================================ 100 // SUBBAND 101 //============================================================================ 102 103 /** 104 * Primarily for use by persistence mechanism. 105 * Subclass authors should try to prevent this value from being <i>null</i>. 106 */ 107 protected void setContainer(CorrelatorSubbandAbs newContainer) 108 { 109 subband = newContainer; 110 } 111 112 /** Primarily for use by persistence mechanism. */ 113 protected CorrelatorSubbandAbs getContainer() 114 { 115 return subband; 116 } 117 118 //============================================================================ 119 // CORRELATION PRODUCTS 120 //============================================================================ 121 122 /** 123 * Called when something that can influence the total number of channels, 124 * or the distribution of channels among products, has changed. 125 * The main job of this method is to update the <tt>channels</tt> map. 126 * Implementations should also set the variable <tt>needToRecalcChannels</tt> 127 * to <i>false</i> upon successful recalculation. 128 */ 129 abstract protected void recalculateChannels(); 130 131 /** 132 * Removes from the <tt>channels</tt> map any entries for Stokes parameters 133 * that are not in the allowable set. 134 */ 135 protected void removeDisallowedProducts() 136 { 137 SortedSet<StokesParameter> allowed = getAllowablePolarizationProducts(); 138 139 Set<StokesParameter> disallowed = new TreeSet<StokesParameter>(); 140 141 //Can't remove while in this loop or get ConcurrentModificationException 142 for (StokesParameter sp : channels.keySet()) 143 if (!allowed.contains(sp)) 144 disallowed.add(sp); 145 146 for (StokesParameter sp : disallowed) 147 channels.remove(sp); 148 } 149 150 /* (non-Javadoc) 151 * @see edu.nrao.sss.model.resource.CorrelatorSubband#getAllowablePolarizationProducts() 152 */ 153 public SortedSet<StokesParameter> getAllowablePolarizationProducts() 154 { 155 SortedSet<StokesParameter> allowableProducts; 156 157 CorrelatorBaseband bb = null; 158 159 if (subband != null) 160 bb = subband.getBaseband(); 161 162 if (bb == null) 163 { 164 allowableProducts = new TreeSet<StokesParameter>(); 165 } 166 else //Get the polarization type(s) from the baseband 167 { 168 List<PolarizationType> polarizations = bb.getPolarizations(); 169 PolarizationType p1 = null, p2 = null; 170 int listSize = polarizations.size(); 171 if (listSize >= 1) 172 { 173 p1 = polarizations.get(0); 174 175 if (listSize >= 2) 176 { 177 p2 = polarizations.get(1); 178 179 if (listSize > 2) 180 throw new IllegalStateException("PROGRAMMER ERROR: Found " + 181 polarizations.size() + 182 " polarizations in baseband. Expected either 1 or 2."); 183 } 184 } 185 //Turn the polarization type(s) into Stokes parameter(s) 186 allowableProducts = StokesParameter.getStokesFor(p1, p2); 187 } 188 189 return allowableProducts; 190 } 191 192 /* (non-Javadoc) 193 * @see CorrelationProductGroup#addPolarizationProduct(StokesParameter) 194 */ 195 public boolean addPolarizationProduct(StokesParameter stokes) 196 { 197 //See if it's allowed 198 boolean success = getAllowablePolarizationProducts().contains(stokes); 199 200 //If allowed, see if we already have it; if not, add it 201 if (success && !channels.keySet().contains(stokes)) 202 { 203 channels.put(stokes, 0); 204 needToRecalcChannels = true; 205 } 206 207 return success; 208 } 209 210 /* (non-Javadoc) 211 * @see CorrelationProductGroup#removePolarizationProduct(StokesParameter) 212 */ 213 public int removePolarizationProduct(StokesParameter stokes) 214 { 215 Integer channelsRemoved = channels.remove(stokes); 216 217 int answer = (channelsRemoved == null) ? 0 : channelsRemoved.intValue(); 218 219 if (answer > 0) 220 needToRecalcChannels = true; 221 222 return answer; 223 } 224 225 /* (non-Javadoc) 226 * @see CorrelationProductGroup#removeAllPolarizationProducts() 227 */ 228 public void removeAllPolarizationProducts() 229 { 230 channels.clear(); 231 needToRecalcChannels = true; 232 } 233 234 /* (non-Javadoc) 235 * @see CorrelationProductGroup#getSpectralChannels() 236 */ 237 public int getSpectralChannels() 238 { 239 if (needToRecalcChannels) 240 recalculateChannels(); 241 242 int channelCount = 0; 243 for (int ch : channels.values()) 244 channelCount += ch; 245 246 return channelCount; 247 } 248 249 /* (non-Javadoc) 250 * @see CorrelationProductGroup#getSpectralChannels(StokesParameter) 251 */ 252 public int getSpectralChannels(StokesParameter stokes) 253 { 254 if (needToRecalcChannels) 255 recalculateChannels(); 256 257 Integer channelCount = channels.get(stokes); 258 259 return channelCount == null ? 0 : channelCount.intValue(); 260 } 261 262 @XmlElementWrapper(name="correlationProducts") 263 @XmlElement(name="product") 264 @SuppressWarnings("unused") //JAXB use. Helps deal w/ the underlying sorted map. 265 private CorrProd[] getXmlCorrProds() 266 { 267 int count = channels.size(); 268 269 CorrProd[] array = new CorrProd[count]; 270 271 int i = 0; 272 273 for (Map.Entry<StokesParameter, Integer> map : channels.entrySet()) 274 array[i++] = new CorrProd(map.getKey(), map.getValue()); 275 276 return array; 277 } 278 279 @SuppressWarnings("unused") //JAXB use 280 private void setXmlCorrProds(CorrProd[] replacements) 281 { 282 channels.clear(); 283 284 for (CorrProd corrProd : replacements) 285 channels.put(corrProd.correlation, corrProd.channels); 286 } 287 288 //Used only for JAXB. Helps deal w/ the underlying sorted map. 289 static class CorrProd 290 { 291 @XmlAttribute StokesParameter correlation; 292 @XmlAttribute Integer channels; 293 294 CorrProd() { this(null,null); } 295 296 CorrProd(StokesParameter key, Integer value) 297 { 298 this.correlation = key; 299 this.channels = value; 300 } 301 } 302 303 //============================================================================ 304 // UTILITY 305 //============================================================================ 306 307 /** 308 * Sets {@code other}'s internal variables to the same values as those of 309 * this group. The exceptions are the ID, which is cleared, and the UUID. 310 * 311 * @param other 312 * a correlation product group whose values should be set to mimic the 313 * values in this group. 314 */ 315 protected void copyInto(CorrelationProductGroupAbs other) 316 { 317 other.channels = new TreeMap<StokesParameter, Integer>(this.channels); 318 319 other.needToRecalcChannels = this.needToRecalcChannels; 320 321 other.clearId(); 322 } 323 324 //NOTE TO PROGRAMMERS: 325 // 326 // The equals and hashCode methods have been intentionally removed. 327 // The reason centered around the channels inst var and the way 328 // the WIDAR subclass handled these. Do NOT reinsert these methods 329 // unless you have a thorough understanding of the consequences 330 // for the Widar, and any other, subclasses. Be sure to run the 331 // cloning and xmlTransformation unit tests for the Widar subclass 332 // before committing any changes. 333 // --DMH 2009-Apr-15 334 /** 335 * Returns <i>true</i> if <tt>o</tt> is equal to this group. 336 * <p> 337 * Items not compared in the equality tests:</p> 338 * <ol> 339 * <li>ID</li> 340 * <li>UUID</li> 341 * <li>Subband</li> 342 * <li>Number of channels</li> 343 * </ol> 344 * <p> 345 * Note that the number and type of correlation products is examined, 346 * but not the channels for each. Subclasses may use such a check 347 * in their overrides of this method.</p> 348 */ 349 @Override 350 public boolean equals(Object o) 351 { 352 //Quick exit if o is null 353 if (o == null) 354 return false; 355 356 //Quick exit if o is this 357 if (o == this) 358 return true; 359 360 //Quick exit if classes are different 361 if (!o.getClass().equals(this.getClass())) 362 return false; 363 364 CorrelationProductGroupAbs other = (CorrelationProductGroupAbs)o; 365 366 //Intentionally NOT comparing ID, uuid, subband, numbers of channels 367 368 return other.channels.keySet().equals(this.channels.keySet()); 369 } 370 371 @Override 372 public int hashCode() 373 { 374 //Taken from the Effective Java book by Joshua Bloch. 375 //The constants 17 & 37 are arbitrary & carry no meaning. 376 int result = 17; 377 378 //You MUST keep this method in sync w/ the equals method 379 380 return 37 * result + this.channels.keySet().hashCode(); 381 } 382 }