1    /*
2     * Copyright 2006 :torweg free software group
3     * 
4     * This program is free software: you can redistribute it and/or modify
5     * it under the terms of the GNU General Public License as published by
6     * the Free Software Foundation, either version 3 of the License, or
7     * (at your option) any later version.
8     * 
9     * This program is distributed in the hope that it will be useful,
10    * but WITHOUT ANY WARRANTY; without even the implied warranty of
11    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    * GNU General Public License for more details.
13    * 
14    * You should have received a copy of the GNU General Public License
15    * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16    *
17    */
18   package org.torweg.pulse.util.entity;
19   
20   import java.io.Serializable;
21   import java.util.ArrayList;
22   import java.util.HashSet;
23   import java.util.List;
24   import java.util.Map;
25   import java.util.Set;
26   import java.util.TreeMap;
27   
28   import javax.persistence.CascadeType;
29   import javax.persistence.FetchType;
30   import javax.persistence.ManyToMany;
31   import javax.persistence.ManyToOne;
32   import javax.persistence.MappedSuperclass;
33   import javax.persistence.Transient;
34   import javax.xml.bind.annotation.XmlAccessOrder;
35   import javax.xml.bind.annotation.XmlAccessType;
36   import javax.xml.bind.annotation.XmlAccessorOrder;
37   import javax.xml.bind.annotation.XmlAccessorType;
38   import javax.xml.bind.annotation.XmlAttribute;
39   import javax.xml.bind.annotation.XmlElement;
40   import javax.xml.bind.annotation.XmlElementWrapper;
41   import javax.xml.bind.annotation.XmlRootElement;
42   import javax.xml.bind.annotation.XmlTransient;
43   
44   import org.hibernate.LazyInitializationException;
45   import org.hibernate.annotations.BatchSize;
46   import org.jdom.Element;
47   import org.slf4j.Logger;
48   import org.slf4j.LoggerFactory;
49   import org.torweg.pulse.bundle.Bundle;
50   import org.torweg.pulse.util.INameable;
51   import org.torweg.pulse.util.entity.AbstractImageGroupConfiguration.Version;
52   import org.torweg.pulse.vfs.VirtualFile;
53   
54   /**
55    * Holds different versions of an image (e.g. the differently sized versions of
56    * an image assigned to a {@code Content}).
57    * 
58    * @see AbstractImageGroupConfiguration
59    * @author Christian Schatt, Daniel Dietz
60    * @version $Revision: 2014 $
61    */
62   @MappedSuperclass
63   @XmlRootElement(name = "abstract-image-group")
64   @XmlAccessorOrder(XmlAccessOrder.UNDEFINED)
65   @XmlAccessorType(XmlAccessType.FIELD)
66   public abstract class AbstractImageGroup extends AbstractNamableEntity
67           implements INameable {
68   
69       /**
70        * serialVersionUID.
71        */
72       private static final long serialVersionUID = 4685251301965010817L;
73   
74       /**
75        * the logger.
76        */
77       private static final Logger LOGGER = LoggerFactory
78               .getLogger(AbstractImageGroup.class);
79   
80       /**
81        * the bundle the image group belongs to.
82        */
83       @ManyToOne
84       @XmlElement(name = "bundle")
85       private Bundle bundle;
86   
87       /**
88        * The images associated with the {@code AbstractImageGroup}.
89        */
90       @ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST,
91               CascadeType.MERGE, CascadeType.REFRESH })
92       @BatchSize(size = 10)
93       @XmlTransient
94       // getter JAXB-annotated
95       private final Map<String, VirtualFile> imageVersions = new TreeMap<String, VirtualFile>();
96   
97       /**
98        * The underlying {@code AbstractImageGroupConfiguration}.
99        */
100      @Transient
101      @XmlTransient
102      private AbstractImageGroupConfiguration imageVersionConfiguration;
103  
104      /**
105       * used for Hibernate<sup>TM</sup>.
106       */
107      protected AbstractImageGroup() {
108          super();
109      }
110  
111      /**
112       * The {@code Constructor} that sets the name of the
113       * {@code AbstractImageGroup}.
114       * 
115       * @param nam
116       *            the name to be set.
117       * @param b
118       *            the bundle
119       */
120      public AbstractImageGroup(final String nam, final Bundle b) {
121          super();
122          setName(nam);
123          this.bundle = b;
124      }
125  
126      /**
127       * returns the bundle of the {@code AbstractImageGroup}.
128       * 
129       * @return the bundle
130       */
131      public final Bundle getBundle() {
132          return this.bundle;
133      }
134  
135      /**
136       * sets the bundle of the {@code AbstractImageGroup}.
137       * 
138       * @param b
139       *            the bundle
140       */
141      public final void setBundle(final Bundle b) {
142          this.bundle = b;
143      }
144  
145      /**
146       * Returns a {@code List} containing the version names of all image versions
147       * assignable to the {@code AbstractImageGroup}.
148       * 
149       * @return the version names of all image versions assignable to the
150       *         {@code AbstractImageGroup}
151       */
152      public abstract List<Version> getAllAssignableVersions();
153  
154      /**
155       * Returns a {@code List} containing the version names of the image versions
156       * assigned to the {@code AbstractImageGroup}.
157       * 
158       * @return the version names of the image versions assigned to the
159       *         {@code AbstractImageGroup}
160       */
161      public final List<String> getAssignedVersionNames() {
162          return new ArrayList<String>(this.imageVersions.keySet());
163      }
164  
165      /**
166       * Returns the image version with the given version name.
167       * 
168       * @param versionName
169       *            the version name of the image version to be returned
170       * @return the image version with the given version name or {@code null}
171       */
172      public final VirtualFile getImageVersion(final String versionName) {
173          return this.imageVersions.get(versionName);
174      }
175  
176      /**
177       * returns a flat set of all assigned images.
178       * 
179       * @return a flat set of all assigned images
180       */
181      public final List<VirtualFile> getImages() {
182          return new ArrayList<VirtualFile>(this.imageVersions.values());
183      }
184  
185      /**
186       * Sets the image version with the given version name.
187       * 
188       * @param versionName
189       *            the version name of the image version to be set
190       * @param image
191       *            the image version to be set
192       */
193      public final void setImageVersion(final String versionName,
194              final VirtualFile image) {
195          if (image == null) {
196              throw new NullPointerException(
197                      "null must not be added to the image versions of the AbstractImageGroup.");
198          }
199  
200          boolean isAvailableVersion = isAvailableVersionName(versionName);
201  
202          if (!isAvailableVersion) {
203              throw new ImageVersionNameException(
204                      "The given parameter versionName: "
205                              + versionName
206                              + " is not in the list of assignable version names of the AbstractImageGroup.");
207          }
208  
209          this.imageVersions.put(versionName, image);
210      }
211  
212      /**
213       * checks whether the given name is assignable for the
214       * {@code AbstractImageGroup}.
215       * 
216       * @param versionName
217       *            the version name to check
218       * @return {@code true}, if and only if {@code setImageVersion(String,
219       *         VirtualFile)} can be safely called for the given name. Otherwise
220       *         {@code false}.
221       */
222      public final boolean isAvailableVersionName(final String versionName) {
223          boolean isAvailableVersion = false;
224          for (Version version : getAllAssignableVersions()) {
225              if (version.getName().equals(versionName)) {
226                  isAvailableVersion = true;
227                  break;
228              }
229          }
230          return isAvailableVersion;
231      }
232  
233      /**
234       * initialises the lazy fields of the {@code AbstractImageGroup}.
235       */
236      public final void init() {
237          this.imageVersions.size();
238      }
239  
240      /**
241       * Sets the {@code AbstractImageGroupConfiguration}.
242       * 
243       * @param conf
244       *            the {@code AbstractImageGroupConfiguration}
245       */
246      protected final void setImageVersionConfiguration(
247              final AbstractImageGroupConfiguration conf) {
248          if (conf == null) {
249              throw new NullPointerException(
250                      "Given AbstractImageGroupConfiguration must not be null.");
251          }
252          this.imageVersionConfiguration = conf;
253      }
254  
255      /**
256       * Returns the underlying {@code AbstractImageGroupConfiguration}.
257       * 
258       * @return the <tt>imageVersionConfiguration</tt>
259       */
260      public final AbstractImageGroupConfiguration getImageVersionConfiguration() {
261          return this.imageVersionConfiguration;
262      }
263  
264      /**
265       * Deserializes the state of the {@code AbstractImageGroup} as a JDOM
266       * {@code Element}.
267       * 
268       * @return the state of the {@code AbstractImageGroup} as a JDOM
269       *         {@code Element}
270       */
271      public Element deserializeToJDOM() {
272          Element element = new Element(getClass().getSimpleName()).setAttribute(
273                  "class", getClass().getCanonicalName());
274          if (getId() != null) {
275              element.setAttribute("id", getId().toString());
276          }
277          if (getBundle() != null) {
278              element.setAttribute("bundle", getBundle().getName());
279          }
280          element.setAttribute("name", getName());
281          try {
282              for (String versionName : getAssignedVersionNames()) {
283                  element.addContent(getImageVersion(versionName)
284                          .deserializeToJDOM().setAttribute("version",
285                                  versionName));
286              }
287          } catch (LazyInitializationException e) {
288              LOGGER.debug("ignored: {}", e.getLocalizedMessage());
289          }
290          return element;
291      }
292  
293      /**
294       * For JAXB only.
295       * 
296       * @return this.imageVersions
297       */
298      @XmlElementWrapper(name = "image-versions")
299      @XmlElement(name = "image")
300      @SuppressWarnings("unused")
301      @Deprecated
302      private HashSet<ImageVersion> getImageVersionsJAXB() { // NOPMD
303          try {
304              return new HashSet<ImageVersion>(
305                      ImageVersion.getImageVersions(this.imageVersions));
306          } catch (LazyInitializationException e) {
307              LOGGER.debug("ignored: {}", e.getLocalizedMessage());
308              return null;
309          }
310      }
311  
312      /**
313       * Utility-class for outputting a {@code ImageVersion} via JAXB.
314       * 
315       * @author Daniel Dietz
316       * @version $Revision: 2014 $
317       * 
318       */
319      @XmlRootElement(name = "image-version")
320      @XmlAccessorOrder(XmlAccessOrder.UNDEFINED)
321      @XmlAccessorType(XmlAccessType.FIELD)
322      private static final class ImageVersion implements Serializable {
323  
324          /**
325           * The serialVersionUID.
326           */
327          private static final long serialVersionUID = 3085378991033796094L;
328  
329          /**
330           * The version-name.
331           */
332          @XmlAttribute(name = "version")
333          @SuppressWarnings("unused")
334          private String versionName; // NOPMD
335  
336          /**
337           * The {@code VirtualFile}.
338           */
339          @XmlElement(name = "file")
340          @SuppressWarnings("unused")
341          private VirtualFile file; // NOPMD
342  
343          /**
344           * Default constructor.
345           */
346          @Deprecated
347          @SuppressWarnings("unused")
348          protected ImageVersion() {
349              super();
350          }
351  
352          /**
353           * Creates a new {@code ImageVersion} with the given version-name and
354           * the given {@code VirtualFile}.
355           * 
356           * @param version
357           *            the version-name
358           * @param f
359           *            the {@code VirtualFile}
360           */
361          protected ImageVersion(final String version, final VirtualFile f) {
362              super();
363              this.versionName = version;
364              this.file = f;
365          }
366  
367          /**
368           * Convenience method to create a {@code List<ImageVersion>} from a
369           * given {@code Map<String, VirtualFile>}.
370           * 
371           * @param versions
372           *            the {@code Map<String, VirtualFile>} of localisations
373           * 
374           * @return a {@code List<ImageVersion>}
375           */
376          protected static Set<ImageVersion> getImageVersions(
377                  final Map<String, VirtualFile> versions) {
378              Set<ImageVersion> vers = new HashSet<ImageVersion>();
379              for (Map.Entry<String, VirtualFile> entry : versions.entrySet()) {
380                  vers.add(new ImageVersion(entry.getKey(), entry.getValue()));
381              }
382              return vers;
383          }
384      }
385  
386  }
387