001    /**
002     * ========================================
003     * JFreeReport : a free Java report library
004     * ========================================
005     *
006     * Project Info:  http://reporting.pentaho.org/
007     *
008     * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors.
009     *
010     * This library is free software; you can redistribute it and/or modify it under the terms
011     * of the GNU Lesser General Public License as published by the Free Software Foundation;
012     * either version 2.1 of the License, or (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015     * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016     * See the GNU Lesser General Public License for more details.
017     *
018     * You should have received a copy of the GNU Lesser General Public License along with this
019     * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020     * Boston, MA 02111-1307, USA.
021     *
022     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023     * in the United States and other countries.]
024     *
025     * ------------
026     * $Id: Element.java 2791 2007-05-14 08:55:52Z taqua $
027     * ------------
028     * (C) Copyright 2000-2005, by Object Refinery Limited.
029     * (C) Copyright 2005-2007, by Pentaho Corporation.
030     */
031    
032    package org.jfree.report.structure;
033    
034    import java.util.ArrayList;
035    import java.util.Arrays;
036    import java.util.HashMap;
037    import java.util.Locale;
038    import java.util.Map;
039    import java.util.Iterator;
040    import java.util.Collections;
041    
042    import org.jfree.layouting.input.style.CSSStyleRule;
043    import org.jfree.layouting.input.style.keys.box.BoxStyleKeys;
044    import org.jfree.layouting.input.style.values.CSSConstant;
045    import org.jfree.layouting.namespace.Namespaces;
046    import org.jfree.layouting.util.AttributeMap;
047    import org.jfree.layouting.util.LocaleUtility;
048    import org.jfree.report.JFreeReportInfo;
049    import org.jfree.report.expressions.Expression;
050    
051    /**
052     * An element is a node that can have attributes. The 'id' and the 'name'
053     * attribute is defined for all elements.
054     * <p/>
055     * Both the name and the id attribute may be null.
056     * <p/>
057     * Properties in the 'http://jfreereport.sourceforge.net/namespaces/engine/flow'
058     * namespace and in the 'http://jfreereport.sourceforge.net/namespaces/engine/compatibility'
059     * namespace are considered internal. You should only touch them, if you really
060     * know what you are doing.
061     *
062     * @author Thomas Morgner
063     */
064    public abstract class Element extends Node
065    {
066      private static final Expression[] EMPTY_EXPRESSIONS = new Expression[0];
067      private static final String[] EMPTY_STRINGS = new String[0];
068      private static final Map EMPTY_MAP = Collections.unmodifiableMap(new HashMap());
069      public static final String NAME_ATTRIBUTE = "name";
070      public static final String ID_ATTRIBUTE = "id";
071      /**
072       * The type corresponds (somewhat) to the tagname of HTML.
073       */
074      public static final String TYPE_ATTRIBUTE = "type";
075      /**
076       * See XML-Namespaces for the idea of that one ...
077       */
078      public static final String NAMESPACE_ATTRIBUTE = "namespace";
079      public static final String VIRTUAL_ATTRIBUTE = "virtual";
080    
081    
082      /**
083       * The name of the element.
084       */
085      private AttributeMap attributes;
086      private CSSStyleRule style;
087      private ArrayList expressions;
088      private AttributeMap attributeExpressions;
089      private HashMap styleExpressions;
090      private boolean enabled;
091      private boolean virtual;
092      private Expression displayCondition;
093    
094      /**
095       * Constructs an element.
096       * <p/>
097       * The element inherits the element's defined default ElementStyleSheet to
098       * provide reasonable default values for common stylekeys. When the element is
099       * added to the band, the bands stylesheet is set as parent to the element's
100       * stylesheet.
101       * <p/>
102       * A datasource is assigned with this element is set to a default source,
103       * which always returns null.
104       */
105      protected Element()
106      {
107        this.style = new CSSStyleRule(null, null);
108        this.attributes = new AttributeMap();
109        this.enabled = true;
110        setNamespace(JFreeReportInfo.REPORT_NAMESPACE);
111      }
112    
113      public String getNamespace()
114      {
115        return (String) getAttribute
116            (JFreeReportInfo.REPORT_NAMESPACE, Element.NAMESPACE_ATTRIBUTE);
117      }
118    
119      public void setNamespace(final String id)
120      {
121        setAttribute
122            (JFreeReportInfo.REPORT_NAMESPACE, Element.NAMESPACE_ATTRIBUTE, id);
123      }
124    
125      public String getId()
126      {
127        return (String) getAttribute
128            (Namespaces.XML_NAMESPACE, Element.ID_ATTRIBUTE);
129      }
130    
131      public void setId(final String id)
132      {
133        setAttribute(Namespaces.XML_NAMESPACE, Element.ID_ATTRIBUTE, id);
134      }
135    
136      public String getType()
137      {
138        return (String) getAttribute
139            (JFreeReportInfo.REPORT_NAMESPACE, Element.TYPE_ATTRIBUTE);
140      }
141    
142      public void setType(final String type)
143      {
144        setAttribute
145            (JFreeReportInfo.REPORT_NAMESPACE, Element.TYPE_ATTRIBUTE, type);
146      }
147    
148      /**
149       * Defines the name for this Element. The name must not be empty, or a
150       * NullPointerException is thrown.
151       * <p/>
152       * Names can be used to lookup an element within a band. There is no
153       * requirement for element names to be unique.
154       *
155       * @param name the name of this element
156       */
157      public void setName(final String name)
158      {
159        setAttribute(Namespaces.XML_NAMESPACE, Element.NAME_ATTRIBUTE, name);
160      }
161    
162    
163      /**
164       * Returns the name of the Element. The name of the Element is never null.
165       *
166       * @return the name.
167       */
168      public String getName()
169      {
170        return (String) getAttribute
171            (Namespaces.XML_NAMESPACE, Element.NAME_ATTRIBUTE);
172      }
173    
174      public void setAttribute(final String name, final Object value)
175      {
176        setAttribute(getNamespace(), name, value);
177      }
178    
179      public void setAttribute(final String namespace,
180                               final String name,
181                               final Object value)
182      {
183        if (name == null)
184        {
185          throw new NullPointerException();
186        }
187        if (attributes == null)
188        {
189          this.attributes = new AttributeMap();
190        }
191        this.attributes.setAttribute(namespace, name, value);
192      }
193    
194      public Object getAttribute(final String name)
195      {
196        return getAttribute(getNamespace(), name);
197      }
198    
199      public Object getAttribute(final String namespace, final String name)
200      {
201        if (this.attributes == null)
202        {
203          return null;
204        }
205        return this.attributes.getAttribute(namespace, name);
206      }
207    
208      public Map getAttributes(final String namespace)
209      {
210        if (this.attributes == null)
211        {
212          return null;
213        }
214        return this.attributes.getAttributes(namespace);
215      }
216    
217      public AttributeMap getAttributeMap()
218      {
219        return new AttributeMap(this.attributes);
220      }
221    
222      public String[] getAttributeNameSpaces()
223      {
224        if (this.attributes == null)
225        {
226          return Element.EMPTY_STRINGS;
227        }
228        return this.attributes.getNameSpaces();
229      }
230    
231      /**
232       * Returns this elements private stylesheet. This sheet can be used to
233       * override the default values set in one of the parent-stylesheets.
234       *
235       * @return the Element's stylesheet
236       */
237      public CSSStyleRule getStyle()
238      {
239        return style;
240      }
241    
242      public void setVisibility(final CSSConstant v)
243      {
244        getStyle().setPropertyValue(BoxStyleKeys.VISIBILITY, v);
245      }
246    
247    
248      public CSSConstant getVisibility()
249      {
250        return (CSSConstant) getStyle().getPropertyCSSValue(BoxStyleKeys.VISIBILITY);
251      }
252    
253      public void setAttributeExpression(final String attr,
254                                         final Expression function)
255      {
256        setAttribute(getNamespace(), attr, function);
257      }
258    
259      /**
260       * Adds a function to the report's collection of expressions.
261       *
262       * @param namespace
263       * @param attr
264       * @param function  the function.
265       */
266      public void setAttributeExpression(final String namespace,
267                                         final String attr,
268                                         final Expression function)
269      {
270    
271        if (attributeExpressions == null)
272        {
273          if (function == null)
274          {
275            return;
276          }
277          this.attributeExpressions = new AttributeMap();
278        }
279        attributeExpressions.setAttribute(namespace, attr, function);
280      }
281    
282      /**
283       * Returns the expressions for the report.
284       *
285       * @param attr
286       * @return the expressions.
287       */
288      public Expression getAttributeExpression(final String attr)
289      {
290        return getAttributeExpression(getNamespace(), attr);
291      }
292    
293      public Expression getAttributeExpression(final String namespace,
294                                               final String attr)
295      {
296        if (attributeExpressions == null)
297        {
298          return null;
299        }
300        return (Expression) attributeExpressions.getAttribute(namespace, attr);
301      }
302    
303      public Map getAttributeExpressions(final String namespace)
304      {
305        if (attributeExpressions == null)
306        {
307          return null;
308        }
309        return attributeExpressions.getAttributes(namespace);
310      }
311    
312      public AttributeMap getAttributeExpressionMap()
313      {
314        if (this.attributeExpressions == null)
315        {
316          return new AttributeMap();
317        }
318    
319        return new AttributeMap(this.attributeExpressions);
320      }
321    
322    
323      /**
324       * Adds a function to the report's collection of expressions.
325       *
326       * @param function the function.
327       * @param property
328       */
329      public void setStyleExpression(final String property,
330                                     final Expression function)
331      {
332        if (function == null)
333        {
334          if (styleExpressions != null)
335          {
336            styleExpressions.remove(property);
337          }
338        }
339        else
340        {
341          if (styleExpressions == null)
342          {
343            styleExpressions = new HashMap();
344          }
345          styleExpressions.put(property, function);
346        }
347      }
348    
349      /**
350       * Returns the expressions for the report.
351       *
352       * @param property
353       * @return the expressions.
354       */
355      public Expression getStyleExpression(final String property)
356      {
357        if (styleExpressions == null)
358        {
359          return null;
360        }
361        return (Expression) styleExpressions.get(property);
362      }
363    
364      public Map getStyleExpressions()
365      {
366        if (styleExpressions == null)
367        {
368          return Element.EMPTY_MAP;
369        }
370        return Collections.unmodifiableMap(styleExpressions);
371      }
372    
373      /**
374       * Adds a function to the report's collection of expressions.
375       *
376       * @param function the function.
377       */
378      public void addExpression(final Expression function)
379      {
380        if (expressions == null)
381        {
382          expressions = new ArrayList();
383        }
384        expressions.add(function);
385      }
386    
387      /**
388       * Returns the expressions for the report.
389       *
390       * @return the expressions.
391       */
392      public Expression[] getExpressions()
393      {
394        if (expressions == null)
395        {
396          return Element.EMPTY_EXPRESSIONS;
397        }
398        return (Expression[]) expressions.toArray
399            (new Expression[expressions.size()]);
400      }
401    
402      /**
403       * Sets the expressions for the report.
404       *
405       * @param expressions the expressions (<code>null</code> not permitted).
406       */
407      public void setExpressions(final Expression[] expressions)
408      {
409        if (expressions == null)
410        {
411          throw new NullPointerException(
412              "JFreeReport.setExpressions(...) : null not permitted.");
413        }
414        if (this.expressions == null)
415        {
416          this.expressions = new ArrayList(expressions.length);
417        }
418        else
419        {
420          this.expressions.clear();
421        }
422        this.expressions.addAll(Arrays.asList(expressions));
423      }
424    
425      /**
426       * Returns true, if the element is enabled.
427       *
428       * @return true or false
429       */
430      public boolean isEnabled()
431      {
432        return enabled;
433      }
434    
435      /**
436       * Defines whether the element is enabled. Disabled elements will be fully
437       * ignored by the report processor. This is a design time property to exclude
438       * elements from the processing without actually having to deal with the other
439       * complex properties.
440       *
441       * @param enabled
442       */
443      public void setEnabled(final boolean enabled)
444      {
445        this.enabled = enabled;
446      }
447    
448      public Expression getDisplayCondition()
449      {
450        return displayCondition;
451      }
452    
453      public void setDisplayCondition(final Expression displayCondition)
454      {
455        this.displayCondition = displayCondition;
456      }
457    
458      public Locale getLocale()
459      {
460        final Locale locale = getLocaleFromAttributes();
461        if (locale != null)
462        {
463          return locale;
464        }
465        return super.getLocale();
466      }
467    
468      protected Locale getLocaleFromAttributes()
469      {
470        final Object mayBeXmlLang = getAttribute(Namespaces.XML_NAMESPACE, "lang");
471        if (mayBeXmlLang instanceof String)
472        {
473          return LocaleUtility.createLocale((String) mayBeXmlLang);
474        }
475        else if (mayBeXmlLang instanceof Locale)
476        {
477          return (Locale) mayBeXmlLang;
478        }
479    
480        final Object mayBeXhtmlLang = getAttribute(Namespaces.XHTML_NAMESPACE,
481            "lang");
482        if (mayBeXhtmlLang instanceof String)
483        {
484          return LocaleUtility.createLocale((String) mayBeXhtmlLang);
485        }
486        else if (mayBeXhtmlLang instanceof Locale)
487        {
488          return (Locale) mayBeXhtmlLang;
489        }
490    //
491    //    final Object mayBeHtmlLang = getAttribute(Namespaces.XHTML_NAMESPACE, "lang");
492    //    if (mayBeHtmlLang instanceof String)
493    //    {
494    //      return LocaleUtility.createLocale((String) mayBeHtmlLang);
495    //    }
496    //    else if (mayBeHtmlLang instanceof Locale)
497    //    {
498    //      return (Locale) mayBeHtmlLang;
499    //    }
500    
501        return null;
502      }
503    
504      public boolean isVirtual()
505      {
506        return virtual;
507      }
508    
509      public void setVirtual(final boolean virtual)
510      {
511        this.virtual = virtual;
512      }
513    
514    
515      public Object clone()
516          throws CloneNotSupportedException
517      {
518        final Element element = (Element) super.clone();
519        element.style = (CSSStyleRule) style.clone();
520        if (attributes != null)
521        {
522          element.attributes = (AttributeMap) attributes.clone();
523        }
524    
525        if (attributeExpressions != null)
526        {
527          element.attributeExpressions = (AttributeMap) attributeExpressions.clone();
528          final String[] namespaces = element.attributeExpressions.getNameSpaces();
529          for (int i = 0; i < namespaces.length; i++)
530          {
531            final String namespace = namespaces[i];
532            final Map attrsNs = element.attributeExpressions.getAttributes(
533                namespace);
534            final Iterator it =
535                attrsNs.entrySet().iterator();
536            while (it.hasNext())
537            {
538              final Map.Entry entry = (Map.Entry) it.next();
539              final Expression exp = (Expression) entry.getValue();
540              entry.setValue(exp.clone());
541            }
542          }
543        }
544    
545        if (expressions != null)
546        {
547          element.expressions = (ArrayList) expressions.clone();
548          element.expressions.clear();
549          for (int i = 0; i < expressions.size(); i++)
550          {
551            final Expression expression = (Expression) expressions.get(i);
552            element.expressions.add(expression.clone());
553          }
554        }
555        if (styleExpressions != null)
556        {
557          element.styleExpressions = (HashMap) styleExpressions.clone();
558          final Iterator styleExpressionsIt =
559              element.styleExpressions.entrySet().iterator();
560          while (styleExpressionsIt.hasNext())
561          {
562            final Map.Entry entry = (Map.Entry) styleExpressionsIt.next();
563            final Expression exp = (Expression) entry.getValue();
564            entry.setValue(exp.clone());
565          }
566        }
567    
568        if (displayCondition != null)
569        {
570          element.displayCondition = (Expression) displayCondition.clone();
571        }
572        return element;
573      }
574    }