KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > dspace > content > Item


1 /*
2  * Item.java
3  *
4  * Version: $Revision: 1.72 $
5  *
6  * Date: $Date: 2006/09/01 15:34:23 $
7  *
8  * Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
9  * Institute of Technology. All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions are
13  * met:
14  *
15  * - Redistributions of source code must retain the above copyright
16  * notice, this list of conditions and the following disclaimer.
17  *
18  * - Redistributions in binary form must reproduce the above copyright
19  * notice, this list of conditions and the following disclaimer in the
20  * documentation and/or other materials provided with the distribution.
21  *
22  * - Neither the name of the Hewlett-Packard Company nor the name of the
23  * Massachusetts Institute of Technology nor the names of their
24  * contributors may be used to endorse or promote products derived from
25  * this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
32  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
33  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
34  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
35  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
36  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
37  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
38  * DAMAGE.
39  */

40 package org.dspace.content;
41
42 import java.io.ByteArrayInputStream JavaDoc;
43 import java.io.IOException JavaDoc;
44 import java.io.InputStream JavaDoc;
45 import java.sql.SQLException JavaDoc;
46 import java.util.ArrayList JavaDoc;
47 import java.util.Date JavaDoc;
48 import java.util.HashMap JavaDoc;
49 import java.util.Iterator JavaDoc;
50 import java.util.List JavaDoc;
51 import java.util.ListIterator JavaDoc;
52 import java.util.Map JavaDoc;
53 import java.util.StringTokenizer JavaDoc;
54
55 import org.apache.log4j.Logger;
56 import org.dspace.authorize.AuthorizeException;
57 import org.dspace.authorize.AuthorizeManager;
58 import org.dspace.authorize.ResourcePolicy;
59 import org.dspace.browse.Browse;
60 import org.dspace.core.ConfigurationManager;
61 import org.dspace.core.Constants;
62 import org.dspace.core.Context;
63 import org.dspace.core.LogManager;
64 import org.dspace.eperson.EPerson;
65 import org.dspace.eperson.Group;
66 import org.dspace.handle.HandleManager;
67 import org.dspace.history.HistoryManager;
68 import org.dspace.search.DSIndexer;
69 import org.dspace.storage.rdbms.DatabaseManager;
70 import org.dspace.storage.rdbms.TableRow;
71 import org.dspace.storage.rdbms.TableRowIterator;
72
73 /**
74  * Class representing an item in DSpace.
75  * <P>
76  * This class holds in memory the item Dublin Core metadata, the bundles in the
77  * item, and the bitstreams in those bundles. When modifying the item, if you
78  * modify the Dublin Core or the "in archive" flag, you must call
79  * <code>update</code> for the changes to be written to the database.
80  * Creating, adding or removing bundles or bitstreams has immediate effect in
81  * the database.
82  *
83  * @author Robert Tansley
84  * @author Martin Hald
85  * @version $Revision: 1.72 $
86  */

