1    /*
2     * Copyright 2008 :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   package org.torweg.pulse.site.content;
18   
19   import java.io.IOException;
20   import java.io.StringReader;
21   import java.util.HashSet;
22   import java.util.Set;
23   
24   import javax.persistence.CascadeType;
25   import javax.persistence.Column;
26   import javax.persistence.FetchType;
27   import javax.persistence.Lob;
28   import javax.persistence.ManyToOne;
29   import javax.persistence.MappedSuperclass;
30   import javax.persistence.Transient;
31   import javax.xml.bind.annotation.XmlAccessOrder;
32   import javax.xml.bind.annotation.XmlAccessType;
33   import javax.xml.bind.annotation.XmlAccessorOrder;
34   import javax.xml.bind.annotation.XmlAccessorType;
35   import javax.xml.bind.annotation.XmlAttribute;
36   import javax.xml.bind.annotation.XmlElement;
37   import javax.xml.bind.annotation.XmlRootElement;
38   import javax.xml.bind.annotation.XmlTransient;
39   import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
40   
41   import org.hibernate.LazyInitializationException;
42   import org.jdom.Element;
43   import org.jdom.JDOMException;
44   import org.jdom.input.SAXBuilder;
45   import org.torweg.pulse.accesscontrol.User;
46   import org.torweg.pulse.service.PulseException;
47   import org.torweg.pulse.service.request.ServiceRequest;
48   import org.torweg.pulse.site.content.util.ILinkCorrectableElement;
49   import org.torweg.pulse.util.xml.XMLConverter;
50   import org.torweg.pulse.util.xml.bind.ElementXmlAdapter;
51   
52   /**
53    * @param <T>
54    *            the actual implementation
55    * 
56    * @author Christian Schatt, Daniel Dietz
57    * @version $Revision: 1984 $
58    */
59   @MappedSuperclass
60   @XmlRootElement(name = "abstract-basic-variant")
61   @XmlAccessorOrder(XmlAccessOrder.UNDEFINED)
62   @XmlAccessorType(XmlAccessType.FIELD)
63   public abstract class AbstractBasicVariant<T extends AbstractBasicVariant<T>>
64           extends Variant<T> {
65   
66       /**
67        * serialVersionUID.
68        */
69       private static final long serialVersionUID = 1697853927204347160L;
70   
71       /**
72        * The {@code Content} of the {@code AbstractBasicVariant}.
73        */
74       @ManyToOne(fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST,
75               CascadeType.MERGE, CascadeType.REFRESH }, optional = false)
76       @XmlTransient
77       // -> this.getContentIdJAXB()
78       private Content content = null;
79   
80       /**
81        * The summary of the {@code AbstractBasicVariant}.
82        */
83       @Lob
84       @Column(length = 65535)
85       @XmlTransient
86       private String summary = "<body/>";
87   
88       /**
89        * The summary of the {@code AbstractBasicVariant} as a JDOM {@code Element}
90        * .
91        */
92       @Transient
93       // getter JAXB-annotated
94       private transient Element summaryElement = null;
95   
96       /**
97        * Returns the {@code Content} of the {@code AbstractBasicVariant} .
98        * 
99        * @return the {@code Content} of the {@code AbstractBasicVariant} .
100       */
101      @Override
102      public final Content getContent() {
103          return this.content;
104      }
105  
106      /**
107       * Sets the {@code Content} of the {@code AbstractBasicVariant}.
108       * 
109       * @param pContent
110       *            the {@code Content} to be set.
111       */
112      @Override
113      public void setContent(final Content pContent) {
114          if (pContent == null) {
115              throw new IllegalArgumentException(
116                      "The given Content must be not be null.");
117          } else if (!Content.class.isAssignableFrom(pContent.getClass())) {
118              throw new IllegalArgumentException("The runtime class "
119                      + "of the given Content must be Content.");
120          }
121          this.content = (Content) pContent;
122          if (!getVariants().isEmpty() && getVariants() != null) {
123              for (AbstractBasicVariant<?> variant : getVariants()) {
124                  variant.setContent(pContent);
125              }
126          }
127      }
128  
129      /**
130       * For JAXB only.
131       * 
132       * @return this.getContent().getId()
133       */
134      @XmlElement(name = "content-id")
135      @SuppressWarnings("unused")
136      @Deprecated
137      private Long getContentIdJAXB() {
138          try {
139              return getContent().getId();
140          } catch (LazyInitializationException e) {
141              LOGGER.debug("ignored: {}", e.getLocalizedMessage());
142              return null;
143          }
144      }
145  
146      /**
147       * For JAXB only.
148       * 
149       * @return this.getContent().getBundle().getName()
150       */
151      @XmlAttribute(name = "bundle")
152      @SuppressWarnings("unused")
153      @Deprecated
154      private String getBundleJAXB() {
155          try {
156              return getContent().getBundle().getName();
157          } catch (LazyInitializationException e) {
158              LOGGER.debug("ignored: {}", e.getLocalizedMessage());
159              return null;
160          }
161      }
162  
163      /**
164       * Returns the summary of the {@code AbstractBasicVariant}.
165       * 
166       * @return the summary of the {@code AbstractBasicVariant}.
167       */
168      public final String getSummary() {
169          return this.summary;
170      }
171  
172      /**
173       * Sets the summary of the {@code AbstractBasicVariant} from a JDOM {@code
174       * Element}.
175       * 
176       * @param pSummaryElement
177       *            the JDOM {@code Element} representing the summary to be set.
178       */
179      public final void setSummary(final Element pSummaryElement) {
180          this.summary = XMLConverter.getRawString(pSummaryElement, true);
181          this.summaryElement = pSummaryElement;
182      }
183  
184      /**
185       * Returns the summary of the {@code AbstractBasicVariant} as a JDOM {@code
186       * Element}.
187       * 
188       * @return the summary of the {@code AbstractBasicVariant} as a JDOM {@code
189       *         Element}.
190       */
191      public final Element getSummaryElement() {
192          if (getSummary() == null) {
193              return null;
194          }
195          if (this.summaryElement == null) {
196              try {
197                  this.summaryElement = new SAXBuilder().build(
198                          new StringReader(getSummary())).getRootElement();
199              } catch (JDOMException e) {
200                  throw new PulseException("Error parsing XML for summary: "
201                          + e.getLocalizedMessage(), e);
202              } catch (IOException e) {
203                  throw new PulseException("Error reading XML for summary: "
204                          + e.getLocalizedMessage(), e);
205              }
206          }
207          return (Element) this.summaryElement.clone();
208      }
209  
210      /**
211       * For JAXB only.
212       * 
213       * @return this.getSummaryElement()
214       */
215      @XmlElement(name = "summary-element")
216      @XmlJavaTypeAdapter(value = ElementXmlAdapter.class)
217      @SuppressWarnings("unused")
218      @Deprecated
219      private Element getSummaryElementJAXB() {
220          try {
221              return getSummaryElement();
222          } catch (LazyInitializationException e) {
223              LOGGER.debug("ignored: {}", e.getLocalizedMessage());
224              return null;
225          }
226      }
227  
228      /**
229       * Returns the {@code AbstractBasicVariant}'s textual information as it is
230       * supposed to be supplied for the index.
231       * 
232       * <p>
233       * Implementing classes may override the method to supply more information
234       * for the index.
235       * </p>
236       * 
237       * @return the {@code AbstractBasicVariant}'s textual information as it is
238       *         supposed to be supplied for the index.
239       */
240      @Override
241      public StringBuilder getFullTextValue() {
242          return new StringBuilder(getName()).append(' ').append(
243                  XMLConverter.getHTMLText(getSummaryElement()));
244      }
245  
246      /**
247       * Returns the state of the {@code AbstractBasicVariant} as a JDOM {@code
248       * Element}.
249       * 
250       * @return the state of the {@code AbstractBasicVariant} as a JDOM {@code
251       *         Element}.
252       */
253      public Element deserializeToJDOM() {
254          return deserializeToJDOM(null);
255      }
256  
257      /**
258       * Returns the state of the {@code AbstractBasicVariant} as a JDOM {@code
259       * Element}.
260       * 
261       * @param pServiceRequest
262       *            the current {@code ServiceRequest} or {@code null}.
263       * 
264       * @return the state of the {@code AbstractBasicVariant} as a JDOM {@code
265       *         Element}.
266       */
267      public Element deserializeToJDOM(final ServiceRequest pServiceRequest) {
268          Element element = new Element("Variant").setAttribute("class",
269                  getClass().getCanonicalName());
270          if (getId() != null) {
271              element.setAttribute("id", getId().toString());
272          }
273          if (getContent() != null) {
274              element.setAttribute("bundle", getContent().getBundle().getName());
275              element.addContent(new Element("content-id").setText(getContent()
276                      .getId().toString()));
277          }
278          if (getName() != null) {
279              element.addContent(new Element("name").setText(getName()));
280          }
281          addAttachments(pServiceRequest, element);
282          if (getSummary() != null) {
283              element.addContent(new Element("summary")
284                      .addContent((Element) getSummaryElement().clone()));
285          }
286          if (getParentVariant() != null) {
287              element.addContent(new Element("parent-variant-id")
288                      .setText(getParentVariant().getId().toString()));
289          }
290          Element variants = new Element("variants");
291          try {
292              for (T variant : getVariants()) {
293                  variants.addContent(variant.deserializeToJDOM());
294              }
295          } catch (LazyInitializationException e) {
296              LOGGER.debug("ignored: {}", e.getLocalizedMessage());
297          }
298          element.addContent(variants);
299          return element;
300      }
301  
302      /**
303       * @return the set of {@code ILinkCorrectableElement}s
304       * @see org.torweg.pulse.site.content.util.ILinkCorretable#getLinkCorrectables()
305       */
306      public Set<ILinkCorrectableElement> getLinkCorrectables() {
307          Set<ILinkCorrectableElement> correctables = new HashSet<ILinkCorrectableElement>();
308          correctables.add(new SummaryCorrectable(this));
309          return correctables;
310      }
311  
312      /**
313       * Adds the {@code Attachment}s of the {@code Variant} to the given JDOM
314       * {@code Element}.
315       * 
316       * @param pServiceRequest
317       *            the current {@code ServiceRequest} or {@code null}.
318       * @param pElement
319       *            the JDOM {@code Element} to be filled with data.
320       */
321      private void addAttachments(final ServiceRequest pServiceRequest,
322              final Element pElement) {
323          /* get user for extended information */
324          User user = null;
325          if (pServiceRequest != null) {
326              user = pServiceRequest.getUser();
327          }
328          Element element = new Element("attachments");
329          try {
330              for (Attachment attachment : getAttachments()) {
331                  Element attElem = attachment.deserializeToJDOM();
332                  attElem.setAttribute("canRead", Boolean.toString(attachment
333                          .getVirtualFile().canRead(user)));
334                  attElem.setAttribute("canWrite", Boolean.toString(attachment
335                          .getVirtualFile().canWrite(user)));
336                  element.addContent(attElem);
337              }
338          } catch (LazyInitializationException e) {
339              LOGGER.trace("adding attachments: {}", e.getMessage());
340          } finally {
341              pElement.addContent(element);
342          }
343      }
344  
345      /**
346       * is an {@code ILinkCorrectableElement} for the summary of the {@code
347       * AbstractBasicVariant}.
348       * 
349       * @author Thomas Weber
350       */
351      private static final class SummaryCorrectable implements
352              ILinkCorrectableElement {
353  
354          /**
355           * the abstract basic content.
356           */
357          private final AbstractBasicVariant<?> variant;
358  
359          /**
360           * creates a new {@code SummaryCorrectable} for the given {@code
361           * AbstractBasicVariant}.
362           * 
363           * @param v
364           *            the variant as a back reference
365           */
366          public SummaryCorrectable(final AbstractBasicVariant<?> v) {
367              super();
368              this.variant = v;
369          }
370  
371          /**
372           * @return the element
373           * @see org.torweg.pulse.site.content.util.ILinkCorrectableElement#getElement()
374           */
375          public Element getElement() {
376              return this.variant.getSummaryElement();
377          }
378  
379          /**
380           * @param e
381           *            the element to set
382           * @see org.torweg.pulse.site.content.util.ILinkCorrectableElement#setElement(org.jdom.Element)
383           */
384          public void setElement(final Element e) {
385              this.variant.setSummary(e);
386          }
387  
388      }
389  
390  }
391