1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 package com.sun.star.report.pentaho.output;
24 
25 import com.sun.star.report.pentaho.OfficeNamespaces;
26 import com.sun.star.report.pentaho.model.DataStyle;
27 import com.sun.star.report.pentaho.model.FontFaceDeclsSection;
28 import com.sun.star.report.pentaho.model.FontFaceElement;
29 import com.sun.star.report.pentaho.model.OfficeStyle;
30 import com.sun.star.report.pentaho.model.OfficeStyles;
31 import com.sun.star.report.pentaho.model.OfficeStylesCollection;
32 
33 import java.util.ArrayList;
34 import java.util.HashSet;
35 import java.util.Set;
36 
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39 
40 import org.jfree.report.ReportProcessingException;
41 import org.jfree.report.structure.Element;
42 import org.jfree.report.structure.Section;
43 import org.jfree.report.util.AttributeNameGenerator;
44 
45 
46 /**
47  * Todo: Document me!
48  *
49  * @author Thomas Morgner
50  * @since 13.03.2007
51  */
52 public class StyleUtilities
53 {
54 
55     private static final Log LOGGER = LogFactory.getLog(StyleUtilities.class);
56     private static final String STYLE = "style";
57 
StyleUtilities()58     private StyleUtilities()
59     {
60     }
61 
62     /**
63      * Copies the specififed style (keyed by its family and name) into the current styles collection. This copies the
64      * style and all inherited styles into the target collection. Inherited common styles will be always be added to the
65      * common collection (which will be written into the 'styles.xml' later).
66      * <p/>
67      * This method does nothing if the specified style already exists in the styles collection.
68      *
69      * @param styleFamily      the family of the style to copy
70      * @param styleName        the unique name of the style.
71      * @param stylesCollection the current styles collection
72      * @param commonCollection the global styles collection
73      * @param predefCollection the predefined styles from where to copy the styles.
74      * @throws ReportProcessingException if the style copying failed.
75      */
copyStyle(final String styleFamily, final String styleName, final OfficeStylesCollection stylesCollection, final OfficeStylesCollection commonCollection, final OfficeStylesCollection predefCollection)76     public static void copyStyle(final String styleFamily,
77             final String styleName,
78             final OfficeStylesCollection stylesCollection,
79             final OfficeStylesCollection commonCollection,
80             final OfficeStylesCollection predefCollection)
81             throws ReportProcessingException
82     {
83         copyStyle(styleFamily, styleName, stylesCollection,
84                 commonCollection, predefCollection, new HashSet());
85     }
86 
87     /**
88      * Copies the specififed style (keyed by its family and name) into the current styles collection. This copies the
89      * style and all inherited styles into the target collection. Inherited common styles will be always be added to the
90      * common collection (which will be written into the 'styles.xml' later).
91      * <p/>
92      * This method does nothing if the specified style already exists in the styles collection.
93      *
94      * @param styleFamily        the family of the style to copy
95      * @param styleName          the unique name of the style.
96      * @param stylesCollection   the current styles collection
97      * @param commonCollection   the global styles collection
98      * @param predefCollection   the predefined styles from where to copy the styles.
99      * @param inheritanceTracker a collection of all styles that have been touched. This is used to prevent infinite
100      *                           loops and duplicates.
101      * @throws ReportProcessingException if the style copying failed.
102      */
copyStyle(final String styleFamily, final String styleName, final OfficeStylesCollection stylesCollection, final OfficeStylesCollection commonCollection, final OfficeStylesCollection predefCollection, final Set inheritanceTracker)103     private static void copyStyle(final String styleFamily,
104             final String styleName,
105             final OfficeStylesCollection stylesCollection,
106             final OfficeStylesCollection commonCollection,
107             final OfficeStylesCollection predefCollection,
108             final Set inheritanceTracker)
109             throws ReportProcessingException
110     {
111         if (inheritanceTracker.contains(styleName))
112         {
113             return;
114         }
115         inheritanceTracker.add(styleName);
116 
117         if (stylesCollection.containsStyle(styleFamily, styleName) || commonCollection.getCommonStyles().containsStyle(styleFamily, styleName))
118         {
119             // fine, there's already a copy of the stylesheet.
120             return;
121         }
122 
123         final OfficeStyle predefCommonStyle =
124                 predefCollection.getCommonStyles().getStyle(styleFamily, styleName);
125         if (predefCommonStyle != null)
126         {
127             // so we have an style from the predefined collection.
128             // copy it an add it to the current stylescollection
129             final OfficeStyles commonStyles = commonCollection.getCommonStyles();
130 
131             copyStyleInternal(predefCommonStyle, commonStyles, stylesCollection,
132                     commonCollection, predefCollection, styleFamily, inheritanceTracker);
133             return;
134         }
135 
136         final OfficeStyle predefAutoStyle =
137                 predefCollection.getAutomaticStyles().getStyle(styleFamily, styleName);
138         if (predefAutoStyle != null)
139         {
140             // so we have an style from the predefined collection.
141             // copy it an add it to the current stylescollection
142             final OfficeStyles autoStyles = stylesCollection.getAutomaticStyles();
143             copyStyleInternal(predefAutoStyle, autoStyles, stylesCollection,
144                     commonCollection, predefCollection, styleFamily, inheritanceTracker);
145             return;
146         }
147 
148         // There is no automatic style either. Now this means that someone
149         // messed up the fileformat. Lets create a new empty style for this.
150         final OfficeStyle autostyle = new OfficeStyle();
151         autostyle.setNamespace(OfficeNamespaces.STYLE_NS);
152         autostyle.setType(STYLE);
153         autostyle.setStyleFamily(styleFamily);
154         autostyle.setStyleName(styleName);
155 
156         final OfficeStyles autoStyles = stylesCollection.getAutomaticStyles();
157         autoStyles.addStyle(autostyle);
158     }
159 
copyStyleInternal( final OfficeStyle predefCommonStyle, final OfficeStyles styles, final OfficeStylesCollection stylesCollection, final OfficeStylesCollection commonCollection, final OfficeStylesCollection predefCollection, final String styleFamily, final Set inheritanceTracker)160     private static OfficeStyle copyStyleInternal(
161             final OfficeStyle predefCommonStyle,
162             final OfficeStyles styles,
163             final OfficeStylesCollection stylesCollection,
164             final OfficeStylesCollection commonCollection,
165             final OfficeStylesCollection predefCollection,
166             final String styleFamily,
167             final Set inheritanceTracker)
168             throws ReportProcessingException
169     {
170         try
171         {
172             final OfficeStyle preStyle = (OfficeStyle) predefCommonStyle.clone();
173             styles.addStyle(preStyle);
174             performFontFaceProcessing(preStyle, stylesCollection, predefCollection);
175             performDataStyleProcessing(preStyle, stylesCollection, predefCollection);
176 
177             // Lookup the parent style ..
178             final String styleParent = preStyle.getStyleParent();
179             final OfficeStyle inherited =
180                     stylesCollection.getStyle(styleFamily, styleParent);
181             if (inherited != null)
182             {
183                 // OK, recurse (and hope that we dont run into an infinite loop) ..
184                 copyStyle(styleFamily, styleParent, stylesCollection,
185                         commonCollection, predefCollection, inheritanceTracker);
186             }
187             else if (styleParent != null)
188             {
189                 LOGGER.warn("Inconsistent styles: " + styleFamily + ":" + styleParent + " does not exist.");
190             }
191             return preStyle;
192         }
193         catch (CloneNotSupportedException e)
194         {
195             throw new ReportProcessingException("Failed to derive a stylesheet", e);
196         }
197     }
198 
performFontFaceProcessing(final OfficeStyle style, final OfficeStylesCollection stylesCollection, final OfficeStylesCollection predefCollection)199     private static void performFontFaceProcessing(final OfficeStyle style,
200             final OfficeStylesCollection stylesCollection,
201             final OfficeStylesCollection predefCollection)
202             throws ReportProcessingException
203     {
204         final Element textProperties = style.getTextProperties();
205         if (textProperties == null)
206         {
207             return;
208         }
209 
210         try
211         {
212             final FontFaceDeclsSection currentFonts = stylesCollection.getFontFaceDecls();
213             final FontFaceDeclsSection predefFonts = predefCollection.getFontFaceDecls();
214 
215             final String fontName = (String) textProperties.getAttribute(OfficeNamespaces.STYLE_NS, "font-name");
216             if (fontName != null && !currentFonts.containsFont(fontName))
217             {
218                 final FontFaceElement element = predefFonts.getFontFace(fontName);
219                 if (element != null)
220                 {
221                     currentFonts.addFontFace((FontFaceElement) element.clone());
222                 }
223             }
224 
225             final String fontNameAsian = (String) textProperties.getAttribute(OfficeNamespaces.STYLE_NS,
226                     "font-name-asian");
227             if (fontNameAsian != null && !currentFonts.containsFont(fontNameAsian))
228             {
229                 final FontFaceElement element = predefFonts.getFontFace(
230                         fontNameAsian);
231                 if (element != null)
232                 {
233                     currentFonts.addFontFace((FontFaceElement) element.clone());
234                 }
235             }
236 
237             final String fontNameComplex = (String) textProperties.getAttribute(OfficeNamespaces.STYLE_NS,
238                     "font-name-complex");
239             if (fontNameComplex != null && !currentFonts.containsFont(fontNameComplex))
240             {
241                 final FontFaceElement element = predefFonts.getFontFace(
242                         fontNameComplex);
243                 if (element != null)
244                 {
245                     currentFonts.addFontFace((FontFaceElement) element.clone());
246                 }
247             }
248         }
249         catch (CloneNotSupportedException e)
250         {
251             throw new ReportProcessingException("Failed to clone font-face element");
252         }
253     }
254 
performDataStyleProcessing(final OfficeStyle style, final OfficeStylesCollection stylesCollection, final OfficeStylesCollection predefCollection)255     private static void performDataStyleProcessing(final OfficeStyle style,
256             final OfficeStylesCollection stylesCollection,
257             final OfficeStylesCollection predefCollection)
258             throws ReportProcessingException
259     {
260         final Section derivedStyle = performDataStyleProcessing(style, stylesCollection, predefCollection, "data-style-name");
261         if (derivedStyle != null)
262         {
263             try
264             {
265                 final Section styleMap = (Section) derivedStyle.findFirstChild(OfficeNamespaces.STYLE_NS, "map");
266                 if (styleMap != null)
267                 {
268                     performDataStyleProcessing(styleMap, stylesCollection, predefCollection, "apply-style-name");
269                 }
270             }
271             catch (Exception e)
272             {
273             }
274         }
275     }
276 
performDataStyleProcessing(final Section style, final OfficeStylesCollection stylesCollection, final OfficeStylesCollection predefCollection, final String attributeName)277     private static Section performDataStyleProcessing(final Section style,
278             final OfficeStylesCollection stylesCollection,
279             final OfficeStylesCollection predefCollection,
280             final String attributeName)
281             throws ReportProcessingException
282     {
283         final Object attribute = style.getAttribute(OfficeNamespaces.STYLE_NS, attributeName);
284         final DataStyle derivedStyle;
285         if (attribute != null)
286         {
287             final String styleName = String.valueOf(attribute);
288             if (!stylesCollection.getAutomaticStyles().containsDataStyle(styleName) && !stylesCollection.getCommonStyles().containsDataStyle(styleName))
289             {
290                 try
291                 {
292                     final OfficeStyles automaticStyles = predefCollection.getAutomaticStyles();
293                     final DataStyle autoDataStyle = automaticStyles.getDataStyle(styleName);
294                     if (autoDataStyle != null)
295                     {
296                         derivedStyle = (DataStyle) autoDataStyle.clone();
297                         stylesCollection.getAutomaticStyles().addDataStyle(derivedStyle);
298                     }
299                     else
300                     {
301                         final OfficeStyles commonStyles = predefCollection.getCommonStyles();
302                         final DataStyle commonDataStyle = commonStyles.getDataStyle(styleName);
303                         if (commonDataStyle != null)
304                         {
305                             derivedStyle = (DataStyle) commonDataStyle.clone();
306                             stylesCollection.getCommonStyles().addDataStyle(derivedStyle);
307                         }
308                         else
309                         {
310                             LOGGER.warn("Dangling data style: " + styleName);
311                             derivedStyle = null;
312                         }
313                     }
314                 }
315                 catch (CloneNotSupportedException e)
316                 {
317                     throw new ReportProcessingException("Failed to copy style. This should not have happened.");
318                 }
319             }
320             else
321             {
322                 derivedStyle = null;
323             }
324         }
325         else
326         {
327             derivedStyle = null;
328         }
329         return derivedStyle;
330     }
331 
332     /**
333      * Derives the named style. If the style is a common style, a new automatic style is generated and inserted into the
334      * given stylesCollection. If the named style is an automatic style, the style is copied and inserted as new automatic
335      * style.
336      * <p/>
337      * After the style has been created, the style's inheritance hierarchy will be copied as well.
338      * <p/>
339      * If there is no style with the given name and family, a new empty automatic style will be created.
340      *
341      * @param styleFamily      the family of the style to copy
342      * @param styleName        the unique name of the style.
343      * @param stylesCollection the current styles collection
344      * @param commonCollection the global styles collection
345      * @param predefCollection the predefined styles from where to copy the styles.
346      * @param generator        the style-name-generator of the current report-target
347      * @return the derived style instance.
348      * @throws ReportProcessingException if the style copying failed.
349      */
deriveStyle(final String styleFamily, final String styleName, final OfficeStylesCollection stylesCollection, final OfficeStylesCollection commonCollection, final OfficeStylesCollection predefCollection, final AttributeNameGenerator generator)350     public static OfficeStyle deriveStyle(final String styleFamily,
351             final String styleName,
352             final OfficeStylesCollection stylesCollection,
353             final OfficeStylesCollection commonCollection,
354             final OfficeStylesCollection predefCollection,
355             final AttributeNameGenerator generator)
356             throws ReportProcessingException
357     {
358         if (styleFamily == null)
359         {
360             throw new NullPointerException("StyleFamily must not be null");
361         }
362         if (styleName != null)
363         {
364 
365             final OfficeStyle currentAuto =
366                     stylesCollection.getAutomaticStyles().getStyle(styleFamily,
367                     styleName);
368             if (currentAuto != null)
369             {
370                 // handle an automatic style ..
371                 final OfficeStyle derivedStyle =
372                         deriveAutomaticStyle(currentAuto, styleFamily, styleName,
373                         generator, commonCollection, predefCollection);
374                 stylesCollection.getAutomaticStyles().addStyle(derivedStyle);
375                 return derivedStyle;
376             }
377 
378             final OfficeStyle currentCommon =
379                     stylesCollection.getCommonStyles().getStyle(styleFamily, styleName);
380             if (currentCommon != null)
381             {
382                 // handle an common style ..
383                 final OfficeStyle derivedStyle =
384                         deriveCommonStyle(currentCommon, styleFamily, styleName,
385                         generator, commonCollection, predefCollection);
386                 stylesCollection.getAutomaticStyles().addStyle(derivedStyle);
387                 return derivedStyle;
388             }
389 
390 //      final OfficeStyle commonAuto =
391 //          commonCollection.getAutomaticStyles().getStyle(styleFamily,
392 //              styleName);
393 //      if (commonAuto != null)
394 //      {
395 //        // handle an automatic style ..
396 //        final OfficeStyle derivedStyle =
397 //            deriveAutomaticStyle(commonAuto, styleFamily, styleName,
398 //                generator, commonCollection, predefCollection);
399 //        stylesCollection.getAutomaticStyles().addStyle(derivedStyle);
400 //        return derivedStyle;
401 //      }
402 
403             final OfficeStyle commonCommon =
404                     commonCollection.getCommonStyles().getStyle(styleFamily, styleName);
405             if (commonCommon != null)
406             {
407                 // handle an common style ..
408                 final OfficeStyle derivedStyle =
409                         deriveCommonStyle(commonCommon, styleFamily, styleName,
410                         generator, commonCollection, predefCollection);
411                 stylesCollection.getAutomaticStyles().addStyle(derivedStyle);
412                 return derivedStyle;
413             }
414 
415             final OfficeStyle predefAuto =
416                     predefCollection.getAutomaticStyles().getStyle(styleFamily,
417                     styleName);
418             if (predefAuto != null)
419             {
420                 // handle an automatic style ..
421                 final OfficeStyle derivedStyle =
422                         deriveAutomaticStyle(predefAuto, styleFamily, styleName,
423                         generator, commonCollection, predefCollection);
424                 stylesCollection.getAutomaticStyles().addStyle(derivedStyle);
425                 return derivedStyle;
426             }
427 
428             final OfficeStyle predefCommon =
429                     predefCollection.getCommonStyles().getStyle(styleFamily, styleName);
430             if (predefCommon != null)
431             {
432                 // handle an common style ..
433                 final OfficeStyle derivedStyle =
434                         deriveCommonStyle(predefCommon, styleFamily, styleName,
435                         generator, commonCollection, predefCollection);
436                 stylesCollection.getAutomaticStyles().addStyle(derivedStyle);
437                 return derivedStyle;
438             }
439         }
440 
441         // No such style. Create a new one ..
442         final OfficeStyle autostyle = new OfficeStyle();
443         autostyle.setNamespace(OfficeNamespaces.STYLE_NS);
444         autostyle.setType(STYLE);
445         autostyle.setStyleFamily(styleFamily);
446         if (styleName != null)
447         {
448             autostyle.setStyleName(styleName);
449         }
450         else
451         {
452             autostyle.setStyleName(generator.generateName("derived_anonymous"));
453         }
454 
455         final OfficeStyles autoStyles = stylesCollection.getAutomaticStyles();
456         autoStyles.addStyle(autostyle);
457         return autostyle;
458     }
459 
deriveCommonStyle(final OfficeStyle commonStyle, final String styleFamily, final String styleName, final AttributeNameGenerator nameGenerator, final OfficeStylesCollection commonCollection, final OfficeStylesCollection predefCollection)460     private static OfficeStyle deriveCommonStyle(final OfficeStyle commonStyle,
461             final String styleFamily,
462             final String styleName,
463             final AttributeNameGenerator nameGenerator,
464             final OfficeStylesCollection commonCollection,
465             final OfficeStylesCollection predefCollection)
466             throws ReportProcessingException
467     {
468         final OfficeStyle autostyle = new OfficeStyle();
469         autostyle.setNamespace(OfficeNamespaces.STYLE_NS);
470         autostyle.setType(STYLE);
471         autostyle.setStyleFamily(styleFamily);
472         autostyle.setStyleName(nameGenerator.generateName("derived_" + styleName));
473         autostyle.setStyleParent(styleName);
474 
475         // now copy the common style ..
476         final OfficeStyles commonStyles = commonCollection.getCommonStyles();
477         if (!commonStyles.containsStyle(styleFamily, styleName))
478         {
479             copyStyleInternal(commonStyle, commonStyles,
480                     commonCollection, commonCollection, predefCollection,
481                     styleFamily, new HashSet());
482         }
483         return autostyle;
484     }
485 
deriveAutomaticStyle(final OfficeStyle commonStyle, final String styleFamily, final String styleName, final AttributeNameGenerator nameGenerator, final OfficeStylesCollection commonCollection, final OfficeStylesCollection predefCollection)486     private static OfficeStyle deriveAutomaticStyle(final OfficeStyle commonStyle,
487             final String styleFamily,
488             final String styleName,
489             final AttributeNameGenerator nameGenerator,
490             final OfficeStylesCollection commonCollection,
491             final OfficeStylesCollection predefCollection)
492             throws ReportProcessingException
493     {
494         try
495         {
496             final OfficeStyle autostyle = (OfficeStyle) commonStyle.clone();
497             autostyle.setNamespace(OfficeNamespaces.STYLE_NS);
498             autostyle.setType(STYLE);
499             autostyle.setStyleFamily(styleFamily);
500             autostyle.setStyleName(nameGenerator.generateName("derived_auto_" + styleName));
501 
502 
503             final String parent = autostyle.getStyleParent();
504             if (parent != null)
505             {
506                 copyStyle(styleFamily, parent, commonCollection, commonCollection,
507                         predefCollection);
508             }
509             return autostyle;
510         }
511         catch (CloneNotSupportedException e)
512         {
513             throw new ReportProcessingException(
514                     "Deriving the style failed. Clone error: ", e);
515         }
516     }
517 
queryStyle(final OfficeStylesCollection predefCollection, final String styleFamily, final String styleName, final String sectionName, final String propertyNamespace, final String propertyName)518     public static String queryStyle(final OfficeStylesCollection predefCollection,
519             final String styleFamily,
520             final String styleName,
521             final String sectionName,
522             final String propertyNamespace,
523             final String propertyName)
524     {
525         return queryStyle(predefCollection, styleFamily,
526                 styleName, sectionName, propertyNamespace, propertyName, new HashSet());
527     }
528 
queryStyleByProperties(final OfficeStylesCollection predefCollection, final String styleFamily, final String sectionName, final ArrayList propertyNamespace, final ArrayList propertyName, final ArrayList propertyValues)529     public static OfficeStyle queryStyleByProperties(final OfficeStylesCollection predefCollection,
530             final String styleFamily,
531             final String sectionName,
532             final ArrayList propertyNamespace,
533             final ArrayList propertyName,
534             final ArrayList propertyValues)
535     {
536         if (propertyNamespace.size() != propertyName.size())
537         {
538             return null;
539         }
540         final OfficeStyle[] styles = predefCollection.getAutomaticStyles().getAllStyles();
541         for (int i = 0; i < styles.length; i++)
542         {
543             final OfficeStyle officeStyle = styles[i];
544             if (officeStyle.getStyleFamily().equals(styleFamily))
545             {
546                 final Element section = officeStyle.findFirstChild(OfficeNamespaces.STYLE_NS, sectionName);
547                 if (section != null)
548                 {
549                     int j = 0;
550                     for (; j < propertyNamespace.size(); j++)
551                     {
552                         final String ns = (String) propertyNamespace.get(j);
553                         final String prop = (String) propertyName.get(j);
554                         final Object obj = section.getAttribute(ns, prop);
555                         final Object value = propertyValues.get(j);
556                         if (obj == null || value == null)
557                         {
558                             continue;
559                         }
560                         if (!propertyValues.get(j).equals(obj))
561                         {
562                             break;
563                         }
564                     }
565                     if (j == propertyName.size())
566                     {
567                         return officeStyle;
568                     }
569                 }
570             }
571         }
572         return null;
573     }
574 
queryStyle(final OfficeStylesCollection predefCollection, final String styleFamily, final String styleName, final String sectionName, final String propertyNamespace, final String propertyName, final Set seenStyles)575     private static String queryStyle(final OfficeStylesCollection predefCollection,
576             final String styleFamily,
577             final String styleName,
578             final String sectionName,
579             final String propertyNamespace,
580             final String propertyName,
581             final Set seenStyles)
582     {
583         if (seenStyles.contains(styleName))
584         {
585             return null;
586         }
587         seenStyles.add(styleName);
588 
589         final OfficeStyle style = predefCollection.getStyle(styleFamily, styleName);
590         if (style == null)
591         {
592             return null; // no such style
593 
594         }
595         final Element section = style.findFirstChild(OfficeNamespaces.STYLE_NS, sectionName);
596         if (section != null)
597         {
598             final Object attribute = section.getAttribute(propertyNamespace, propertyName);
599             if (attribute != null)
600             {
601                 return String.valueOf(attribute);
602             }
603         }
604         final String parent = style.getStyleParent();
605         if (parent == null)
606         {
607             return null;
608         }
609         return queryStyle(predefCollection, styleFamily, parent, sectionName, propertyNamespace, propertyName, seenStyles);
610     }
611 }
612