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: LayoutControllerUtil.java 3048 2007-07-28 18:02:42Z tmorgner $
027: * ------------
028: * (C) Copyright 2000-2005, by Object Refinery Limited.
029: * (C) Copyright 2005-2007, by Pentaho Corporation.
030: */package org.jfree.report.flow.layoutprocessor;
031:
032: import java.util.Iterator;
033: import java.util.Map;
034:
035: import org.jfree.layouting.input.style.CSSDeclarationRule;
036: import org.jfree.layouting.input.style.CSSStyleRule;
037: import org.jfree.layouting.input.style.StyleKey;
038: import org.jfree.layouting.input.style.StyleKeyRegistry;
039: import org.jfree.layouting.input.style.StyleRule;
040: import org.jfree.layouting.input.style.values.CSSValue;
041: import org.jfree.layouting.namespace.NamespaceDefinition;
042: import org.jfree.layouting.namespace.Namespaces;
043: import org.jfree.layouting.util.AttributeMap;
044: import org.jfree.report.DataSourceException;
045: import org.jfree.report.EmptyReportData;
046: import org.jfree.report.JFreeReportInfo;
047: import org.jfree.report.ReportDataFactoryException;
048: import org.jfree.report.ReportProcessingException;
049: import org.jfree.report.data.GlobalMasterRow;
050: import org.jfree.report.data.PrecomputeNode;
051: import org.jfree.report.data.PrecomputeNodeKey;
052: import org.jfree.report.data.PrecomputedValueRegistry;
053: import org.jfree.report.data.ReportDataRow;
054: import org.jfree.report.data.StaticExpressionRuntimeData;
055: import org.jfree.report.expressions.Expression;
056: import org.jfree.report.expressions.ExpressionRuntime;
057: import org.jfree.report.flow.EmptyReportTarget;
058: import org.jfree.report.flow.FlowControlOperation;
059: import org.jfree.report.flow.FlowController;
060: import org.jfree.report.flow.LayoutExpressionRuntime;
061: import org.jfree.report.flow.ReportContext;
062: import org.jfree.report.flow.ReportJob;
063: import org.jfree.report.flow.ReportStructureRoot;
064: import org.jfree.report.flow.ReportTarget;
065: import org.jfree.report.structure.Element;
066: import org.jfree.report.structure.Group;
067: import org.jfree.report.structure.Node;
068: import org.jfree.report.structure.Section;
069: import org.jfree.resourceloader.Resource;
070: import org.jfree.resourceloader.ResourceKey;
071: import org.jfree.resourceloader.ResourceManager;
072:
073: /**
074: * Creation-Date: 24.11.2006, 15:01:22
075: *
076: * @author Thomas Morgner
077: */
078: public class LayoutControllerUtil {
079: public static final EmptyReportData EMPTY_REPORT_DATA = new EmptyReportData();
080:
081: private LayoutControllerUtil() {
082: }
083:
084: public static int findNodeInParent(final Section parentSection,
085: final Node n) {
086: final Node[] nodes = parentSection.getNodeArray();
087: for (int i = 0; i < nodes.length; i++) {
088: final Node node = nodes[i];
089: if (node == n) {
090: return i;
091: }
092: }
093: return -1;
094: }
095:
096: public static StaticExpressionRuntimeData getStaticExpressionRuntime(
097: final FlowController fc, final Object declaringParent) {
098: final GlobalMasterRow dataRow = fc.getMasterRow();
099: final ReportJob reportJob = fc.getReportJob();
100: final StaticExpressionRuntimeData sdd = new StaticExpressionRuntimeData();
101: sdd.setData(dataRow.getReportDataRow().getReportData());
102: sdd.setDeclaringParent(declaringParent);
103: sdd.setConfiguration(reportJob.getConfiguration());
104: sdd.setReportContext(fc.getReportContext());
105: return sdd;
106: }
107:
108: public static LayoutExpressionRuntime getExpressionRuntime(
109: final FlowController fc, final Object node) {
110: final LayoutExpressionRuntime ler = new LayoutExpressionRuntime();
111: ler.setConfiguration(fc.getReportJob().getConfiguration());
112: ler.setReportContext(fc.getReportContext());
113:
114: final GlobalMasterRow masterRow = fc.getMasterRow();
115: ler.setDataRow(masterRow.getGlobalView());
116:
117: final ReportDataRow reportDataRow = masterRow
118: .getReportDataRow();
119: if (reportDataRow == null) {
120: ler.setData(EMPTY_REPORT_DATA);
121: ler.setCurrentRow(-1);
122: } else {
123: ler.setData(reportDataRow.getReportData());
124: ler.setCurrentRow(reportDataRow.getCursor());
125: }
126:
127: ler.setDeclaringParent(node);
128: return ler;
129: }
130:
131: public static FlowController processFlowOperations(
132: FlowController fc, final FlowControlOperation[] ops)
133: throws DataSourceException {
134: for (int i = 0; i < ops.length; i++) {
135: final FlowControlOperation op = ops[i];
136: fc = fc.performOperation(op);
137: }
138: return fc;
139: }
140:
141: /**
142: * Checks, whether the current group should continue. If there is no group, we assume that we should continue. (This
143: * emulates the control-break-algorithm's default behaviour if testing an empty set of arguments.)
144: *
145: * @param fc the current flow controller holding the data
146: * @param node the current node.
147: * @return true, if the group is finished and we should stop reiterating it, false if the group is not finished and we
148: * can start iterating it again.
149: * @throws org.jfree.report.DataSourceException
150: *
151: */
152: public static boolean isGroupFinished(final FlowController fc,
153: final Node node) throws DataSourceException {
154: final Node nodeParent = node.getParent();
155: if (nodeParent == null) {
156: return false;
157: }
158: Group group = nodeParent.getGroup();
159: if (group == null) {
160: return false;
161: }
162:
163: // maybe we can move this state into the layoutstate itself so that
164: // we do not have to rebuild that crap all the time.
165: LayoutExpressionRuntime ler = null;
166:
167: // OK, now we are almost complete.
168: while (group != null) {
169: if (ler == null) {
170: ler = getExpressionRuntime(fc, node);
171: }
172:
173: ler.setDeclaringParent(group);
174:
175: final Expression groupingExpression = group
176: .getGroupingExpression();
177: if (groupingExpression != null) {
178: groupingExpression.setRuntime(ler);
179: final Object groupFinished;
180: try {
181: groupFinished = groupingExpression.computeValue();
182: } finally {
183: groupingExpression.setRuntime(null);
184: }
185:
186: if (Boolean.TRUE.equals(groupFinished)) {
187: // If the group expression returns true, we should pack our belongings
188: // and stop with that process. The group is finished.
189:
190: // In Cobol, this would mean that one of the group-fields has changed.
191: return true;
192: }
193: }
194:
195: final Node parent = group.getParent();
196: if (parent == null) {
197: group = null;
198: } else {
199: group = parent.getGroup();
200: }
201: }
202: return false;
203: }
204:
205: private static void mergeDeclarationRule(
206: final CSSDeclarationRule target,
207: final CSSDeclarationRule source) {
208: final Iterator it = source.getPropertyKeys();
209: while (it.hasNext()) {
210: final StyleKey key = (StyleKey) it.next();
211: final CSSValue value = source.getPropertyCSSValue(key);
212: final boolean sourceImportant = source.isImportant(key);
213: final boolean targetImportant = target.isImportant(key);
214: if (targetImportant) {
215: continue;
216: }
217: target.setPropertyValue(key, value);
218: target.setImportant(key, sourceImportant);
219: }
220: }
221:
222: private static CSSDeclarationRule processStyleAttribute(
223: final Object styleAttributeValue, final Element node,
224: final ExpressionRuntime runtime,
225: CSSDeclarationRule targetRule) throws DataSourceException {
226: if (targetRule == null) {
227: try {
228: targetRule = (CSSDeclarationRule) node.getStyle()
229: .clone();
230: } catch (CloneNotSupportedException e) {
231: targetRule = new CSSStyleRule(null, null);
232: }
233: }
234:
235: if (styleAttributeValue instanceof String) {
236: // ugly, we have to parse that thing. Cant think of nothing
237: // worse than that.
238: final String styleText = (String) styleAttributeValue;
239: try {
240: final ReportContext reportContext = runtime
241: .getReportContext();
242: final ReportStructureRoot root = reportContext
243: .getReportStructureRoot();
244: final ResourceKey baseResource = root.getBaseResource();
245: final ResourceManager resourceManager = root
246: .getResourceManager();
247:
248: final byte[] bytes = styleText.getBytes("UTF-8");
249: final ResourceKey key = resourceManager
250: .createKey(bytes);
251: final Resource resource = resourceManager.create(key,
252: baseResource, StyleRule.class);
253:
254: final CSSDeclarationRule parsedRule = (CSSDeclarationRule) resource
255: .getResource();
256: mergeDeclarationRule(targetRule, parsedRule);
257: } catch (Exception e) {
258: // ignore ..
259: e.printStackTrace();
260: }
261: } else if (styleAttributeValue instanceof CSSStyleRule) {
262: final CSSStyleRule styleRule = (CSSStyleRule) styleAttributeValue;
263: mergeDeclarationRule(targetRule, styleRule);
264: }
265:
266: // ok, not lets fill in the stuff from the style expressions ..
267: final Map styleExpressions = node.getStyleExpressions();
268: final Iterator styleExIt = styleExpressions.entrySet()
269: .iterator();
270:
271: while (styleExIt.hasNext()) {
272: final Map.Entry entry = (Map.Entry) styleExIt.next();
273: final String name = (String) entry.getKey();
274: final Expression expression = (Expression) entry.getValue();
275: try {
276: expression.setRuntime(runtime);
277: final Object value = expression.computeValue();
278: if (value instanceof CSSValue) {
279: final CSSValue cssvalue = (CSSValue) value;
280: final StyleKey keyByName = StyleKeyRegistry
281: .getRegistry().findKeyByName(name);
282: if (keyByName != null) {
283: targetRule
284: .setPropertyValue(keyByName, cssvalue);
285: } else {
286: targetRule.setPropertyValueAsString(name,
287: cssvalue.getCSSText());
288: }
289: } else if (value != null) {
290: targetRule.setPropertyValueAsString(name, String
291: .valueOf(value));
292: }
293: } finally {
294: expression.setRuntime(null);
295: }
296: }
297: return targetRule;
298: }
299:
300: private static AttributeMap collectAttributes(final Element node,
301: final ExpressionRuntime runtime) throws DataSourceException {
302: final AttributeMap attributes = node.getAttributeMap();
303: final AttributeMap attributeExpressions = node
304: .getAttributeExpressionMap();
305: final String[] namespaces = attributeExpressions
306: .getNameSpaces();
307: for (int i = 0; i < namespaces.length; i++) {
308: final String namespace = namespaces[i];
309: final Map attrEx = attributeExpressions
310: .getAttributes(namespace);
311:
312: final Iterator attributeExIt = attrEx.entrySet().iterator();
313: while (attributeExIt.hasNext()) {
314: final Map.Entry entry = (Map.Entry) attributeExIt
315: .next();
316: final String name = (String) entry.getKey();
317: final Expression expression = (Expression) entry
318: .getValue();
319: try {
320: expression.setRuntime(runtime);
321: final Object value = expression.computeValue();
322: attributes.setAttribute(namespace, name, value);
323: } finally {
324: expression.setRuntime(null);
325: }
326: }
327: }
328: return attributes;
329: }
330:
331: public static AttributeMap processAttributes(final Element node,
332: final ReportTarget target, final ExpressionRuntime runtime)
333: throws DataSourceException {
334: final AttributeMap attributes = collectAttributes(node, runtime);
335: CSSDeclarationRule rule = null;
336:
337: final AttributeMap retval = new AttributeMap();
338:
339: final String[] attrNamespaces = attributes.getNameSpaces();
340: for (int i = 0; i < attrNamespaces.length; i++) {
341: final String namespace = attrNamespaces[i];
342: final Map attributeMap = attributes
343: .getAttributes(namespace);
344: if (attributeMap == null) {
345: continue;
346: }
347:
348: final NamespaceDefinition nsDef = target
349: .getNamespaceByUri(namespace);
350: final Iterator attributeIt = attributeMap.entrySet()
351: .iterator();
352: while (attributeIt.hasNext()) {
353: final Map.Entry entry = (Map.Entry) attributeIt.next();
354: final String key = (String) entry.getKey();
355: if (isStyleAttribute(nsDef, node.getType(), key)) {
356: final Object styleAttributeValue = entry.getValue();
357: rule = processStyleAttribute(styleAttributeValue,
358: node, runtime, rule);
359: } else {
360: retval.setAttribute(namespace, key, entry
361: .getValue());
362: }
363: }
364: }
365:
366: // Just in case there was no style-attribute but there are style-expressions
367: if (rule == null) {
368: rule = processStyleAttribute(null, node, runtime, rule);
369: }
370:
371: if (rule != null && rule.getSize() > 0) {
372: retval.setAttribute(Namespaces.LIBLAYOUT_NAMESPACE,
373: "style", rule);
374: }
375:
376: return retval;
377: }
378:
379: private static boolean isStyleAttribute(
380: final NamespaceDefinition def, final String elementName,
381: final String attrName) {
382: if (def == null) {
383: return false;
384: }
385:
386: final String[] styleAttr = def.getStyleAttribute(elementName);
387: for (int i = 0; i < styleAttr.length; i++) {
388: final String styleAttrib = styleAttr[i];
389: if (attrName.equals(styleAttrib)) {
390: return true;
391: }
392: }
393: return false;
394: }
395:
396: public static AttributeMap createEmptyMap(final String namespace,
397: final String tagName) {
398: final AttributeMap map = new AttributeMap();
399: map.setAttribute(JFreeReportInfo.REPORT_NAMESPACE,
400: Element.NAMESPACE_ATTRIBUTE, namespace);
401: map.setAttribute(JFreeReportInfo.REPORT_NAMESPACE,
402: Element.TYPE_ATTRIBUTE, tagName);
403: return map;
404: }
405:
406: public static Object performPrecompute(
407: final int expressionPosition,
408: final PrecomputeNodeKey nodeKey,
409: final LayoutController layoutController,
410: final FlowController flowController)
411: throws ReportProcessingException,
412: ReportDataFactoryException, DataSourceException {
413: final FlowController fc = flowController
414: .createPrecomputeInstance();
415: final PrecomputedValueRegistry pcvr = fc
416: .getPrecomputedValueRegistry();
417:
418: pcvr.startElementPrecomputation(nodeKey);
419:
420: final LayoutController rootLc = layoutController
421: .createPrecomputeInstance(fc);
422: final LayoutController rootParent = rootLc.getParent();
423: final ReportTarget target = new EmptyReportTarget(fc
424: .getReportJob(), fc.getExportDescriptor());
425:
426: LayoutController lc = rootLc;
427: while (lc.isAdvanceable()) {
428: lc = lc.advance(target);
429: while (lc.isAdvanceable() == false
430: && lc.getParent() != null) {
431: final LayoutController parent = lc.getParent();
432: lc = parent.join(lc.getFlowController());
433: }
434: }
435:
436: target.commit();
437: final PrecomputeNode precomputeNode = pcvr.currentNode();
438: final Object functionResult = precomputeNode
439: .getFunctionResult(expressionPosition);
440: pcvr.finishElementPrecomputation(nodeKey);
441: return functionResult;
442: // throw new IllegalStateException
443: // ("Ups - we did not get to the root parent again. This is awful and we cannot continue.");
444: }
445:
446: public static LayoutController skipInvisibleElement(
447: final LayoutController layoutController)
448: throws ReportProcessingException,
449: ReportDataFactoryException, DataSourceException {
450: final FlowController fc = layoutController.getFlowController();
451: final ReportTarget target = new EmptyReportTarget(fc
452: .getReportJob(), fc.getExportDescriptor());
453: final LayoutController rootParent = layoutController
454: .getParent();
455:
456: // Now start to iterate until the derived layout controller 'lc' that has this given parent
457: // wants to join.
458: LayoutController lc = layoutController;
459: while (lc.isAdvanceable()) {
460: lc = lc.advance(target);
461: while (lc.isAdvanceable() == false
462: && lc.getParent() != null) {
463: final LayoutController parent = lc.getParent();
464: lc = parent.join(lc.getFlowController());
465: if (parent == rootParent) {
466: target.commit();
467: return lc;
468: }
469: }
470: }
471: target.commit();
472: throw new IllegalStateException(
473: "Ups - we did not get to the root parent again. This is awful and we cannot continue.");
474: // return lc;
475: }
476:
477: public static Object evaluateExpression(
478: final FlowController flowController,
479: final Object declaringParent, final Expression expression)
480: throws DataSourceException {
481: final ExpressionRuntime runtime = getExpressionRuntime(
482: flowController, declaringParent);
483:
484: try {
485: expression.setRuntime(runtime);
486: return expression.computeValue();
487: } catch (DataSourceException dse) {
488: throw dse;
489: } catch (Exception e) {
490: throw new DataSourceException(
491: "Failed to evaluate expression", e);
492: } finally {
493: expression.setRuntime(null);
494: }
495: }
496: }
|