001    package edu.nrao.sss.model.source;
002    
003    import java.math.BigDecimal;
004    import java.net.MalformedURLException;
005    import java.net.URL;
006    import java.util.*;
007    
008    import edu.nrao.sss.astronomy.*;
009    import edu.nrao.sss.math.MathUtil;
010    import edu.nrao.sss.measure.*;
011    import edu.nrao.sss.util.*;
012    
013    /**
014     * Helper for test classes; builds sources and their components.
015     * <p>
016     * <b>Version Info:</b>
017     * <table style="margin-left:2em">
018     *   <tr><td>$Revision: 1361 $</td></tr>
019     *   <tr><td>$Date: 2008-06-19 14:21:15 -0600 (Thu, 19 Jun 2008) $</td></tr>
020     *   <tr><td>$Author: dharland $</td></tr>
021     * </table></p>
022     *  
023     * @author David M. Harland
024     * @since 2006-09-29
025     */
026    public class SourceBuilder
027    {
028      private static final EnumerationUtility ENUM_UTIL =
029        EnumerationUtility.getSharedInstance();
030    
031      private Random random = new Random(System.currentTimeMillis());
032    
033      private boolean setId        = false;
034      private Long    nextSourceId = 1L;
035      private Long    nextTableId  = 1L;
036      
037      private SkyPositionBuilder positionBuilder = new SkyPositionBuilder();
038      
039      //Used for coordinating multiple intervals
040      private Date timeIntervalEnd = null;
041      
042      //============================================================================
043      // PUBLIC METHODS
044      //============================================================================
045      
046      /**
047       * Tells the factory methods whether or not they should set the object
048       * identifiers.
049       */
050      public void setIdentifiers(boolean setIds)
051      {
052        setId = setIds;
053      }
054    
055      public Source makeSource(String name)
056      {
057        Source source = new Source(name == null ? makeName() : name);
058    
059        if (setId)
060          source.setId(nextSourceId++);
061        
062        //User info
063        Date now = new Date();
064        source.setCreatedOn(now);
065        source.setLastUpdatedOn(now);
066    
067        long id = 1L + (long)random.nextInt(1000);
068        source.setCreatedBy(id);
069        source.setLastUpdatedBy(id);
070        
071        if (random.nextDouble() > 0.8)
072          source.setOriginOfInformation(makeName());
073        
074        //Aliases
075        List<String> aliases = source.getAliases();
076        int count = random.nextInt(5);
077        for (int i=0; i < count; i++)
078          aliases.add(makeName());
079        
080        //User-defined keywords
081        Map<String, String> udvs = source.getUserDefinedValues();
082        count = random.nextInt(5);
083        for (int i=0; i < count; i++)
084          udvs.put(makeName(), makeName());
085        
086        //Image links
087        List<SourceImageLink> images = source.getImageLinks();
088        count = random.nextInt(5);
089        for (int i=0; i < count; i++)
090          images.add(makeImageLink());
091        
092        //Info links
093        List<URL> links = source.getLinks();
094        count = random.nextInt(5);
095        for (int i=0; i < count; i++)
096          links.add(makeInfoLink());
097        
098        //Historical records
099        List<String> historicalRecords = source.getHistoricalRecords();
100        count = random.nextInt(5);
101        for (int i=0; i < count; i++)
102          historicalRecords.add(makeHistoricalRecord());
103        
104        //Notes
105        List<String> notes = source.getNotes();
106        count = random.nextInt(3);
107        for (int n=0; n < count; n++)
108          notes.add("Random note #"+n);
109    
110        //Subsources
111        makeSubsource(source.getCentralSubsource());
112        
113        count = random.nextInt(2);
114        for (int ss=0; ss < count; ss++)
115          source.addSubsource(makeSubsource(null));
116    
117        return source;
118      }
119      
120      public SourceLookupTable makeTable(String name)
121      {
122        SourceLookupTable table = new SourceLookupTable(name == null ? makeName()
123                                                                     : name);
124        if (setId)
125          table.setId(nextTableId++);
126        
127        Calendar cal = Calendar.getInstance();
128    
129        int entryCount = 4 + random.nextInt(6);
130        for (int e=0; e < entryCount; e++)
131        {
132          table.put(cal.getTime(), makeSource(null));
133          cal.add(Calendar.DATE, 10);
134        }
135        
136        return table;
137      }
138      
139      public SourceGroup makeGroup(String name)
140      {
141        SourceGroup group = new SourceGroup(null, name == null ? makeName() : name);
142        
143        //Add sources
144        final int MIN_SRC_COUNT = 5;
145        int srcCount = MIN_SRC_COUNT + random.nextInt(15);
146        for (int s=0; s < srcCount; s++)
147          group.add(makeSource(null));
148    
149        Calendar cal = Calendar.getInstance();
150        List<SourceCatalogEntry> sources = group.getAll();
151        
152        //Add a table that contains only existing sources
153        SourceLookupTable table = new SourceLookupTable("Made From Existing Sources");
154        for (int s=0; s < MIN_SRC_COUNT; s++)
155        {
156          table.put(cal.getTime(), (Source)sources.get(s));
157          cal.add(Calendar.DATE, 10);
158        }
159        group.add(table);
160        
161        //Add a table that contains only new sources
162        group.add(makeTable("Made From New Sources"));
163        
164        //Add a table that contains a mix of new & existing sources
165        table = makeTable("Made From New And Existing Sources");
166        cal.setTime(table.getKeySet().last());
167        for (int s=0; s < MIN_SRC_COUNT; s++)
168        {
169          cal.add(Calendar.DATE, 10);
170          table.put(cal.getTime(), (Source)sources.get(s));
171        }
172        group.add(table);
173        
174        //Notes
175        List<String> notes = group.getNotes();
176        int count = random.nextInt(3);
177        for (int n=0; n < count; n++)
178          notes.add("Random note #"+n);
179    
180        return group;
181      }
182      
183      public SourceCatalog makeCatalog(String name)
184      {
185        SourceCatalog catalog = new SourceCatalog(name == null ? makeName() : name);
186        
187        Date now = new Date();
188        catalog.setCreatedOn(now);
189        catalog.setLastUpdatedOn(now);
190    
191        long id = 1L + (long)random.nextInt(1000);
192        catalog.setCreatedBy(id);
193        catalog.setLastUpdatedBy(id);
194        catalog.setOwner(id);
195    
196        //Add sources
197        int srcCount = 20 + random.nextInt(20);
198        for (int s=0; s < srcCount; s++)
199          catalog.addItem(makeSource(null));
200        
201        //Add a table
202        catalog.addItem(makeTable("Table 1"));
203        
204        //Add groups
205        int grpCount = random.nextInt(5);
206        for (int g=0; g < grpCount; g++)
207          catalog.addGroup(makeGroup(null));
208        
209        //Notes
210        List<String> notes = catalog.getNotes();
211        int count = random.nextInt(3);
212        for (int n=0; n < count; n++)
213          notes.add("Random note #"+n);
214    
215        return catalog;
216      }
217    
218      //============================================================================
219      // SUBSOURCE
220      //============================================================================
221    
222      Subsource makeSubsource(Subsource ss)
223      {
224        if (ss == null)
225        {
226          ss = new Subsource();
227          ss.setName(makeName());
228        }
229        
230        int count = 0;
231      
232        //Position
233        ss.setPosition(makePosition());
234        
235        //Velocities
236        count = 1 + random.nextInt(2);
237        for (int v=0; v < count; v++)
238          ss.addVelocity(makeVelocity());
239        
240        //Brightnesses
241        timeIntervalEnd = null;
242        count = 1 + random.nextInt(2);
243        for (int b=0; b < count; b++)
244          ss.addBrightness(makeBrightness());
245        
246        VlaFluxObservation fluxObs = makeVlaFluxData();
247        SourceBrightness[] sb = SourceBrightness.createBrightnesses(fluxObs);
248        ss.addBrightness(sb[0]);
249        ss.addBrightness(sb[1]);
250    
251        return ss;
252      }
253      
254      //============================================================================
255      // POSITION
256      //============================================================================
257    
258      private SkyPosition makePosition()
259      {
260        return positionBuilder.makePosition();
261      }
262    
263      //Moved old position-building code to new SkyPositionBuilder class
264      
265      //============================================================================
266      // VELOCITY
267      //============================================================================
268      
269      /** Returns a velocity populated with random values. */
270      SourceVelocity makeVelocity()
271      {
272        SourceVelocity velocity = new SourceVelocity();
273        
274        velocity.setConvention(ENUM_UTIL.getRandomValueFor(VelocityConvention.class));
275    
276        velocity.setRestFrame(ENUM_UTIL.getRandomValueFor(VelocityFrame.class));
277        
278        velocity.setValidFrequency(makeFrequencyRange());
279        
280        LinearVelocity speed =
281          new LinearVelocity(BigDecimal.valueOf(1000.0 * random.nextDouble()),
282                             velocity.getConvention().getDefaultUnits());
283        
284        velocity.setRadialVelocity(speed);
285        
286        return velocity;
287      }
288    
289      //============================================================================
290      // BRIGHTNESS
291      //============================================================================
292      
293      /** Returns a brightness populated with random values. */
294      SourceBrightness makeBrightness()
295      {
296        SourceBrightness brightness;
297        
298        double x = random.nextDouble();
299        
300        if      (x < 0.05) brightness = makeCleanFileBrightness();
301        else if (x < 0.10) brightness = makeFitsFileBrightness();
302        else if (x < 0.35) brightness = makeGaussianBrightness();
303        else if (x < 0.60) brightness = makeDiskBrightness();
304        else if (x < 0.85) brightness = makePointBrightness();
305        else               brightness = makeUnknownBrightness();
306    
307        return brightness;
308      }
309      
310      /** Simulates an observation record from the VLA. */
311      VlaFluxObservation makeVlaFluxData()
312      {
313        VlaFluxObservation data = new VlaFluxObservation();
314        
315        data.Source          = "bogus";
316        data.mjad            = 47000.0 + random.nextInt(8000);
317        data.correlator_mode = "no idea";
318        data.el_end          = random.nextInt(30);
319        data.obs_start       = 12.0 * random.nextDouble();
320        data.obs_end         = 12.0 * random.nextDouble() + 12.0;
321        data.time_obs        = data.obs_end - data.obs_start;
322        data.ac_freq         = 50.0 + random.nextDouble();
323        data.bd_freq         = data.ac_freq + 0.05;
324        data.ac_flux         = random.nextDouble() * random.nextInt(10);
325        data.bd_flux         = random.nextDouble() * random.nextInt(10);
326        data.ac_flux_stddev  = data.ac_flux * 0.1 * random.nextDouble(); 
327        data.bd_flux_stddev  = data.bd_flux * 0.1 * random.nextDouble();
328        
329        return data;
330      }
331    
332      private SourceBrightness makeCleanFileBrightness()
333      {
334        CleanFileBrightness brightness = new CleanFileBrightness();
335        
336        brightness.setValidTime(makeTimeInterval(timeIntervalEnd));
337    
338        try
339        {
340          brightness.setBrightnessFile(new URL(
341            "http://lacerta.gsfc.nasa.gov/vlbi/images/J2359-3133/J2359-3133_X_2005_07_09_yyk_map.fits"));
342        }
343        catch (MalformedURLException ex)
344        {
345          brightness.setBrightnessFile(null);
346        }
347        
348        return brightness;
349      }
350      
351      private SourceBrightness makeFitsFileBrightness()
352      {
353        FitsFileBrightness brightness = new FitsFileBrightness();
354        
355        brightness.setValidTime(makeTimeInterval(timeIntervalEnd));
356        
357        try
358        {
359          brightness.setBrightnessFile(new URL(
360          "http://lacerta.gsfc.nasa.gov/vlbi/images/J2359-3133/J2359-3133_X_2005_07_09_yyk_vis.fits"));
361        }
362        catch (MalformedURLException ex)
363        {
364          brightness.setBrightnessFile(null);
365        }
366        
367        return brightness;
368      }
369      
370      private SourceBrightness makePointBrightness()
371      {
372        PointBrightness brightness = new PointBrightness();
373       
374        brightness.setValidTime(makeTimeInterval(timeIntervalEnd));
375        
376        brightness.setValidFrequency(makeFrequencyRange());
377        brightness.setPeakFluxDensity(makeFluxDensity());
378        brightness.setTotalFluxDensity(makeFluxDensity());
379        brightness.setPolarization(
380          ENUM_UTIL.getRandomValueFor(StokesParameter.class));
381        
382        return brightness;
383      }
384      
385      private SourceBrightness makeUnknownBrightness()
386      {
387        UnknownBrightness brightness = new UnknownBrightness();
388       
389        brightness.setValidTime(makeTimeInterval(timeIntervalEnd));
390        
391        brightness.setValidFrequency(makeFrequencyRange());
392        brightness.setPeakFluxDensity(makeFluxDensity());
393        brightness.setTotalFluxDensity(makeFluxDensity());
394        brightness.setPolarization(
395          ENUM_UTIL.getRandomValueFor(StokesParameter.class));
396        brightness.setDiameter(10.0 + 90.0 * random.nextDouble());
397    
398        return brightness;
399      }
400    
401      private SourceBrightness makeGaussianBrightness()
402      {
403        GaussianBrightness brightness = new GaussianBrightness();
404    
405        brightness.setValidTime(makeTimeInterval(timeIntervalEnd));
406        
407        brightness.setValidFrequency(makeFrequencyRange());
408        brightness.setPeakFluxDensity(makeFluxDensity());
409        brightness.setTotalFluxDensity(makeFluxDensity());
410        brightness.setPolarization(
411          ENUM_UTIL.getRandomValueFor(StokesParameter.class));
412    
413        double axis = 10.0 + 90.0 * random.nextDouble();
414        brightness.setMinorAxisDiameter(axis);
415        brightness.setMajorAxisDiameter(axis * (1.0 + 3.0 * random.nextDouble()));
416        
417        return brightness;
418      }
419      
420      private SourceBrightness makeDiskBrightness()
421      {
422        DiskBrightness brightness = new DiskBrightness();
423    
424        brightness.setValidTime(makeTimeInterval(timeIntervalEnd));
425        
426        brightness.setValidFrequency(makeFrequencyRange());
427        brightness.setPeakFluxDensity(makeFluxDensity());
428        brightness.setTotalFluxDensity(makeFluxDensity());
429        brightness.setPolarization(
430          ENUM_UTIL.getRandomValueFor(StokesParameter.class));
431    
432        double axis = 10.0 + 90.0 * random.nextDouble();
433        brightness.setMinorAxisDiameter(axis);
434        brightness.setMajorAxisDiameter(axis * (1.0 + 3.0 * random.nextDouble()));
435        
436        brightness.setLimbDarkening(-1.0 + 2.0 * random.nextDouble());
437    
438        return brightness;
439      }
440    
441      //============================================================================
442      // HELPERS
443      //============================================================================
444      
445      private static final String[] histPrefix =
446      {
447       "Once upon a time,",
448       "In the beginning",
449       "In days of old",
450       "It is written that",
451       "A long, long time ago"
452      };
453      
454      private String makeHistoricalRecord()
455      {
456        StringBuilder buff = 
457          new StringBuilder(histPrefix[random.nextInt(histPrefix.length)]);
458        
459        int wordCount = 3 + random.nextInt(10);
460        for (int w=1; w <= wordCount; w++)
461          buff.append(' ').append(makeName());
462        
463        buff.append('.');
464        
465        return buff.toString();
466      }
467      
468      SourceImageLink makeImageLink()
469      {
470        SourceImageLink link = 
471          new SourceImageLink(makeFrequency(),
472                              ENUM_UTIL.getRandomValueFor(StokesParameter.class),
473                              makeUrl());
474        
475        link.setDisplayName("Image " + random.nextInt(10000));
476        
477        if (random.nextDouble() > 0.5)
478        {
479          StringBuilder buff = new StringBuilder("Hey, ");
480          int wordCount = random.nextInt(10);
481          for (int w=0; w < wordCount; w++)
482            buff.append(makeName()).append(' ');
483          buff.append(makeName()).append('!');
484        
485          link.setComments(buff.toString());
486        }
487        
488        return link;
489      }
490      
491      private static final String[] URLS =
492      {
493       "http://www.google.com",
494       "http://www.yahoo.com",
495       "http://www.wikipedia.com",
496       "http://www.nrao.edu",
497       "http://www.aoc.nrao.edu/~dharland/SteveAndDave.jpg",
498       "http://www.aoc.nrao.edu/~dharland/spongeBobHat.jpg",
499       "http://www.nj.com/mets/",
500       "http://www.nj.com/giants/"
501      };
502      
503      URL makeInfoLink()
504      {
505        return makeUrl(URLS[random.nextInt(URLS.length)]);
506      }
507      
508      private URL makeUrl()
509      {
510        return makeUrl("http://"+makeName()+".com/"+makeName()+".html");
511      }
512      
513      private URL makeUrl(String addr)
514      {
515        try {
516          return new URL(addr);
517        }
518        catch (Exception ex) {
519          return null;
520        }
521      }
522      
523      private Frequency makeFrequency()
524      {
525        return new Frequency(new BigDecimal(0.9 + 999.0 * random.nextDouble()),
526                             ENUM_UTIL.getRandomValueFor(FrequencyUnits.class));
527      }
528      
529      private FrequencyRange makeFrequencyRange()
530      {
531        FrequencyUnits freqUnits =
532          ENUM_UTIL.getRandomValueFor(FrequencyUnits.class);
533        
534        BigDecimal freqLow  = new BigDecimal(1.0 + 99.0 * random.nextDouble());
535        BigDecimal freqHigh = freqLow.multiply(new BigDecimal(1.0 + 9.0 * random.nextDouble()));
536        
537        //Try infinite freq once in awhile
538        if (random.nextDouble() <= 0.02)
539          freqHigh = MathUtil.POSITIVE_INFINITY;
540    
541        return new FrequencyRange(new Frequency(freqLow,  freqUnits),
542                                  new Frequency(freqHigh, freqUnits));
543      }
544      
545      private FluxDensity makeFluxDensity()
546      {
547        double r = 0.1 + 1000.0 * random.nextDouble();
548        return new FluxDensity(BigDecimal.valueOf(r),
549                               ENUM_UTIL.getRandomValueFor(FluxDensityUnits.class));
550      }
551      
552      private TimeInterval makeTimeInterval(Date startTime)
553      {
554        int year, month, day;
555        Date from;
556        Calendar cal = Calendar.getInstance();
557        
558        if (startTime == null)
559        {
560          year  = 1950 + random.nextInt(55);
561          month = random.nextInt(12);
562          day   = 1 + random.nextInt(27);
563          cal.set(year, month, day);
564          from = cal.getTime();
565        }
566        else
567        {
568          from = startTime;
569        }
570        
571        year = 2006 + random.nextInt(100);
572        month = random.nextInt(12);
573        day   = 1 + random.nextInt(27);
574        cal.set(year, month, day);
575        timeIntervalEnd = cal.getTime();
576        
577        return new TimeInterval(from, timeIntervalEnd);
578      }
579      
580      private String makeName()
581      {
582        StringBuilder srcName = new StringBuilder();
583    
584        int numChars = random.nextInt(3)+3;
585        for (int i=0; i < numChars; i++)
586          srcName.append((char)('a'+random.nextInt(25)));
587    
588        return srcName.toString();
589      }
590    }