87 public class Item extends DSpaceObject
88 {
89     /**
90      * Wild card for Dublin Core metadata qualifiers/languages
91      */

92     public final static String JavaDoc ANY = "*";
93
94     /** log4j category */
95     private static Logger log = Logger.getLogger(Item.class);
96
97     /** Our context */
98     private Context ourContext;
99
100     /** The table row corresponding to this item */
101     private TableRow itemRow;
102
103     /** The e-person who submitted this item */
104     private EPerson submitter;
105
106     /** The bundles in this item - kept in sync with DB */
107     private List JavaDoc bundles;
108
109     /** The Dublin Core metadata - a list of DCValue objects. */
110     private List JavaDoc dublinCore;
111
112     /** Handle, if any */
113     private String JavaDoc handle;
114
115     /**
116      * True if the Dublin Core has changed since reading from the DB or the last
117      * update()
118      */

119     private boolean dublinCoreChanged;
120
121     /**
122      * Construct an item with the given table row
123      *
124      * @param context
125      * the context this object exists in
126      * @param row
127      * the corresponding row in the table
128      * @throws SQLException
129      */

130     Item(Context context, TableRow row) throws SQLException JavaDoc
131     {
132         ourContext = context;
133         itemRow = row;
134         dublinCoreChanged = false;
135         dublinCore = new ArrayList JavaDoc();
136  
137         // Get Dublin Core metadata
138
TableRowIterator tri = DatabaseManager.queryTable(ourContext, "MetadataValue",
139                 "SELECT * FROM MetadataValue WHERE item_id= ? " +
140                 " ORDER BY metadata_field_id, place",
141                 itemRow.getIntColumn("item_id"));
142
143         while (tri.hasNext())
144         {
145             TableRow resultRow = tri.next();
146
147             // Get the associated metadata field and schema information
148
int fieldID = resultRow.getIntColumn("metadata_field_id");
149             MetadataField field = MetadataField.find(context, fieldID);
150
151             if (field == null)
152             {
153                 log.error("Loading item - cannot found metadata field "
154                         + fieldID);
155             }
156             else
157             {
158                 MetadataSchema schema = MetadataSchema.find(
159                         context, field.getSchemaID());
160
161             // Make a DCValue object
162
DCValue dcv = new DCValue();
163                 dcv.element = field.getElement();
164                 dcv.qualifier = field.getQualifier();
165             dcv.value = resultRow.getStringColumn("text_value");
166             dcv.language = resultRow.getStringColumn("text_lang");
167                 //dcv.namespace = schema.getNamespace();
168
dcv.schema = schema.getName();
169
170             // Add it to the list
171
dublinCore.add(dcv);
172         }
173         }
174         // close the TableRowIterator to free up resources
175
tri.close();
176
177         // Get our Handle if any
178
handle = HandleManager.findHandle(context, this);
179
180         // Cache ourselves
181
context.cache(this, row.getIntColumn("item_id"));
182     }
183
184     /**
185      * Get an item from the database. The item, its Dublin Core metadata, and
186      * the bundle and bitstream metadata are all loaded into memory.
187      *
188      * @param context
189      * DSpace context object
190      * @param id
191      * Internal ID of the item
192      * @return the item, or null if the internal ID is invalid.
193      * @throws SQLException
194      */

195     public static Item find(Context context, int id) throws SQLException JavaDoc
196     {
197         // First check the cache
198
Item fromCache = (Item) context.fromCache(Item.class, id);
199
200         if (fromCache != null)
201         {
202             return fromCache;
203         }
204
205         TableRow row = DatabaseManager.find(context, "item", id);
206
207         if (row == null)
208         {
209             if (log.isDebugEnabled())
210             {
211                 log.debug(LogManager.getHeader(context, "find_item",
212                         "not_found,item_id=" + id));
213             }
214
215             return null;
216         }
217
218         // not null, return item
219
if (log.isDebugEnabled())
220         {
221             log.debug(LogManager.getHeader(context, "find_item", "item_id="
222                     + id));
223         }
224
225         return new Item(context, row);
226     }
227
228     /**
229      * Create a new item, with a new internal ID. This method is not public,
230      * since items need to be created as workspace items. Authorisation is the
231      * responsibility of the caller.
232      *
233      * @param context
234      * DSpace context object
235      * @return the newly created item
236      * @throws SQLException
237      * @throws AuthorizeException
238      */

239     static Item create(Context context) throws SQLException JavaDoc, AuthorizeException
240     {
241         TableRow row = DatabaseManager.create(context, "item");
242         Item i = new Item(context, row);
243
244         // Call update to give the item a last modified date. OK this isn't
245
// amazingly efficient but creates don't happen that often.
246
context.setIgnoreAuthorization(true);
247         i.update();
248         context.setIgnoreAuthorization(false);
249
250         HistoryManager.saveHistory(context, i, HistoryManager.CREATE, context
251                 .getCurrentUser(), context.getExtraLogInfo());
252
253         log.info(LogManager.getHeader(context, "create_item", "item_id="
254                 + row.getIntColumn("item_id")));
255
256         return i;
257     }
258
259     /**
260      * Get all the items in the archive. Only items with the "in archive" flag
261      * set are included. The order of the list is indeterminate.
262      *
263      * @param context
264      * DSpace context object
265      * @return an iterator over the items in the archive.
266      * @throws SQLException
267      */

268     public static ItemIterator findAll(Context context) throws SQLException JavaDoc
269     {
270         String JavaDoc myQuery = "SELECT * FROM item WHERE in_archive='1'";
271
272         TableRowIterator rows = DatabaseManager.queryTable(context, "item", myQuery);
273
274         return new ItemIterator(context, rows);
275     }
276
277     /**
278      * Find all the items in the archive by a given submitter. The order is
279      * indeterminate. Only items with the "in archive" flag set are included.
280      *
281      * @param context
282      * DSpace context object
283      * @param eperson
284      * the submitter
285      * @return an iterator over the items submitted by eperson
286      * @throws SQLException
287      */

288     public static ItemIterator findBySubmitter(Context context, EPerson eperson)
289             throws SQLException JavaDoc
290     {
291         String JavaDoc myQuery = "SELECT * FROM item WHERE in_archive='1' AND submitter_id="
292                 + eperson.getID();
293
294         TableRowIterator rows = DatabaseManager.queryTable(context, "item", myQuery);
295
296         return new ItemIterator(context, rows);
297     }
298
299     /**
300      * Get the internal ID of this item. In general, this shouldn't be exposed
301      * to users
302      *
303      * @return the internal identifier
304      */

305     public int getID()
306     {
307         return itemRow.getIntColumn("item_id");
308     }
309
310     /**
311      * @see org.dspace.content.DSpaceObject#getHandle()
312      */

313     public String JavaDoc getHandle()
314     {
315         return handle;
316     }
317
318     /**
319      * Find out if the item is part of the main archive
320      *
321      * @return true if the item is in the main archive
322      */

323     public boolean isArchived()
324     {
325         return itemRow.getBooleanColumn("in_archive");
326     }
327
328     /**
329      * Find out if the item has been withdrawn
330      *
331      * @return true if the item has been withdrawn
332      */

333     public boolean isWithdrawn()
334     {
335         return itemRow.getBooleanColumn("withdrawn");
336     }
337
338     /**
339      * Get the date the item was last modified, or the current date if
340      * last_modified is null
341      *
342      * @return the date the item was last modified, or the current date if the
343      * column is null.
344      */

345     public Date JavaDoc getLastModified()
346     {
347         Date JavaDoc myDate = itemRow.getDateColumn("last_modified");
348
349         if (myDate == null)
350         {
351             myDate = new Date JavaDoc();
352         }
353
354         return myDate;
355     }
356
357     /**
358      * Set the "is_archived" flag. This is public and only
359      * <code>WorkflowItem.archive()</code> should set this.
360      *
361      * @param isArchived
362      * new value for the flag
363      */

364     public void setArchived(boolean isArchived)
365     {
366         itemRow.setColumn("in_archive", isArchived);
367     }
368
369     /**
370      * Set the owning Collection for the item
371      *
372      * @param c
373      * Collection
374      */

375     public void setOwningCollection(Collection c)
376     {
377         itemRow.setColumn("owning_collection", c.getID());
378     }
379
380     /**
381      * Get the owning Collection for the item
382      *
383      * @return Collection that is the owner of the item
384      * @throws SQLException
385      */

386     public Collection getOwningCollection() throws java.sql.SQLException JavaDoc
387     {
388         Collection myCollection = null;
389
390         // get the collection ID
391
int cid = itemRow.getIntColumn("owning_collection");
392
393         myCollection = Collection.find(ourContext, cid);
394
395         return myCollection;
396     }
397
398     /**
399      * Get Dublin Core metadata for the item.
400      * Passing in a <code>null</code> value for <code>qualifier</code>
401      * or <code>lang</code> only matches Dublin Core fields where that
402      * qualifier or languages is actually <code>null</code>.
403      * Passing in <code>Item.ANY</code>
404      * retrieves all metadata fields with any value for the qualifier or
405      * language, including <code>null</code>
406      * <P>
407      * Examples:
408      * <P>
409      * Return values of the unqualified "title" field, in any language.
410      * Qualified title fields (e.g. "title.uniform") are NOT returned:
411      * <P>
412      * <code>item.getDC( "title", null, Item.ANY );</code>
413      * <P>
414      * Return all US English values of the "title" element, with any qualifier
415      * (including unqualified):
416      * <P>
417      * <code>item.getDC( "title", Item.ANY, "en_US" );</code>
418      * <P>
419      * The ordering of values of a particular element/qualifier/language
420      * combination is significant. When retrieving with wildcards, values of a
421      * particular element/qualifier/language combinations will be adjacent, but
422      * the overall ordering of the combinations is indeterminate.
423      *
424      * @param element
425      * the Dublin Core element. <code>Item.ANY</code> matches any
426      * element. <code>null</code> doesn't really make sense as all
427      * DC must have an element.
428      * @param qualifier
429      * the qualifier. <code>null</code> means unqualified, and
430      * <code>Item.ANY</code> means any qualifier (including
431      * unqualified.)
432      * @param lang
433      * the ISO639 language code, optionally followed by an underscore
434      * and the ISO3166 country code. <code>null</code> means only
435      * values with no language are returned, and
436      * <code>Item.ANY</code> means values with any country code or
437      * no country code are returned.
438      * @return Dublin Core fields that match the parameters
439      */

440     public DCValue[] getDC(String JavaDoc element, String JavaDoc qualifier, String JavaDoc lang)
441     {
442         return getMetadata(MetadataSchema.DC_SCHEMA, element, qualifier, lang);
443     }
444
445     /**
446      * Get metadata for the item in a chosen schema.
447      * See <code>MetadataSchema</code> for more information about schemas.
448      * Passing in a <code>null</code> value for <code>qualifier</code>
449      * or <code>lang</code> only matches metadata fields where that
450      * qualifier or languages is actually <code>null</code>.
451      * Passing in <code>Item.ANY</code>
452      * retrieves all metadata fields with any value for the qualifier or
453      * language, including <code>null</code>
454      * <P>
455      * Examples:
456      * <P>
457      * Return values of the unqualified "title" field, in any language.
458      * Qualified title fields (e.g. "title.uniform") are NOT returned:
459      * <P>
460      * <code>item.getMetadata("dc", "title", null, Item.ANY );</code>
461      * <P>
462      * Return all US English values of the "title" element, with any qualifier
463      * (including unqualified):
464      * <P>
465      * <code>item.getMetadata("dc, "title", Item.ANY, "en_US" );</code>
466      * <P>
467      * The ordering of values of a particular element/qualifier/language
468      * combination is significant. When retrieving with wildcards, values of a
469      * particular element/qualifier/language combinations will be adjacent, but
470      * the overall ordering of the combinations is indeterminate.
471      *
472      * @param schema
473      * the schema for the metadata field. <em>Must</em> match
474      * the <code>name</code> of an existing metadata schema.
475      * @param element
476      * the element name. <code>Item.ANY</code> matches any
477      * element. <code>null</code> doesn't really make sense as all
478      * metadata must have an element.
479      * @param qualifier
480      * the qualifier. <code>null</code> means unqualified, and
481      * <code>Item.ANY</code> means any qualifier (including
482      * unqualified.)
483      * @param lang
484      * the ISO639 language code, optionally followed by an underscore
485      * and the ISO3166 country code. <code>null</code> means only
486      * values with no language are returned, and
487      * <code>Item.ANY</code> means values with any country code or
488      * no country code are returned.
489      * @return metadata fields that match the parameters
490      */

491     public DCValue[] getMetadata(String JavaDoc schema, String JavaDoc element, String JavaDoc qualifier,
492             String JavaDoc lang)
493     {
494         // Build up list of matching values
495
List JavaDoc values = new ArrayList JavaDoc();
496         Iterator JavaDoc i = dublinCore.iterator();
497
498         while (i.hasNext())
499         {
500             DCValue dcv = (DCValue) i.next();
501
502             if (match(schema, element, qualifier, lang, dcv))
503             {
504                 // We will return a copy of the object in case it is altered
505
DCValue copy = new DCValue();
506                 copy.element = dcv.element;
507                 copy.qualifier = dcv.qualifier;
508                 copy.value = dcv.value;
509                 copy.language = dcv.language;
510                 copy.schema = dcv.schema;
511
512                 values.add(copy);
513             }
514         }
515
516         // Create an array of matching values
517
DCValue[] valueArray = new DCValue[values.size()];
518         valueArray = (DCValue[]) values.toArray(valueArray);
519
520         return valueArray;
521     }
522     
523     /**
524      * Retrieve metadata field values from a given metadata string
525      * of the form <schema prefix>.<element>[.<qualifier>|.*]
526      *
527      * @param mdString
528      * The metadata string of the form
529      * <schema prefix>.<element>[.<qualifier>|.*]
530      */

531     public DCValue[] getMetadata(String JavaDoc mdString)
532     {
533         StringTokenizer JavaDoc dcf = new StringTokenizer JavaDoc(mdString, ".");
534         
535         String JavaDoc[] tokens = { "", "", "" };
536         int i = 0;
537         while(dcf.hasMoreTokens())
538         {
539             tokens[i] = dcf.nextToken().toLowerCase().trim();
540             i++;
541         }
542         String JavaDoc schema = tokens[0];
543         String JavaDoc element = tokens[1];
544         String JavaDoc qualifier = tokens[2];
545         
546         DCValue[] values;
547         if ("*".equals(qualifier))
548         {
549             values = getMetadata(schema, element, Item.ANY, Item.ANY);
550         }
551         else if ("".equals(qualifier))
552         {
553             values = getMetadata(schema, element, null, Item.ANY);
554         }
555         else
556         {
557             values = getMetadata(schema, element, qualifier, Item.ANY);
558         }
559         
560         return values;
561     }
562
563     /**
564      * Add Dublin Core metadata fields. These are appended to existing values.
565      * Use <code>clearDC</code> to remove values. The ordering of values
566      * passed in is maintained.
567      *
568      * @param element
569      * the Dublin Core element
570      * @param qualifier
571      * the Dublin Core qualifer, or <code>null</code> for
572      * unqualified
573      * @param lang
574      * the ISO639 language code, optionally followed by an underscore
575      * and the ISO3166 country code. <code>null</code> means the
576      * value has no language (for example, a date).
577      * @param values
578      * the values to add.
579      */

580     public void addDC(String JavaDoc element, String JavaDoc qualifier, String JavaDoc lang,
581             String JavaDoc[] values)
582     {
583         addMetadata(MetadataSchema.DC_SCHEMA, element, qualifier, lang, values);
584     }
585
586     /**
587      * Add a single Dublin Core metadata field. This is appended to existing
588      * values. Use <code>clearDC</code> to remove values.
589      *
590      * @param element
591      * the Dublin Core element
592      * @param qualifier
593      * the Dublin Core qualifer, or <code>null</code> for
594      * unqualified
595      * @param lang
596      * the ISO639 language code, optionally followed by an underscore
597      * and the ISO3166 country code. <code>null</code> means the
598      * value has no language (for example, a date).
599      * @param value
600      * the value to add.
601      */

602     public void addDC(String JavaDoc element, String JavaDoc qualifier, String JavaDoc lang,
603             String JavaDoc value)
604     {
605         addMetadata(MetadataSchema.DC_SCHEMA, element, qualifier, lang, value);
606     }
607     
608     /**
609      * Add metadata fields. These are appended to existing values.
610      * Use <code>clearDC</code> to remove values. The ordering of values
611      * passed in is maintained.
612      * @param schema
613      * the schema for the metadata field. <em>Must</em> match
614      * the <code>name</code> of an existing metadata schema.
615      * @param element
616      * the metadata element name
617      * @param qualifier
618      * the metadata qualifer name, or <code>null</code> for
619      * unqualified
620      * @param lang
621      * the ISO639 language code, optionally followed by an underscore
622      * and the ISO3166 country code. <code>null</code> means the
623      * value has no language (for example, a date).
624      * @param values
625      * the values to add.
626      */

627     public void addMetadata(String JavaDoc schema, String JavaDoc element, String JavaDoc qualifier, String JavaDoc lang,
628             String JavaDoc[] values)
629     {
630         // We will not verify that they are valid entries in the registry
631
// until update() is called.
632
for (int i = 0; i < values.length; i++)
633         {
634             DCValue dcv = new DCValue();
635             dcv.schema = schema;
636             dcv.element = element;
637             dcv.qualifier = qualifier;
638             dcv.language = lang;
639             dcv.value = (values[i] == null ? null : values[i].trim());
640             dublinCore.add(dcv);
641         }
642
643         if (values.length > 0)
644         {
645             dublinCoreChanged = true;
646         }
647     }
648
649     /**
650      * Add a single metadata field. This is appended to existing
651      * values. Use <code>clearDC</code> to remove values.
652      *
653      * @param schema
654      * the schema for the metadata field. <em>Must</em> match
655      * the <code>name</code> of an existing metadata schema.
656      * @param element
657      * the metadata element name
658      * @param qualifier
659      * the metadata qualifer, or <code>null</code> for
660      * unqualified
661      * @param lang
662      * the ISO639 language code, optionally followed by an underscore
663      * and the ISO3166 country code. <code>null</code> means the
664      * value has no language (for example, a date).
665      * @param value
666      * the value to add.
667      */

668     public void addMetadata(String JavaDoc schema, String JavaDoc element, String JavaDoc qualifier,
669             String JavaDoc lang, String JavaDoc value)
670     {
671         String JavaDoc[] valArray = new String JavaDoc[1];
672         valArray[0] = value;
673
674         addMetadata(schema, element, qualifier, lang, valArray);
675     }
676
677     /**
678      * Clear Dublin Core metadata values. As with <code>getDC</code> above,
679      * passing in <code>null</code> only matches fields where the qualifier or
680      * language is actually <code>null</code>.<code>Item.ANY</code> will
681      * match any element, qualifier or language, including <code>null</code>.
682      * Thus, <code>item.clearDC(Item.ANY, Item.ANY, Item.ANY)</code> will
683      * remove all Dublin Core metadata associated with an item.
684      *
685      * @param element
686      * the Dublin Core element to remove, or <code>Item.ANY</code>
687      * @param qualifier
688      * the qualifier. <code>null</code> means unqualified, and
689      * <code>Item.ANY</code> means any qualifier (including
690      * unqualified.)
691      * @param lang
692      * the ISO639 language code, optionally followed by an underscore
693      * and the ISO3166 country code. <code>null</code> means only
694      * values with no language are removed, and <code>Item.ANY</code>
695      * means values with any country code or no country code are
696      * removed.
697      */

698     public void clearDC(String JavaDoc element, String JavaDoc qualifier, String JavaDoc lang)
699     {
700         clearMetadata(MetadataSchema.DC_SCHEMA, element, qualifier, lang);
701     }
702
703     /**
704      * Clear metadata values. As with <code>getDC</code> above,
705      * passing in <code>null</code> only matches fields where the qualifier or
706      * language is actually <code>null</code>.<code>Item.ANY</code> will
707      * match any element, qualifier or language, including <code>null</code>.
708      * Thus, <code>item.clearDC(Item.ANY, Item.ANY, Item.ANY)</code> will
709      * remove all Dublin Core metadata associated with an item.
710      *
711      * @param schema
712      * the schema for the metadata field. <em>Must</em> match
713      * the <code>name</code> of an existing metadata schema.
714      * @param element
715      * the Dublin Core element to remove, or <code>Item.ANY</code>
716      * @param qualifier
717      * the qualifier. <code>null</code> means unqualified, and
718      * <code>Item.ANY</code> means any qualifier (including
719      * unqualified.)
720      * @param lang
721      * the ISO639 language code, optionally followed by an underscore
722      * and the ISO3166 country code. <code>null</code> means only
723      * values with no language are removed, and <code>Item.ANY</code>
724      * means values with any country code or no country code are
725      * removed.
726      */

727     public void clearMetadata(String JavaDoc schema, String JavaDoc element, String JavaDoc qualifier,
728             String JavaDoc lang)
729     {
730         // We will build a list of values NOT matching the values to clear
731
List JavaDoc values = new ArrayList JavaDoc();
732         Iterator JavaDoc i = dublinCore.iterator();
733
734         while (i.hasNext())
735         {
736             DCValue dcv = (DCValue) i.next();
737
738             if (!match(schema, element, qualifier, lang, dcv))
739             {
740                 values.add(dcv);
741             }
742         }
743
744         // Now swap the old list of values for the new, unremoved values
745
dublinCore = values;
746         dublinCoreChanged = true;
747     }
748
749     /**
750      * Utility method for pattern-matching metadata elements. This
751      * method will return <code>true</code> if the given schema,
752      * element, qualifier and language match the schema, element,
753      * qualifier and language of the <code>DCValue</code> object passed
754      * in. Any or all of the elemenent, qualifier and language passed
755      * in can be the <code>Item.ANY</code> wildcard.
756      *
757      * @param schema
758      * the schema for the metadata field. <em>Must</em> match
759      * the <code>name</code> of an existing metadata schema.
760      * @param element
761      * the element to match, or <code>Item.ANY</code>
762      * @param qualifier
763      * the qualifier to match, or <code>Item.ANY</code>
764      * @param language
765      * the language to match, or <code>Item.ANY</code>
766      * @param dcv
767      * the Dublin Core value
768      * @return <code>true</code> if there is a match
769      */

770     private boolean match(String JavaDoc schema, String JavaDoc element, String JavaDoc qualifier,
771             String JavaDoc language, DCValue dcv)
772     {
773         // We will attempt to disprove a match - if we can't we have a match
774
if (!element.equals(Item.ANY) && !element.equals(dcv.element))
775         {
776             // Elements do not match, no wildcard
777
return false;
778         }
779
780         if (qualifier == null)
781         {
782             // Value must be unqualified
783
if (dcv.qualifier != null)
784             {
785                 // Value is qualified, so no match
786
return false;
787             }
788         }
789         else if (!qualifier.equals(Item.ANY))
790         {
791             // Not a wildcard, so qualifier must match exactly
792
if (!qualifier.equals(dcv.qualifier))
793             {
794                 return false;
795             }
796         }
797
798         if (language == null)
799         {
800             // Value must be null language to match
801
if (dcv.language != null)
802             {
803                 // Value is qualified, so no match
804
return false;
805             }
806         }
807         else if (!language.equals(Item.ANY))
808         {
809             // Not a wildcard, so language must match exactly
810
if (!language.equals(dcv.language))
811             {
812                 return false;
813             }
814         }
815         else if (!schema.equals(Item.ANY))
816         {
817             if (dcv.schema != null && !dcv.schema.equals(schema))
818             {
819                 // The namespace doesn't match
820
return false;
821             }
822         }
823
824         // If we get this far, we have a match
825
return true;
826     }
827
828     /**
829      * Get the e-person that originally submitted this item
830      *
831      * @return the submitter
832      */

833     public EPerson getSubmitter() throws SQLException JavaDoc
834     {
835         if (submitter == null && !itemRow.isColumnNull("submitter_id"))
836         {
837             submitter = EPerson.find(ourContext, itemRow
838                     .getIntColumn("submitter_id"));
839         }
840         return submitter;
841     }
842
843     /**
844      * Set the e-person that originally submitted this item. This is a public
845      * method since it is handled by the WorkspaceItem class in the ingest
846      * package. <code>update</code> must be called to write the change to the
847      * database.
848      *
849      * @param sub
850      * the submitter
851      */

852     public void setSubmitter(EPerson sub)
853     {
854         submitter = sub;
855
856         if (submitter != null)
857         {
858             itemRow.setColumn("submitter_id", submitter.getID());
859         }
860         else
861         {
862             itemRow.setColumnNull("submitter_id");
863         }
864     }
865
866     /**
867      * Get the collections this item is in. The order is indeterminate.
868      *
869      * @return the collections this item is in, if any.
870      * @throws SQLException
871      */

872     public Collection[] getCollections() throws SQLException JavaDoc
873     {
874         List JavaDoc collections = new ArrayList JavaDoc();
875
876         // Get collection table rows
877
TableRowIterator tri = DatabaseManager.queryTable(ourContext,"collection",
878                         "SELECT collection.* FROM collection, collection2item WHERE " +
879                         "collection2item.collection_id=collection.collection_id AND " +
880                         "collection2item.item_id= ? ",
881                         itemRow.getIntColumn("item_id"));
882
883         while (tri.hasNext())
884         {
885             TableRow row = tri.next();
886
887             // First check the cache
888
Collection fromCache = (Collection) ourContext.fromCache(
889                     Collection.class, row.getIntColumn("collection_id"));
890
891             if (fromCache != null)
892             {
893                 collections.add(fromCache);
894             }
895             else
896             {
897                 collections.add(new Collection(ourContext, row));
898             }
899         }
900         // close the TableRowIterator to free up resources
901
tri.close();
902
903         Collection[] collectionArray = new Collection[collections.size()];
904         collectionArray = (Collection[]) collections.toArray(collectionArray);
905
906         return collectionArray;
907     }
908
909     /**
910      * Get the communities this item is in. Returns an unordered array of the
911      * communities that house the collections this item is in, including parent
912      * communities of the owning collections.
913      *
914      * @return the communities this item is in.
915      * @throws SQLException
916      */

917     public Community[] getCommunities() throws SQLException JavaDoc
918     {
919         List JavaDoc communities = new ArrayList JavaDoc();
920
921         // Get community table rows
922
TableRowIterator tri = DatabaseManager.queryTable(ourContext,"community",
923                         "SELECT community.* FROM community, community2item " +
924                         "WHERE community2item.community_id=community.community_id " +
925                         "AND community2item.item_id= ? ",
926                         itemRow.getIntColumn("item_id"));
927
928         while (tri.hasNext())
929         {
930             TableRow row = tri.next();
931
932             // First check the cache
933
Community owner = (Community) ourContext.fromCache(Community.class,
934                     row.getIntColumn("community_id"));
935
936             if (owner == null)
937             {
938                 owner = new Community(ourContext, row);
939             }
940
941             communities.add(owner);
942
943             // now add any parent communities
944
Community[] parents = owner.getAllParents();
945
946             for (int i = 0; i < parents.length; i++)
947             {
948                 communities.add(parents[i]);
949             }
950         }
951         // close the TableRowIterator to free up resources
952
tri.close();
953
954         Community[] communityArray = new Community[communities.size()];
955         communityArray = (Community[]) communities.toArray(communityArray);
956
957         return communityArray;
958     }
959
960     /**
961      * Get the bundles in this item.
962      *
963      * @return the bundles in an unordered array
964      */

965     public Bundle[] getBundles() throws SQLException JavaDoc
966     {
967         if (bundles == null)
968         {
969             bundles = new ArrayList JavaDoc();
970             // Get bundles
971
TableRowIterator tri = DatabaseManager.queryTable(ourContext, "bundle",
972                         "SELECT bundle.* FROM bundle, item2bundle WHERE " +
973                         "item2bundle.bundle_id=bundle.bundle_id AND " +
974                         "item2bundle.item_id= ? ",
975                         itemRow.getIntColumn("item_id"));
976
977             while (tri.hasNext())
978             {
979                 TableRow r = tri.next();
980
981                 // First check the cache
982
Bundle fromCache = (Bundle) ourContext.fromCache(Bundle.class,
983                                             r.getIntColumn("bundle_id"));
984
985                 if (fromCache != null)
986                 {
987                     bundles.add(fromCache);
988                 }
989                 else
990                 {
991                     bundles.add(new Bundle(ourContext, r));
992                 }
993             }
994             // close the TableRowIterator to free up resources
995
tri.close();
996         }
997         
998         Bundle[] bundleArray = new Bundle[bundles.size()];
999         bundleArray = (Bundle[]) bundles.toArray(bundleArray);
1000
1001        return bundleArray;
1002    }
1003
1004    /**
1005     * Get the bundles matching a bundle name (name corresponds roughly to type)
1006     *
1007     * @param name
1008     * name of bundle (ORIGINAL/TEXT/THUMBNAIL)
1009     *
1010     * @return the bundles in an unordered array
1011     */

1012    public Bundle[] getBundles(String JavaDoc name) throws SQLException JavaDoc
1013    {
1014        List JavaDoc matchingBundles = new ArrayList JavaDoc();
1015
1016        // now only keep bundles with matching names
1017
Bundle[] bunds = getBundles();
1018        for (int i = 0; i < bunds.length; i++ )
1019        {
1020            if (name.equals(bunds[i].getName()))
1021            {
1022                matchingBundles.add(bunds[i]);
1023            }
1024        }
1025
1026        Bundle[] bundleArray = new Bundle[matchingBundles.size()];
1027        bundleArray = (Bundle[]) matchingBundles.toArray(bundleArray);
1028
1029        return bundleArray;
1030    }
1031
1032    /**
1033     * Create a bundle in this item, with immediate effect
1034     *
1035     * @param name
1036     * bundle name (ORIGINAL/TEXT/THUMBNAIL)
1037     * @return the newly created bundle
1038     * @throws SQLException
1039     * @throws AuthorizeException
1040     */

1041    public Bundle createBundle(String JavaDoc name) throws SQLException JavaDoc,
1042            AuthorizeException
1043    {
1044        if ((name == null) || "".equals(name))
1045        {
1046            throw new SQLException JavaDoc("Bundle must be created with non-null name");
1047        }
1048
1049        // Check authorisation
1050
AuthorizeManager.authorizeAction(ourContext, this, Constants.ADD);
1051
1052        Bundle b = Bundle.create(ourContext);
1053        b.setName(name);
1054        b.update();
1055
1056        addBundle(b);
1057
1058        return b;
1059    }
1060
1061    /**
1062     * Add an existing bundle to this item. This has immediate effect.
1063     *
1064     * @param b
1065     * the bundle to add
1066     * @throws SQLException
1067     * @throws AuthorizeException
1068     */

1069    public void addBundle(Bundle b) throws SQLException JavaDoc, AuthorizeException
1070    {
1071        // Check authorisation
1072
AuthorizeManager.authorizeAction(ourContext, this, Constants.ADD);
1073
1074        log.info(LogManager.getHeader(ourContext, "add_bundle", "item_id="
1075                + getID() + ",bundle_id=" + b.getID()));
1076
1077        // Check it's not already there
1078
Bundle[] bunds = getBundles();
1079        for (int i = 0; i < bunds.length; i++)
1080        {
1081            if (b.getID() == bunds[i].getID())
1082            {
1083                // Bundle is already there; no change
1084
return;
1085            }
1086        }
1087
1088        // now add authorization policies from owning item
1089
// hmm, not very "multiple-inclusion" friendly
1090
AuthorizeManager.inheritPolicies(ourContext, this, b);
1091
1092        // Add the bundle to in-memory list
1093
bundles.add(b);
1094
1095        // Insert the mapping
1096
TableRow mappingRow = DatabaseManager.create(ourContext, "item2bundle");
1097        mappingRow.setColumn("item_id", getID());
1098        mappingRow.setColumn("bundle_id", b.getID());
1099        DatabaseManager.update(ourContext, mappingRow);
1100    }
1101
1102    /**
1103     * Remove a bundle. This may result in the bundle being deleted, if the
1104     * bundle is orphaned.
1105     *
1106     * @param b
1107     * the bundle to remove
1108     * @throws SQLException
1109     * @throws AuthorizeException
1110     * @throws IOException
1111     */

1112    public void removeBundle(Bundle b) throws SQLException JavaDoc, AuthorizeException,
1113            IOException JavaDoc
1114    {
1115        // Check authorisation
1116
AuthorizeManager.authorizeAction(ourContext, this, Constants.REMOVE);
1117
1118        log.info(LogManager.getHeader(ourContext, "remove_bundle", "item_id="
1119                + getID() + ",bundle_id=" + b.getID()));
1120
1121        // Remove from internal list of bundles
1122
Bundle[] bunds = getBundles();
1123        
1124        for (int i = 0; i < bunds.length; i++)
1125        {
1126            if (b.getID() == bunds[i].getID())
1127            {
1128                // We've found the bundle to remove
1129
bundles.remove(bunds[i]);
1130                break;
1131            }
1132        }
1133
1134        // Remove mapping from DB
1135
DatabaseManager.updateQuery(ourContext,
1136                "DELETE FROM item2bundle WHERE item_id= ? " +
1137                "AND bundle_id= ? ",
1138                getID(), b.getID());
1139
1140        // If the bundle is orphaned, it's removed
1141
TableRowIterator tri = DatabaseManager.query(ourContext,
1142                "SELECT * FROM item2bundle WHERE bundle_id= ? ",
1143                b.getID());
1144
1145        if (!tri.hasNext())
1146        {
1147            //make the right to remove the bundle explicit because the implicit
1148
// relation
1149
//has been removed. This only has to concern the currentUser
1150
// because
1151
//he started the removal process and he will end it too.
1152
//also add right to remove from the bundle to remove it's
1153
// bitstreams.
1154
AuthorizeManager.addPolicy(ourContext, b, Constants.DELETE,
1155                    ourContext.getCurrentUser());
1156            AuthorizeManager.addPolicy(ourContext, b, Constants.REMOVE,
1157                    ourContext.getCurrentUser());
1158
1159            // The bundle is an orphan, delete it
1160
b.delete();
1161        }
1162        // close the TableRowIterator to free up resources
1163
tri.close();
1164    }
1165
1166    /**
1167     * Create a single bitstream in a new bundle. Provided as a convenience
1168     * method for the most common use.
1169     *
1170     * @param is
1171     * the stream to create the new bitstream from
1172     * @param name
1173     * is the name of the bundle (ORIGINAL, TEXT, THUMBNAIL)
1174     * @return Bitstream that is created
1175     * @throws AuthorizeException
1176     * @throws IOException
1177     * @throws SQLException
1178     */

1179    public Bitstream createSingleBitstream(InputStream JavaDoc is, String JavaDoc name)
1180            throws AuthorizeException, IOException JavaDoc, SQLException JavaDoc
1181    {
1182        // Authorisation is checked by methods below
1183
// Create a bundle
1184
Bundle bnd = createBundle(name);
1185        Bitstream bitstream = bnd.createBitstream(is);
1186        addBundle(bnd);
1187
1188        // FIXME: Create permissions for new bundle + bitstream
1189
return bitstream;
1190    }
1191
1192    /**
1193     * Convenience method, calls createSingleBitstream() with name "ORIGINAL"
1194     *
1195     * @param is
1196     * InputStream
1197     * @return created bitstream
1198     * @throws AuthorizeException
1199     * @throws IOException
1200     * @throws SQLException
1201     */

1202    public Bitstream createSingleBitstream(InputStream JavaDoc is)
1203            throws AuthorizeException, IOException JavaDoc, SQLException JavaDoc
1204    {
1205        return createSingleBitstream(is, "ORIGINAL");
1206    }
1207
1208    /**
1209     * Get all non-internal bitstreams in the item. This is mainly used for
1210     * auditing for provenance messages and adding format.* DC values. The order
1211     * is indeterminate.
1212     *
1213     * @return non-internal bitstreams.
1214     */

1215    public Bitstream[] getNonInternalBitstreams() throws SQLException JavaDoc
1216    {
1217        List JavaDoc bitstreamList = new ArrayList JavaDoc();
1218
1219        // Go through the bundles and bitstreams picking out ones which aren't
1220
// of internal formats
1221
Bundle[] bunds = getBundles();
1222
1223        for (int i = 0; i < bunds.length; i++)
1224        {
1225            Bitstream[] bitstreams = bunds[i].getBitstreams();
1226
1227            for (int j = 0; j < bitstreams.length; j++)
1228            {
1229                if (!bitstreams[j].getFormat().isInternal())
1230                {
1231                    // Bitstream is not of an internal format
1232
bitstreamList.add(bitstreams[j]);
1233                }
1234            }
1235        }
1236
1237        Bitstream[] bsArray = new Bitstream[bitstreamList.size()];
1238        bsArray = (Bitstream[]) bitstreamList.toArray(bsArray);
1239
1240        return bsArray;
1241    }
1242
1243    /**
1244     * Store a copy of the license a user granted in this item.
1245     *
1246     * @param license
1247     * the license the user granted
1248     * @param eperson
1249     * the eperson who granted the license
1250     * @throws SQLException
1251     * @throws IOException
1252     * @throws AuthorizeException
1253     */

1254    public void licenseGranted(String JavaDoc license, EPerson eperson)
1255            throws SQLException JavaDoc, IOException JavaDoc, AuthorizeException
1256    {
1257        // Put together text to store
1258
String JavaDoc licenseText = "License granted by " + eperson.getFullName()
1259                + " (" + eperson.getEmail() + ") on "
1260                + DCDate.getCurrent().toString() + " (GMT):\n\n" + license;
1261
1262        // Store text as a bitstream
1263
byte[] licenseBytes = licenseText.getBytes();
1264        ByteArrayInputStream JavaDoc bais = new ByteArrayInputStream JavaDoc(licenseBytes);
1265        Bitstream b = createSingleBitstream(bais, "LICENSE");
1266
1267        // Now set the format and name of the bitstream
1268
b.setName("license.txt");
1269        b.setSource("Written by org.dspace.content.Item");
1270
1271        // Find the License format
1272
BitstreamFormat bf = BitstreamFormat.findByShortDescription(ourContext,
1273                "License");
1274        b.setFormat(bf);
1275
1276        b.update();
1277    }
1278
1279    /**
1280     * Remove all licenses from an item - it was rejected
1281     *
1282     * @throws SQLException
1283     * @throws AuthorizeException
1284     * @throws IOException
1285     */

1286    public void removeLicenses() throws SQLException JavaDoc, AuthorizeException,
1287            IOException JavaDoc
1288    {
1289        // Find the License format
1290
BitstreamFormat bf = BitstreamFormat.findByShortDescription(ourContext,
1291                "License");
1292        int licensetype = bf.getID();
1293
1294        // search through bundles, looking for bitstream type license
1295
Bundle[] bunds = getBundles();
1296
1297        for (int i = 0; i < bunds.length; i++)
1298        {
1299            boolean removethisbundle = false;
1300
1301            Bitstream[] bits = bunds[i].getBitstreams();
1302
1303            for (int j = 0; j < bits.length; j++)
1304            {
1305                BitstreamFormat bft = bits[j].getFormat();
1306
1307                if (bft.getID() == licensetype)
1308                {
1309                    removethisbundle = true;
1310                }
1311            }
1312
1313            // probably serious troubles with Authorizations
1314
// fix by telling system not to check authorization?
1315
if (removethisbundle)
1316            {
1317                removeBundle(bunds[i]);
1318            }
1319        }
1320    }
1321
1322    /**
1323     * Update the item "in archive" flag and Dublin Core metadata in the
1324     * database
1325     *
1326     * @throws SQLException
1327     * @throws AuthorizeException
1328     */

1329    public void update() throws SQLException JavaDoc, AuthorizeException
1330    {
1331        // Check authorisation
1332
// only do write authorization if user is not an editor
1333
if (!canEdit())
1334        {
1335            AuthorizeManager.authorizeAction(ourContext, this, Constants.WRITE);
1336        }
1337
1338        HistoryManager.saveHistory(ourContext, this, HistoryManager.MODIFY,
1339                ourContext.getCurrentUser(), ourContext.getExtraLogInfo());
1340
1341        log.info(LogManager.getHeader(ourContext, "update_item", "item_id="
1342                + getID()));
1343
1344        // Set the last modified date
1345
itemRow.setColumn("last_modified", new Date JavaDoc());
1346
1347        // Set sequence IDs for bitstreams in item
1348
int sequence = 0;
1349        Bundle[] bunds = getBundles();
1350
1351        // find the highest current sequence number
1352
for (int i = 0; i < bunds.length; i++)
1353        {
1354            Bitstream[] streams = bunds[i].getBitstreams();
1355
1356            for (int k = 0; k < streams.length; k++)
1357            {
1358                if (streams[k].getSequenceID() > sequence)
1359                {
1360                    sequence = streams[k].getSequenceID();
1361                }
1362            }
1363        }
1364
1365        // start sequencing bitstreams without sequence IDs
1366
sequence++;
1367
1368        for (int i = 0; i < bunds.length; i++)
1369        {
1370            Bitstream[] streams = bunds[i].getBitstreams();
1371
1372            for (int k = 0; k < streams.length; k++)
1373            {
1374                if (streams[k].getSequenceID() < 0)
1375                {
1376                    streams[k].setSequenceID(sequence);
1377                    sequence++;
1378                    streams[k].update();
1379                }
1380            }
1381        }
1382
1383        // Make sure that withdrawn and in_archive are non-null
1384
if (itemRow.isColumnNull("in_archive"))
1385        {
1386            itemRow.setColumn("in_archive", false);
1387        }
1388
1389        if (itemRow.isColumnNull("withdrawn"))
1390        {
1391            itemRow.setColumn("withdrawn", false);
1392        }
1393
1394        // Map counting number of values for each element/qualifier.
1395
// Keys are Strings: "element" or "element.qualifier"
1396
// Values are Integers indicating number of values written for a
1397
// element/qualifier
1398
Map JavaDoc elementCount = new HashMap JavaDoc();
1399
1400        DatabaseManager.update(ourContext, itemRow);
1401
1402        // Redo Dublin Core if it's changed
1403
if (dublinCoreChanged)
1404        {
1405            // Remove existing DC
1406
removeMetadataFromDatabase();
1407
1408            // Add in-memory DC
1409
Iterator JavaDoc i = dublinCore.iterator();
1410
1411            while (i.hasNext())
1412            {
1413                DCValue dcv = (DCValue) i.next();
1414
1415                // Get the DC Type
1416
int schemaID;
1417                MetadataSchema schema = MetadataSchema.find(ourContext,dcv.schema);
1418                if (schema == null) {
1419                    schemaID = MetadataSchema.DC_SCHEMA_ID;
1420                } else {
1421                    schemaID = schema.getSchemaID();
1422                }
1423
1424                MetadataField field = MetadataField.findByElement(ourContext,
1425                        schemaID, dcv.element, dcv.qualifier);
1426
1427                if (field == null)
1428                {
1429                    // Bad DC field, log and throw exception
1430
log.warn(LogManager
1431                            .getHeader(ourContext, "bad_dc",
1432                                    "Bad DC field. SchemaID="+String.valueOf(schemaID)
1433                                            + ", element: \""
1434                                            + ((dcv.element == null) ? "null"
1435                                                    : dcv.element)
1436                                            + "\" qualifier: \""
1437                                            + ((dcv.qualifier == null) ? "null"
1438                                                    : dcv.qualifier)
1439                                            + "\" value: \""
1440                                            + ((dcv.value == null) ? "null"
1441                                                    : dcv.value) + "\""));
1442
1443                    throw new SQLException JavaDoc("bad_dublin_core "
1444                            + "SchemaID="+String.valueOf(schemaID)+", "
1445                            + dcv.element
1446                            + " " + dcv.qualifier);
1447                }
1448
1449                // Work out the place number for ordering
1450
int current = 0;
1451
1452                // Key into map is "element" or "element.qualifier"
1453
String JavaDoc key = dcv.element
1454                        + ((dcv.qualifier == null) ? "" : ("." + dcv.qualifier));
1455
1456                Integer JavaDoc currentInteger = (Integer JavaDoc) elementCount.get(key);
1457
1458                if (currentInteger != null)
1459                {
1460                    current = currentInteger.intValue();
1461                }
1462
1463                current++;
1464                elementCount.put(key, new Integer JavaDoc(current));
1465
1466                // Write DCValue
1467
MetadataValue metadata = new MetadataValue();
1468                metadata.setItemId(getID());
1469                metadata.setFieldId(field.getFieldID());
1470                metadata.setValue(dcv.value);
1471                metadata.setLanguage(dcv.language);
1472                metadata.setPlace(current);
1473                metadata.create(ourContext);
1474            }
1475
1476            dublinCoreChanged = false;
1477        }
1478
1479        // Update browse indices
1480
Browse.itemChanged(ourContext, this);
1481    }
1482
1483    /**
1484     * Withdraw the item from the archive. It is kept in place, and the content
1485     * and metadata are not deleted, but it is not publicly accessible.
1486     *
1487     * @throws SQLException
1488     * @throws AuthorizeException
1489     * @throws IOException
1490     */

1491    public void withdraw() throws SQLException JavaDoc, AuthorizeException, IOException JavaDoc
1492    {
1493        String JavaDoc timestamp = DCDate.getCurrent().toString();
1494
1495        // Build some provenance data while we're at it.
1496
String JavaDoc collectionProv = "";
1497        Collection[] colls = getCollections();
1498
1499        for (int i = 0; i < colls.length; i++)
1500        {
1501            collectionProv = collectionProv + colls[i].getMetadata("name")
1502                    + " (ID: " + colls[i].getID() + ")\n";
1503        }
1504
1505        // Check permission. User either has to have REMOVE on owning collection
1506
// or be COLLECTION_EDITOR of owning collection
1507
if (AuthorizeManager.authorizeActionBoolean(ourContext,
1508                getOwningCollection(), Constants.COLLECTION_ADMIN)
1509                || AuthorizeManager.authorizeActionBoolean(ourContext,
1510                        getOwningCollection(), Constants.REMOVE))
1511        {
1512            // authorized
1513
}
1514        else
1515        {
1516            throw new AuthorizeException(
1517                    "To withdraw item must be COLLECTION_ADMIN or have REMOVE authorization on owning Collection");
1518        }
1519
1520        // Set withdrawn flag. timestamp will be set; last_modified in update()
1521
itemRow.setColumn("withdrawn", true);
1522
1523        // in_archive flag is now false
1524
itemRow.setColumn("in_archive", false);
1525
1526        // Add suitable provenance - includes user, date, collections +
1527
// bitstream checksums
1528
EPerson e = ourContext.getCurrentUser();
1529        String JavaDoc prov = "Item withdrawn by " + e.getFullName() + " ("
1530                + e.getEmail() + ") on " + timestamp + "\n"
1531                + "Item was in collections:\n" + collectionProv
1532                + InstallItem.getBitstreamProvenanceMessage(this);
1533
1534        addDC("description", "provenance", "en", prov);
1535
1536        // Update item in DB
1537
update();
1538
1539        // Invoke History system
1540
HistoryManager.saveHistory(ourContext, this, HistoryManager.MODIFY, e,
1541                ourContext.getExtraLogInfo());
1542
1543        // Remove from indicies
1544
Browse.itemRemoved(ourContext, getID());
1545        DSIndexer.unIndexContent(ourContext, this);
1546
1547        // and all of our authorization policies
1548
// FIXME: not very "multiple-inclusion" friendly
1549
AuthorizeManager.removeAllPolicies(ourContext, this);
1550
1551        // Write log
1552
log.info(LogManager.getHeader(ourContext, "withdraw_item", "user="
1553                + e.getEmail() + ",item_id=" + getID()));
1554    }
1555
1556    /**
1557     * Reinstate a withdrawn item
1558     *
1559     * @throws SQLException
1560     * @throws AuthorizeException
1561     * @throws IOException
1562     */

1563    public void reinstate() throws SQLException JavaDoc, AuthorizeException,
1564            IOException JavaDoc
1565    {
1566        String JavaDoc timestamp = DCDate.getCurrent().toString();
1567
1568        // Check permission. User must have ADD on all collections.
1569
// Build some provenance data while we're at it.
1570
String JavaDoc collectionProv = "";
1571        Collection[] colls = getCollections();
1572
1573        for (int i = 0; i < colls.length; i++)
1574        {
1575            collectionProv = collectionProv + colls[i].getMetadata("name")
1576                    + " (ID: " + colls[i].getID() + ")\n";
1577            AuthorizeManager.authorizeAction(ourContext, colls[i],
1578                    Constants.ADD);
1579        }
1580
1581        // Clear withdrawn flag
1582
itemRow.setColumn("withdrawn", false);
1583
1584        // in_archive flag is now true
1585
itemRow.setColumn("in_archive", true);
1586
1587        // Add suitable provenance - includes user, date, collections +
1588
// bitstream checksums
1589
EPerson e = ourContext.getCurrentUser();
1590        String JavaDoc prov = "Item reinstated by " + e.getFullName() + " ("
1591                + e.getEmail() + ") on " + timestamp + "\n"
1592                + "Item was in collections:\n" + collectionProv
1593                + InstallItem.getBitstreamProvenanceMessage(this);
1594
1595        addDC("description", "provenance", "en", prov);
1596
1597        // Update item in DB
1598
update();
1599
1600        // Invoke History system
1601
HistoryManager.saveHistory(ourContext, this, HistoryManager.MODIFY, e,
1602                ourContext.getExtraLogInfo());
1603
1604        // Add to indicies
1605
// Remove - update() already performs this
1606
// Browse.itemAdded(ourContext, this);
1607
DSIndexer.indexContent(ourContext, this);
1608
1609        // authorization policies
1610
if (colls.length > 0)
1611        {
1612            // FIXME: not multiple inclusion friendly - just apply access
1613
// policies from first collection
1614
// remove the item's policies and replace them with
1615
// the defaults from the collection
1616
inheritCollectionDefaultPolicies(colls[0]);
1617        }
1618
1619        // Write log
1620
log.info(LogManager.getHeader(ourContext, "reinstate_item", "user="
1621                + e.getEmail() + ",item_id=" + getID()));
1622    }
1623
1624    /**
1625     * Delete (expunge) the item. Bundles and bitstreams are also deleted if
1626     * they are not also included in another item. The Dublin Core metadata is
1627     * deleted.
1628     *
1629     * @throws SQLException
1630     * @throws AuthorizeException
1631     * @throws IOException
1632     */

1633    void delete() throws SQLException JavaDoc, AuthorizeException, IOException JavaDoc
1634    {
1635        HistoryManager.saveHistory(ourContext, this, HistoryManager.REMOVE,
1636                ourContext.getCurrentUser(), ourContext.getExtraLogInfo());
1637
1638        log.info(LogManager.getHeader(ourContext, "delete_item", "item_id="
1639                + getID()));
1640
1641        // Remove from cache
1642
ourContext.removeCached(this, getID());
1643
1644        // Remove from indices, if appropriate
1645
if (isArchived())
1646        {
1647            // Remove from Browse indices
1648
Browse.itemRemoved(ourContext, getID());
1649            DSIndexer.unIndexContent(ourContext, this);
1650        }
1651
1652        // Delete the Dublin Core
1653
removeMetadataFromDatabase();
1654
1655        // Remove bundles
1656
Bundle[] bunds = getBundles();
1657
1658        for (int i = 0; i < bunds.length; i++)
1659        {
1660            removeBundle(bunds[i]);
1661        }
1662
1663        // remove all of our authorization policies
1664
AuthorizeManager.removeAllPolicies(ourContext, this);
1665
1666        // Remove any Handle
1667
// FIXME: This is sort of a "tentacle" - HandleManager should provide
1668
// a way of doing this. Plus, deleting a Handle may have ramifications
1669
// that need considering.
1670
DatabaseManager.updateQuery(ourContext,
1671                "DELETE FROM handle WHERE resource_type_id= ? " +
1672                "AND resource_id= ? ",
1673                Constants.ITEM,getID());
1674
1675        // Finally remove item row
1676
DatabaseManager.delete(ourContext, itemRow);
1677    }
1678    
1679    /**
1680     * Remove item and all its sub-structure from the context cache.
1681     * Useful in batch processes where a single context has a long,
1682     * multi-item lifespan
1683     */

1684    public void decache() throws SQLException JavaDoc
1685    {
1686        // Remove item and it's submitter from cache
1687
ourContext.removeCached(this, getID());
1688        if (submitter != null)
1689        {
1690            ourContext.removeCached(submitter, submitter.getID());
1691        }
1692        // Remove bundles & bitstreams from cache if they have been loaded
1693
if (bundles != null)
1694        {
1695            Bundle[] bunds = getBundles();
1696            for (int i = 0; i < bunds.length; i++)
1697            {
1698                ourContext.removeCached(bunds[i], bunds[i].getID());
1699                Bitstream[] bitstreams = bunds[i].getBitstreams();
1700                for (int j = 0; j < bitstreams.length; j++)
1701                {
1702                    ourContext.removeCached(bitstreams[j], bitstreams[j].getID());
1703                }
1704            }
1705        }
1706    }
1707
1708    /**
1709     * Return <code>true</code> if <code>other</code> is the same Item as
1710     * this object, <code>false</code> otherwise
1711     *
1712     * @param other
1713     * object to compare to
1714     * @return <code>true</code> if object passed in represents the same item
1715     * as this object
1716     */

1717    public boolean equals(DSpaceObject other)
1718    {
1719        if (this.getType() == other.getType())
1720        {
1721            if (this.getID() == other.getID())
1722            {
1723                return true;
1724            }
1725        }
1726
1727        return false;
1728    }
1729
1730    /**
1731     * Return true if this Collection 'owns' this item
1732     *
1733     * @param c
1734     * Collection
1735     * @return true if this Collection owns this item
1736     */

1737    public boolean isOwningCollection(Collection c)
1738    {
1739        int owner_id = itemRow.getIntColumn("owning_collection");
1740
1741        if (c.getID() == owner_id)
1742        {
1743            return true;
1744        }
1745
1746        // not the owner
1747
return false;
1748    }
1749
1750    /**
1751     * Utility method to remove all descriptive metadata associated with the item from
1752     * the database (regardless of in-memory version)
1753     *
1754     * @throws SQLException
1755     */

1756    private void removeMetadataFromDatabase() throws SQLException JavaDoc
1757    {
1758        DatabaseManager.updateQuery(ourContext,
1759                "DELETE FROM MetadataValue WHERE item_id= ? ",
1760                getID());
1761    }
1762
1763    /**
1764     * return type found in Constants
1765     *
1766     * @return int Constants.ITEM
1767     */

1768    public int getType()
1769    {
1770        return Constants.ITEM;
1771    }
1772
1773    /**
1774     * remove all of the policies for item and replace them with a new list of
1775     * policies
1776     *
1777     * @param newpolicies -
1778     * this will be all of the new policies for the item and its
1779     * contents
1780     * @throws SQLException
1781     * @throws AuthorizeException
1782     */

1783    public void replaceAllItemPolicies(List JavaDoc newpolicies) throws SQLException JavaDoc,
1784            AuthorizeException
1785    {
1786        // remove all our policies, add new ones
1787
AuthorizeManager.removeAllPolicies(ourContext, this);
1788        AuthorizeManager.addPolicies(ourContext, newpolicies, this);
1789    }
1790
1791    /**
1792     * remove all of the policies for item's bitstreams and bundles and replace
1793     * them with a new list of policies
1794     *
1795     * @param newpolicies -
1796     * this will be all of the new policies for the bundle and
1797     * bitstream contents
1798     * @throws SQLException
1799     * @throws AuthorizeException
1800     */

1801    public void replaceAllBitstreamPolicies(List JavaDoc newpolicies)
1802            throws SQLException JavaDoc, AuthorizeException
1803    {
1804        // remove all policies from bundles, add new ones
1805
// Remove bundles
1806
Bundle[] bunds = getBundles();
1807
1808        for (int i = 0; i < bunds.length; i++)
1809        {
1810            Bundle mybundle = bunds[i];
1811
1812            Bitstream[] bs = mybundle.getBitstreams();
1813
1814            for (int j = 0; j < bs.length; j++)
1815            {
1816                Bitstream mybitstream = bs[j];
1817
1818                // change bitstream policies
1819
AuthorizeManager.removeAllPolicies(ourContext, bs[j]);
1820                AuthorizeManager.addPolicies(ourContext, newpolicies, bs[j]);
1821            }
1822
1823            // change bundle policies
1824
AuthorizeManager.removeAllPolicies(ourContext, mybundle);
1825            AuthorizeManager.addPolicies(ourContext, newpolicies, mybundle);
1826        }
1827    }
1828
1829    /**
1830     * remove all of the policies for item's bitstreams and bundles that belong
1831     * to a given Group
1832     *
1833     * @param g
1834     * Group referenced by policies that needs to be removed
1835     * @throws SQLException
1836     */

1837    public void removeGroupPolicies(Group g) throws SQLException JavaDoc
1838    {
1839        // remove Group's policies from Item
1840
AuthorizeManager.removeGroupPolicies(ourContext, this, g);
1841
1842        // remove all policies from bundles
1843
Bundle[] bunds = getBundles();
1844
1845        for (int i = 0; i < bunds.length; i++)
1846        {
1847            Bundle mybundle = bunds[i];
1848
1849            Bitstream[] bs = mybundle.getBitstreams();
1850
1851            for (int j = 0; j < bs.length; j++)
1852            {
1853                Bitstream mybitstream = bs[j];
1854
1855                // remove bitstream policies
1856
AuthorizeManager.removeGroupPolicies(ourContext, bs[j], g);
1857            }
1858
1859            // change bundle policies
1860
AuthorizeManager.removeGroupPolicies(ourContext, mybundle, g);
1861        }
1862    }
1863
1864    /**
1865     * remove all policies on an item and its contents, and replace them with
1866     * the DEFAULT_ITEM_READ and DEFAULT_BITSTREAM_READ policies belonging to
1867     * the collection.
1868     *
1869     * @param c
1870     * Collection
1871     * @throws java.sql.SQLException
1872     * if an SQL error or if no default policies found. It's a bit
1873     * draconian, but default policies must be enforced.
1874     * @throws AuthorizeException
1875     */

1876    public void inheritCollectionDefaultPolicies(Collection c)
1877            throws java.sql.SQLException JavaDoc, AuthorizeException
1878    {
1879        // remove the submit authorization policies
1880
// and replace them with the collection's default READ policies
1881
List JavaDoc policies = AuthorizeManager.getPoliciesActionFilter(ourContext, c,
1882                Constants.DEFAULT_ITEM_READ);
1883
1884        // change the action to just READ
1885
// just don't call update on the resourcepolicies!!!
1886
Iterator JavaDoc i = policies.iterator();
1887
1888        // MUST have default policies
1889
if (!i.hasNext())
1890        {
1891            throw new java.sql.SQLException JavaDoc("Collection " + c.getID()
1892                    + " has no default item READ policies");
1893        }
1894
1895        while (i.hasNext())
1896        {
1897            ResourcePolicy rp = (ResourcePolicy) i.next();
1898            rp.setAction(Constants.READ);
1899        }
1900
1901        replaceAllItemPolicies(policies);
1902
1903        policies = AuthorizeManager.getPoliciesActionFilter(ourContext, c,
1904                Constants.DEFAULT_BITSTREAM_READ);
1905
1906        // change the action to just READ
1907
// just don't call update on the resourcepolicies!!!
1908
i = policies.iterator();
1909
1910        if (!i.hasNext())
1911        {
1912            throw new java.sql.SQLException JavaDoc("Collection " + c.getID()
1913                    + " has no default bitstream READ policies");
1914        }
1915
1916        while (i.hasNext())
1917        {
1918            ResourcePolicy rp = (ResourcePolicy) i.next();
1919            rp.setAction(Constants.READ);
1920        }
1921
1922        replaceAllBitstreamPolicies(policies);
1923    }
1924
1925    /**
1926     * return TRUE if context's user can edit item, false otherwise
1927     *
1928     * @return boolean true = current user can edit item
1929     * @throws SQLException
1930     */

1931    public boolean canEdit() throws java.sql.SQLException JavaDoc
1932    {
1933        // can this person write to the item?
1934
if (AuthorizeManager.authorizeActionBoolean(ourContext, this,
1935                Constants.WRITE))
1936        {
1937            return true;
1938        }
1939
1940        // is this collection not yet created, and an item template is created
1941
if (getOwningCollection() == null)
1942        {
1943            return true;
1944        }
1945
1946        // is this person an COLLECTION_EDITOR for the owning collection?
1947
if (getOwningCollection().canEditBoolean())
1948        {
1949            return true;
1950        }
1951
1952        // is this person an COLLECTION_EDITOR for the owning collection?
1953
if (AuthorizeManager.authorizeActionBoolean(ourContext,
1954                getOwningCollection(), Constants.COLLECTION_ADMIN))
1955        {
1956            return true;
1957        }
1958
1959        return false;
1960    }
1961}
1962
Popular Tags