001    package edu.nrao.sss.model.resource;
002    
003    import java.io.FileNotFoundException;
004    import java.io.Reader;
005    import java.io.Writer;
006    import java.util.ArrayList;
007    import java.util.Date;
008    import java.util.HashMap;
009    import java.util.List;
010    
011    import javax.xml.bind.JAXBException;
012    import javax.xml.bind.annotation.XmlElement;
013    import javax.xml.bind.annotation.XmlElementRef;
014    import javax.xml.bind.annotation.XmlElementWrapper;
015    import javax.xml.bind.annotation.XmlRootElement;
016    import javax.xml.bind.annotation.XmlType;
017    import javax.xml.stream.XMLStreamException;
018    
019    import edu.nrao.sss.catalog.Catalog;
020    import edu.nrao.sss.model.RepositoryException;
021    import edu.nrao.sss.model.UserAccountable;
022    import edu.nrao.sss.util.Identifiable;
023    import edu.nrao.sss.util.JaxbUtility;
024    import edu.nrao.sss.util.StringUtil;
025    
026    /**
027     * A catalog of {@link Resource}s.
028     * <p>
029     * Each entry in a resource catalog is a {@code Resource}.
030     * This catalog also supports
031     * the notion of {@link ResourceGroup}s, which serve to
032     * associate resources with similar traits with one another.</p>
033     * <p>
034     * <b>Version Info:</b>
035     * <table style="margin-left:2em">
036     *   <tr><td>$Revision: 1709 $</td></tr>
037     *   <tr><td>$Date: 2008-11-14 11:22:37 -0700 (Fri, 14 Nov 2008) $</td></tr>
038     *   <tr><td>$Author: dharland $ (last person to modify)</td></tr>
039     * </table></p>
040     * 
041     * @author David M. Harland
042     * @since 2006-10-20
043     */
044    @XmlRootElement
045    @XmlType(propOrder= {"owner",
046                         "createdBy","createdOn","lastUpdatedBy","lastUpdatedOn",
047                         "entries", "resourceGroups"
048                         })
049    public class ResourceCatalog
050      extends Catalog<Resource, ResourceGroup, ResourceCatalog>
051      implements Identifiable, UserAccountable, ResourceProvider
052    {
053      //USER TRACKING
054      private Long owner;          //This is a user ID
055      private Long createdBy;      //This is a user ID
056      private Date createdOn;
057      private Long lastUpdatedBy;  //This is a user ID
058      private Date lastUpdatedOn;
059    
060      /** Creates a new catalog with a default name. */
061      public ResourceCatalog()
062      {
063        this(null);
064      }
065      
066      /**
067       * Creates a new catalog with the given name.
068       * 
069       * @param nameOfCatalog the name of this catalog.  If this value is
070       *                      <i>null</i>, this catalog will be given a
071       *                      default name.
072       */
073      public ResourceCatalog(String nameOfCatalog)
074      {
075        super(nameOfCatalog);
076        
077        owner         = UserAccountable.NULL_USER_ID;
078        createdBy     = UserAccountable.NULL_USER_ID;
079        createdOn     = new Date();
080        lastUpdatedBy = UserAccountable.NULL_USER_ID;
081        lastUpdatedOn = new Date();
082      }
083      
084      /** Returns {@link Identifiable#UNIDENTIFIED}. */
085      @Override
086      protected long getIdOfUnidentified()
087      {
088        return Identifiable.UNIDENTIFIED;
089      }
090    
091      @Override
092      protected ResourceGroup createMainGroup()
093      {
094        setReservedGroupName("All Resources");
095        
096        return new ResourceGroup(this);
097      }
098      
099      @Override
100      public ResourceGroup createGroup()
101      {
102        return new ResourceGroup();
103      }
104    
105      /**
106       * Resets this catalog's ID, and the IDs of all its contents,
107       * to a value that represents the unidentified state.
108       * <p>
109       * This method is useful for preparing a catalog for storage in a database.
110       * The ID property (as of now, though this may change in the future) is
111       * used by our persistence mechanism to identify objects.  If you are
112       * persisting this catalog for the first time, you may need to call
113       * this method before performing a save.  This is especially true if
114       * you have created this source from XML, as the XML unmarshalling
115       * brings along the ID property.</p> 
116       */
117      @Override
118      public void clearId()
119      {
120        super.clearId();
121        
122        for (Resource entry : getInternalItemList())
123          entry.clearId();
124      }
125    
126      //============================================================================
127      // INTERFACE UserAccountable & OWNERSHIP
128      //============================================================================
129      
130      /**
131       * Sets the ID of the user who owns this catalog.
132       * 
133       * @param userId the ID of the user who owns this catalog.  If this value is
134       *               <i>null</i> it will be replaced with
135       *               {@link UserAccountable#NULL_USER_ID}.
136       */
137      public void setOwner(Long userId)
138      {
139        owner = (userId == null) ? UserAccountable.NULL_USER_ID : userId;
140      }
141      
142      public void setCreatedBy(Long userId)
143      {
144        createdBy = (userId == null) ? UserAccountable.NULL_USER_ID : userId;
145      }
146      
147      public void setCreatedOn(Date d)
148      {
149        if (d != null)
150          createdOn = d;
151      }
152    
153      public void setLastUpdatedBy(Long userId)
154      {
155        lastUpdatedBy = (userId == null) ? UserAccountable.NULL_USER_ID : userId;
156      }
157    
158      public void setLastUpdatedOn(Date d)
159      {
160        if (d != null)
161          lastUpdatedOn = d;
162      }
163    
164      /**
165       * Returns the ID of the user who owns this catalog.
166       * @return the ID of the user who owns this catalog.
167       */
168      public Long getOwner()          { return owner;         }
169      public Long getCreatedBy()      { return createdBy;     }
170      public Date getCreatedOn()      { return createdOn;     }
171      public Long getLastUpdatedBy()  { return lastUpdatedBy; }
172      public Date getLastUpdatedOn()  { return lastUpdatedOn; }
173    
174      //============================================================================
175      // ResourceProvider Interface
176      //============================================================================
177    
178    
179      /**
180       * Returns the {@code Resource} with the given {@code id}, if any.
181       * <p>
182       * If this provider holds no {@code Resource} with an ID of {@code id},
183       * <i>null</i> is returned.</p>
184       * 
185       * @param id the identifier (primary key) for a {@code Resource} in this
186       *           repository.
187       *            
188       * @return The {@code Resource} with the given {@code id}, or
189       *         <i>null</i>, if this provider holds no such {@code Resource}.
190       * 
191       * @throws RepositoryException if anything goes wrong while trying to fetch
192       *                             resources from this provider.
193       */
194      public Resource findResourceById(long id)
195        throws RepositoryException
196      {
197        for (Resource r : getItems())
198        {
199          if (r.getId() == id)
200            return r;
201        }
202    
203        return null;
204      }
205    
206      /**
207       * Returns the {@code Resource}(s) with the given {@code name}, if any.
208       * <p>
209       * Ideally, the returned list will contain only one resource.  However,
210       * since the name is not usually used as a primary key to a resource, it
211       * is possible that the returned list may contain more than one resource.
212       * If this provider holds no {@code Resource} with a name of {@code name},
213       * the returned list will be empty.</p>
214       * 
215       * @param name the name of a {@code Resource} requested from this provider.
216       *            
217       * @return The {@code Resource}s with the given {@code name}, or
218       *         <i>null</i>, if this provider holds no such {@code Resource}.
219       * 
220       * @throws RepositoryException if anything goes wrong while trying to fetch
221       *                             resources from this provider.
222       */
223      public List<Resource> findResourceByName(String name)
224        throws RepositoryException
225      {
226        List<Resource> found = new ArrayList<Resource>();
227        if (name != null)
228        {
229          for (Resource r : getItems())
230          {
231            if (name.equals(r.getName()))
232              found.add(r);
233          }
234        }
235    
236        return found;
237      }
238      
239      /**
240       * Returns a list of all resources held by this provider.
241       * 
242       * @return a list of all resources held by this provider.  If the provider has
243       *         no resources the returned list will be empty.
244       * 
245       * @throws RepositoryException if anything goes wrong while trying to fetch
246       *                             resources from this provider.
247       */
248      public List<Resource> getAllResources()
249        throws RepositoryException
250      {
251        return getItems();
252      }
253      
254      //============================================================================
255      // TEXT
256      //============================================================================
257    
258      /**
259       * Creates a new catalog from the XML data in the given file.
260       * 
261       * @param xmlFile the name of an XML file.  This method will attempt to locate
262       *                the file by using {@link Class#getResource(String)}.
263       *                
264       * @return a new catalog from the XML data in the given file.
265       * 
266       * @throws FileNotFoundException if the XML file cannot be found.
267       * 
268       * @throws JAXBException if the schema file used (if any) is malformed, if
269       *           the XML file cannot be read, or if the XML file is not
270       *           schema-valid.
271       * 
272       * @throws XMLStreamException if there is a problem opening the XML file,
273       *           if the XML is not well-formed, or for some other
274       *           "unexpected processing conditions".
275       */
276      public static ResourceCatalog fromXml(String xmlFile)
277        throws JAXBException, XMLStreamException, FileNotFoundException
278      {
279        return JaxbUtility.getSharedInstance()
280                          .xmlFileToObject(xmlFile, ResourceCatalog.class);
281      }
282    
283      /**
284       * Creates a new catalog based on the XML data read from {@code reader}.
285       * 
286       * @param reader the source of the XML data.
287       *               If this value is <i>null</i>, <i>null</i> is returned.
288       *               
289       * @return a new catalog based on the XML data read from {@code reader}.
290       * 
291       * @throws XMLStreamException if the XML is not well-formed,
292       *           or for some other "unexpected processing conditions".
293       *           
294       * @throws JAXBException if anything else goes wrong during the
295       *           transformation.
296       */
297      public static ResourceCatalog fromXml(Reader reader)
298        throws JAXBException, XMLStreamException
299      {
300        return JaxbUtility.getSharedInstance()
301                          .readObjectAsXmlFrom(reader, ResourceCatalog.class, null);
302      }
303      
304      /**
305       * Returns an XML representation of this catalog.
306       * @return an XML representation of this catalog.
307       * @throws JAXBException if anything goes wrong during the conversion to XML.
308       */
309      @Override
310      public String toXml()
311        throws JAXBException
312      {
313        giveItemsCatalogIds();
314        
315        return super.toXml();
316      }
317      
318      /**
319       * Writes an XML representation of this catalog to {@code writer}.
320       * @param writer the device to which XML is written.
321       * @throws JAXBException if anything goes wrong during the conversion to XML.
322       */
323      @Override
324      public void writeAsXmlTo(Writer writer)
325        throws JAXBException
326      {
327        giveItemsCatalogIds();
328        
329        super.writeAsXmlTo(writer);
330      }
331      
332      /**
333       * Creates an ID for each resource in this catalog
334       * that is unique WITHIN this catalog.  This was done in order to be
335       * more user-friendly to people creating or manipulating the XML files
336       * by hand.  HOWEVER, it has the drawback that this does not create
337       * a universely unique ID.  Since our use case calls for the
338       * import and export of one catalog at a time, this is fine.
339       * If we want to be more general, though, we can leave the value of
340       * xmlId alone.  Resource's constructor gave this property a
341       * UNIVERSALLY unique ID.  We would then eliminate this method
342       * and the overrides of the XML methods above.
343       */
344      private void giveItemsCatalogIds()
345      {
346        HashMap<String, Integer> nameMap = new HashMap<String, Integer>();
347        
348        StringUtil util = StringUtil.getInstance();
349        
350        for (Resource r : getInternalItemList())
351        {
352          String name = util.toXmlNCName(r.getName(), '_');
353          int count = nameMap.containsKey(name) ? nameMap.get(name) + 1 : 1;
354          nameMap.put(name, count);
355          
356          r.xmlId = (count == 1) ? name : name + "_" + count;
357        }
358      }
359    
360      //----------------------------------------------------------------------------
361      // XML Helpers
362      //----------------------------------------------------------------------------
363      
364      //These methods are here solely to help JAXB do its thing.
365      
366      @XmlElementWrapper
367      @XmlElement(name="resource")
368      @SuppressWarnings("unused")
369      private List<Resource> getEntries()
370      {
371        return getInternalItemList();
372      }
373      
374      @SuppressWarnings("unused")
375      private void setEntries(List<Resource> replacementList)
376      {
377        setInternalItemList(replacementList);
378      }
379      
380      @XmlElementWrapper
381      @XmlElementRef(name="resourceGroup")
382      @SuppressWarnings("unused")
383      private List<ResourceGroup> getResourceGroups()
384      {
385        return getInternalGroupList();
386      }
387      
388      @SuppressWarnings("unused")
389      private void setResourceGroups(List<ResourceGroup> replacementList)
390      {
391        setInternalGroupList(replacementList);
392      }
393    
394      //============================================================================
395      // 
396      //============================================================================
397    
398      @Override
399      public ResourceCatalog clone()
400      {
401        ResourceCatalog clone = null;
402    
403        try {
404          clone = (ResourceCatalog)super.clone();
405          
406          clone.createdOn     = (Date)this.createdOn.clone();
407          clone.lastUpdatedOn = (Date)this.lastUpdatedOn.clone();
408        }
409        catch (Exception ex) {
410          throw new RuntimeException(ex);
411        }
412        
413        return clone;
414      }
415    
416      //============================================================================
417      // 
418      //============================================================================
419      /*
420      public static void main(String[] args)
421      {
422        ResourceBuilder builder = new ResourceBuilder();
423        builder.setIdentifiers(true);
424        ResourceCatalog catalog = builder.makeCatalog("Random");
425    
426        try
427        {
428          System.out.println(catalog.toXml());
429        }
430        catch (JAXBException ex)
431        {
432          System.out.println("Trouble w/ catalog.toXml.  Msg:");
433          System.out.println(ex.getMessage());
434          ex.printStackTrace();
435          
436          System.out.println("Attempting to write XML w/out schema verification:");
437          JaxbUtility.getSharedInstance().setLookForDefaultSchema(false);
438          try
439          {
440            System.out.println(catalog.toXml());
441          }
442          catch (JAXBException ex2)
443          {
444            System.out.println("Still had trouble w/ catalog.toXml.  Msg:");
445            System.out.println(ex.getMessage());
446            ex.printStackTrace();
447          }
448        }
449      }
450      */
451    }