1    /*
2     * Copyright 2007 :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.service.request;
19   
20   import java.util.ArrayList;
21   import java.util.HashSet;
22   import java.util.Locale;
23   import java.util.Set;
24   
25   import javax.persistence.Basic;
26   import javax.persistence.CascadeType;
27   import javax.persistence.Entity;
28   import javax.persistence.FetchType;
29   import javax.persistence.ManyToMany;
30   import javax.persistence.ManyToOne;
31   import javax.persistence.Transient;
32   import javax.xml.bind.annotation.XmlAccessOrder;
33   import javax.xml.bind.annotation.XmlAccessType;
34   import javax.xml.bind.annotation.XmlAccessorOrder;
35   import javax.xml.bind.annotation.XmlAccessorType;
36   import javax.xml.bind.annotation.XmlElement;
37   import javax.xml.bind.annotation.XmlElementWrapper;
38   import javax.xml.bind.annotation.XmlRootElement;
39   import javax.xml.bind.annotation.XmlTransient;
40   import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
41   
42   import org.hibernate.LazyInitializationException;
43   import org.hibernate.annotations.BatchSize;
44   import org.jdom.Element;
45   import org.slf4j.Logger;
46   import org.slf4j.LoggerFactory;
47   import org.torweg.pulse.accesscontrol.CommandMatcher;
48   import org.torweg.pulse.bundle.Bundle;
49   import org.torweg.pulse.bundle.JDOMable;
50   import org.torweg.pulse.util.entity.AbstractBasicEntity;
51   import org.torweg.pulse.util.xml.bind.LocaleXmlAdapter;
52   
53   /**
54    * used to build {@code Commands}.
55    * <p>
56    * Is used to build new {@code Command}s from an existing {@code Command}
57    * (usually the current {@code Command}). {@code CommandBuilder}s can be created
58    * from XML definitions similar to {@code CommandMatcher} or {@code
59    * CommandMatcher}s definitions.
60    * </p>
61    * 
62    * @author Thomas Weber, Daniel Dietz
63    * @version $Revision: 1477 $
64    * @see CommandBuilder#mixIn(Command)
65    */
66   @Entity
67   @XmlRootElement(name = "command-builder")
68   @XmlAccessorOrder(XmlAccessOrder.UNDEFINED)
69   @XmlAccessorType(XmlAccessType.FIELD)
70   public class CommandBuilder extends AbstractBasicEntity implements JDOMable {
71   
72       /**
73        * serialVersionUID.
74        */
75       private static final long serialVersionUID = 3151166572064016775L;
76   
77       /**
78        * the logger.
79        */
80       private static final Logger LOGGER = LoggerFactory
81               .getLogger(CommandBuilder.class);
82   
83       /**
84        * The {@code Locale} of the {@code CommandBuilder}.
85        */
86       @Basic
87       @XmlElement(name = "locale")
88       @XmlJavaTypeAdapter(LocaleXmlAdapter.class)
89       private Locale locale = null;
90   
91       /**
92        * The {@code Bundle} of the {@code CommandBuilder}.
93        */
94       @ManyToOne(targetEntity = Bundle.class, fetch = FetchType.EAGER, cascade = {
95               CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH })
96       @XmlElement(name = "bundle")
97       private Bundle bundle = null;
98   
99       /**
100       * The action of the {@code CommandBuilder}.
101       */
102      @Basic
103      @XmlElement(name = "action")
104      private String action = null;
105  
106      /**
107       * The {@code CommandBuilder}'s {@code SitemapNode} id.
108       */
109      @Basic
110      @XmlElement(name = "sitemapnode-id")
111      private Long sitemapNodeId = null;
112  
113      /**
114       * The {@code Parameter}s associated with the {@code CommandBuilder}.
115       */
116      @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
117      @BatchSize(size = 20)
118      @XmlTransient
119      // getter JAXB-annotated
120      private Set<Parameter> parameters = new HashSet<Parameter>();
121  
122      /**
123       * transient bundleName.
124       */
125      @Transient
126      private String bundleName;
127  
128      /**
129       * default constructor for Hibernate<sup>TM</sup>.
130       */
131      @Deprecated
132      protected CommandBuilder() {
133          super();
134      }
135  
136      /**
137       * builds the {@code CommandBuilder} defined in the mapping XML.
138       * <p>
139       * sample mapping XML snippet:
140       * 
141       * <pre>
142       *                            &lt;command action=&quot;actionname&quot; locale=&quot;de_DE&quot;&gt;
143       *                              &lt;parameter name=&quot;param1&quot;&gt;
144       *                                &lt;value&gt;value1.1&lt;/value&gt;
145       *                                &lt;value&gt;value1.2&lt;/value&gt;
146       *                              &lt;/parameter&gt;
147       *                              &lt;parameter name=&quot;param2&quot;/&gt;
148       *                              &lt;parameter name=&quot;param3&quot;&gt;
149       *                                &lt;value&gt;value3&lt;/value&gt;
150       *                              &lt;/parameter&gt;
151       *                            &lt;/command&gt;
152       * </pre>
153       * 
154       * The above sample is full blown. The following example shows the minimal
155       * mapping:
156       * 
157       * <pre>
158       *                            &lt;command action=&quot;actionname&quot;/&gt;
159       * </pre>
160       * 
161       * </p>
162       * 
163       * @param command
164       *            the command XML
165       * @param b
166       *            the {@code Bundle}
167       */
168      public CommandBuilder(final Element command, final String b) {
169          super();
170          if (command.getAttribute("action") != null) {
171              this.action = command.getAttributeValue("action");
172          }
173          if (b != null) {
174              this.bundleName = b;
175          }
176          if (command.getAttributeValue("bundle") != null) {
177              this.bundleName = command.getAttributeValue("bundle");
178          }
179          /* add locale if present */
180          if ((command.getAttributeValue("locale") != null)
181                  && !command.getAttributeValue("locale").equals("")) {
182              this.locale = new Locale(command.getAttributeValue("locale"));
183          }
184  
185          /* add sitemapID if present */
186          if ((command.getAttributeValue("sitemapID") != null)
187                  && !command.getAttributeValue("sitemapID").equals("")) {
188              this.sitemapNodeId = Long.parseLong(command
189                      .getAttributeValue("sitemapID"));
190          }
191  
192          initializeParameters(command);
193      }
194  
195      /**
196       * used by the constructor to initialise the HTTP and pulse parameters.
197       * 
198       * @param command
199       *            the element describing the {@code CommandBuilder}
200       */
201      private void initializeParameters(final Element command) {
202          /* add pulse parameters if present */
203          for (Object paramObj : command.getChildren("parameter")) {
204              Element parameterElement = (Element) paramObj;
205              ArrayList<String> values = new ArrayList<String>();
206              for (Object valueObj : parameterElement.getChildren("value")) {
207                  values.add(((Element) valueObj).getTextNormalize());
208              }
209              this.parameters.add(new Parameter(parameterElement
210                      .getAttributeValue("name"), values));
211          }
212          /* add http parameters if present */
213          for (Object paramObj : command.getChildren("httpParameter")) {
214              Element parameterElement = (Element) paramObj;
215              ArrayList<String> values = new ArrayList<String>();
216              for (Object valueObj : parameterElement.getChildren("value")) {
217                  values.add(((Element) valueObj).getTextNormalize());
218              }
219              this.parameters.add(new Parameter(parameterElement
220                      .getAttributeValue("name"), values));
221          }
222      }
223  
224      /**
225       * copy constructor.
226       * 
227       * @param cb
228       *            the source for the copy
229       */
230      private CommandBuilder(final CommandBuilder cb) {
231          super();
232          this.locale = cb.locale;
233          this.action = cb.action;
234          this.bundle = cb.bundle;
235          this.bundleName = cb.bundleName;
236          this.sitemapNodeId = cb.sitemapNodeId;
237          // try {
238          for (Parameter p : cb.getParameters()) {
239              this.parameters.add(p.createCopy());
240          }
241          // } catch (LazyInitializationException e) {
242          // this.parameters = cb.parameters;
243          // }
244      }
245  
246      /**
247       * builds a {@code CommandBuilder} from a given {@code CommandMatcher}.
248       * 
249       * @param matcher
250       *            the template {@code CommandMatcher}
251       */
252      public CommandBuilder(final CommandMatcher matcher) {
253          super();
254          this.locale = matcher.getLocale();
255          this.action = matcher.getAction();
256          this.bundle = matcher.getBundle();
257          this.sitemapNodeId = matcher.getSitemapNodeId();
258          this.parameters.addAll(matcher.getParameters());
259      }
260  
261      /**
262       * @return Returns the action.
263       */
264      public final String getAction() {
265          return this.action;
266      }
267  
268      /**
269       * @param a
270       *            The action to set.
271       */
272      public final void setAction(final String a) {
273          this.action = a;
274      }
275  
276      /**
277       * @return Returns the bundle.
278       */
279      public final Bundle getBundle() {
280          return this.bundle;
281      }
282  
283      /**
284       * @param b
285       *            The bundle to set.
286       */
287      public final void setBundle(final Bundle b) {
288          this.bundle = b;
289      }
290  
291      /**
292       * @return Returns the locale.
293       */
294      public final Locale getLocale() {
295          return this.locale;
296      }
297  
298      /**
299       * @param l
300       *            The locale to set.
301       */
302      public final void setLocale(final Locale l) {
303          this.locale = l;
304      }
305  
306      /**
307       * @return Returns the sitemapNodeId.
308       */
309      public final Long getSitemapNodeId() {
310          return this.sitemapNodeId;
311      }
312  
313      /**
314       * @param i
315       *            The sitemapNodeId to set.
316       */
317      public final void setSitemapNodeId(final Long i) {
318          this.sitemapNodeId = i;
319      }
320  
321      /**
322       * creates a mixture of the given template {@code Command} and the {@code
323       * CommandMatcher}, in terms of adding all specified information of the
324       * {@code CommandMatcher} to the given template {@code Command}.
325       * 
326       * @param templateCommand
327       *            the template {@code Command}
328       * @return the modified template Command
329       */
330      public final Command mixIn(final Command templateCommand) {
331          // templateCommand.setSitemapID(null);
332          if (this.locale != null) {
333              templateCommand.setLocale(this.locale);
334          }
335          if (this.bundle != null) {
336              templateCommand.setBundle(this.bundle.getName());
337          }
338          if (this.bundleName != null) {
339              templateCommand.setBundle(this.bundleName);
340          }
341          if (this.action != null) {
342              templateCommand.setAction(this.action);
343          }
344          if (this.sitemapNodeId != null) {
345              templateCommand.setSitemapID(this.sitemapNodeId);
346          }
347          for (Parameter p : this.parameters) {
348              templateCommand.removeParameter(p.getName());
349              templateCommand.addPulseParameter(p.getName(), p.getValues());
350          }
351          return templateCommand;
352      }
353  
354      /**
355       * returns the {@code Parameter}s of the {@code CommandBuilder}.
356       * 
357       * @return the {@code Parameter}s of the {@code CommandBuilder}
358       */
359      public final Set<Parameter> getParameters() {
360          return this.parameters;
361      }
362  
363      /**
364       * For JAXB only.
365       * 
366       * @return this.getParameters()
367       */
368      @XmlElementWrapper(name = "parameters")
369      @XmlElement(name = "parameter")
370      @SuppressWarnings("unused")
371      @Deprecated
372      private HashSet<Parameter> getParametersJAXB() { // NOPMD
373          try {
374              return new HashSet<Parameter>(getParameters());
375          } catch (LazyInitializationException e) {
376              LOGGER.debug("ignored: {}", e.getLocalizedMessage());
377              return null;
378          }
379      }
380  
381      /**
382       * removes a {@code Parameter} from the {@code CommandBuilder}.
383       * 
384       * @param parameter
385       *            the {@code Parameter} to be removed
386       */
387      public final void removeParameter(final Parameter parameter) {
388          this.parameters.remove(parameter);
389      }
390  
391      /**
392       * sets the {@code Parameter}s of the {@code CommandBuilder}.
393       * 
394       * @param newParameters
395       *            the new {@code Parameter}s for the {@code CommandBuilder}
396       */
397      public final void setParameters(final Set<Parameter> newParameters) {
398          this.parameters = newParameters;
399      }
400  
401      /**
402       * returns a clone of the {@code CommandBuilder}.
403       * 
404       * @return a clone of the {@code CommandBuilder}
405       */
406      public final CommandBuilder createCopy() {
407          return new CommandBuilder(this);
408      }
409  
410      /**
411       * @return a basic JDOM representation
412       * @see org.torweg.pulse.bundle.JDOMable#deserializeToJDOM()
413       */
414      public final Element deserializeToJDOM() {
415          Element jdom = new Element(this.getClass().getSimpleName())
416                  .setAttribute("class", this.getClass().getCanonicalName());
417          if (this.locale != null) {
418              jdom.addContent(new Element("Locale").setText(this.locale
419                      .toString()));
420          } else {
421              jdom.addContent(new Element("Locale"));
422          }
423          if (this.bundle != null) {
424              jdom.addContent(new Element("Bundle")
425                      .setText(this.bundle.getName()));
426          } else {
427              jdom.addContent(new Element("Bundle"));
428          }
429          if (this.action != null) {
430              jdom.addContent(new Element("action").setText(this.action));
431          } else {
432              jdom.addContent(new Element("action"));
433          }
434          if (this.sitemapNodeId != null) {
435              jdom.addContent(new Element("sitemapNodeId")
436                      .setText(this.sitemapNodeId.toString()));
437          } else {
438              jdom.addContent(new Element("sitemapNodeId"));
439          }
440          try {
441              if (!this.parameters.isEmpty()) {
442                  Element params = new Element("parameters");
443                  for (Parameter p : this.parameters) {
444                      params.addContent(p.deserializeToJDOM());
445                  }
446                  jdom.addContent(params);
447              }
448          } catch (LazyInitializationException e) {
449              LOGGER.trace(e.getLocalizedMessage());
450          }
451          return jdom;
452      }
453  
454      /**
455       * @return the bundleName
456       */
457      public final String getBundleName() {
458          return bundleName;
459      }
460  
461  }
462