KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > svggen > DOMGroupManager


1 /*
2
3    Copyright 2001,2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.svggen;
19
20 import java.util.HashMap JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.Map JavaDoc;
23
24 import org.apache.batik.ext.awt.g2d.GraphicContext;
25 import org.apache.batik.ext.awt.g2d.TransformStackElement;
26 import org.w3c.dom.Element JavaDoc;
27
28 /**
29  * This class is used by the Graphics2D SVG Generator to manage
30  * a group of Nodes that can later be added to the SVG DOM Tree
31  * managed by the DOMTreeManager.
32  *
33  * There are two rules that control how children nodes are
34  * added to the group managed by this class:
35  *
36  * + Children node are added to the group as long as
37  * there is no more than n graphic context overrides needed to
38  * describe the children style. A graphic context override
39  * happens when style attributes need to be added to a child
40  * node to reflect the state of the graphic context at the
41  * time the child was added. Note that the opacity is never
42  * reflected in a group node and therefore, is not accounted
43  * for in the number of overrides. The number of overrides can
44  * be configured and defaults to 2.
45  * + Children nodes are added to the current group as long as
46  * the associated GraphicContext's transform stack is valid.
47  *
48  * When children nodes can no longer be added, the group is considered
49  * complete and the associated DOMTreeManager is notified of the
50  * availability of a completed group. Then, a new group is started.
51  * <br>
52  * The DOMTreeManager is also notified every thime a new element
53  * is added to the current group. This is needed to let the
54  * DOMTreeManager handle group managers that would be used concurrently.
55  *
56  * @author <a HREF="mailto:cjolif@ilog.fr">Christophe Jolif</a>
57  * @author <a HREF="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
58  * @version $Id: DOMGroupManager.java,v 1.14 2005/04/02 12:58:17 deweese Exp $
59  */

60 public class DOMGroupManager implements SVGSyntax, ErrorConstants {
61     public final static short DRAW = 0x01;
62     public final static short FILL = 0x10;
63
64     /**
65      * Reference to the GraphicContext this manager will use to
66      * reflect style attributes in the tree nodes.
67      */

68     protected GraphicContext gc;
69
70     /**
71      * DOMTreeManager that this group manager cooperates with
72      */

73     protected DOMTreeManager domTreeManager;
74
75     /**
76      * Current group's SVG GraphicContext state
77      */

78     protected SVGGraphicContext groupGC;
79
80     /**
81      * Current group node
82      */

83     protected Element currentGroup;
84
85     /**
86      * Constructor
87      * @param gc graphic context whose state will be reflected in the
88      * element's style attributes.
89      * @param domTreeManager DOMTreeManager instance this group manager
90      * cooperates with.
91      */

92     public DOMGroupManager(GraphicContext gc, DOMTreeManager domTreeManager) {
93         if (gc == null)
94             throw new SVGGraphics2DRuntimeException(ERR_GC_NULL);
95
96         if (domTreeManager == null)
97             throw new SVGGraphics2DRuntimeException(ERR_DOMTREEMANAGER_NULL);
98
99         this.gc = gc;
100         this.domTreeManager = domTreeManager;
101
102         // Start with a new Top Level Group
103
recycleCurrentGroup();
104
105         // Build the default GC descriptor
106
groupGC = domTreeManager.gcConverter.toSVG(gc);
107     }
108
109     /**
110      * Reset the state of this object to handle a new currentGroup
111      */

112     void recycleCurrentGroup() {
113         // Create new initial current group node
114
currentGroup = domTreeManager.getDOMFactory().
115             createElementNS(SVG_NAMESPACE_URI, SVG_G_TAG);
116     }
117
118     /**
119      * Adds a node to the current group, if possible
120      * @param element child Element to add to the group
121      */

122     public void addElement(Element element) {
123         addElement(element, (short)(DRAW|FILL));
124     }
125
126     /**
127      * Adds a node to the current group, if possible
128      * @param element child Element to add to the group
129      */

130     public void addElement(Element element, short method) {
131         //
132
// If this is the first child to be added to the
133
// currentGroup, 'freeze' the style attributes.
134
//
135
if (!currentGroup.hasChildNodes()) {
136             currentGroup.appendChild(element);
137
138             groupGC = domTreeManager.gcConverter.toSVG(gc);
139             SVGGraphicContext deltaGC;
140             deltaGC = processDeltaGC(groupGC,
141                                      domTreeManager.defaultGC);
142             domTreeManager.getStyleHandler().
143                 setStyle(currentGroup, deltaGC.getGroupContext(),
144                          domTreeManager.getGeneratorContext());
145             if ((method & DRAW) == 0) {
146                 // force stroke:none
147
deltaGC.getGraphicElementContext().put(SVG_STROKE_ATTRIBUTE,
148                                                        SVG_NONE_VALUE);
149             }
150             if ((method & FILL) == 0) {
151                 // force fill:none
152
deltaGC.getGraphicElementContext().put(SVG_FILL_ATTRIBUTE,
153                                                        SVG_NONE_VALUE);
154             }
155             domTreeManager.getStyleHandler().
156                 setStyle(element, deltaGC.getGraphicElementContext(),
157                          domTreeManager.getGeneratorContext());
158             setTransform(currentGroup, deltaGC.getTransformStack());
159             domTreeManager.appendGroup(currentGroup, this);
160         } else {
161             if(gc.isTransformStackValid()) {
162                 //
163
// There are children nodes already. Find
164
// out delta between current gc and group
165
// context
166
//
167
SVGGraphicContext elementGC =
168                     domTreeManager.gcConverter.toSVG(gc);
169                 SVGGraphicContext deltaGC = processDeltaGC(elementGC, groupGC);
170
171                 // If there are less than the maximum number
172
// of differences, then add the node to the current
173
// group and set its attributes
174
trimContextForElement(deltaGC, element);
175                 if (countOverrides(deltaGC) <= domTreeManager.maxGCOverrides) {
176                     currentGroup.appendChild(element);
177                     // as there already are children we put all
178
// attributes (group + element) on the element itself.
179
if ((method & DRAW) == 0) {
180                         // force stroke:none
181
deltaGC.getContext().
182                             put(SVG_STROKE_ATTRIBUTE, SVG_NONE_VALUE);
183                     }
184                     if ((method & FILL) == 0) {
185                         // force fill:none
186
deltaGC.getContext().
187                             put(SVG_FILL_ATTRIBUTE, SVG_NONE_VALUE);
188                     }
189                     domTreeManager.getStyleHandler().
190                         setStyle(element, deltaGC.getContext(),
191                                  domTreeManager.getGeneratorContext());
192                     setTransform(element, deltaGC.getTransformStack());
193                 } else {
194                     //
195
// Need to create a new current group
196
//
197
currentGroup =
198                         domTreeManager.getDOMFactory().
199                         createElementNS(SVG_NAMESPACE_URI, SVG_G_TAG);
200                     addElement(element, method);
201                 }
202             } else {
203                 //
204
// Transform stack is invalid. Create a new current
205
// group and validate the stack
206
//
207
currentGroup =
208                     domTreeManager.getDOMFactory().
209                     createElementNS(SVG_NAMESPACE_URI, SVG_G_TAG);
210                 gc.validateTransformStack();
211                 addElement(element, method);
212             }
213         }
214     }
215
216     /**
217      * Analyses the Map to define how many attributes constitute
218      * overrides. Only differences in the group context are considered
219      * overrides.
220      */

221     protected int countOverrides(SVGGraphicContext deltaGC) {
222         return deltaGC.getGroupContext().size();
223     }
224
225     /**
226      * Removes properties that do not apply for a specific element
227      */

228     protected void trimContextForElement(SVGGraphicContext svgGC, Element element) {
229         String JavaDoc tag = element.getTagName();
230         Map JavaDoc groupAttrMap = svgGC.getGroupContext();
231         if (tag != null) {
232             // For each attribute, check if there is an attribute
233
// descriptor. If there is, check if the attribute
234
// applies to the input element. If there is none,
235
// assume the attribute applies to the element.
236
Iterator JavaDoc iter = groupAttrMap.keySet().iterator();
237             while(iter.hasNext()){
238                 String JavaDoc attrName = (String JavaDoc)iter.next();
239                 SVGAttribute attr = SVGAttributeMap.get(attrName);
240                 if(attr != null && !attr.appliesTo(tag))
241                     groupAttrMap.remove(attrName);
242             }
243         }
244     }
245
246     /**
247      * Processes the transform attribute value corresponding to a
248      * given transform stack
249      */

250     protected void setTransform(Element element,
251                               TransformStackElement transformStack[]) {
252         String JavaDoc transform = domTreeManager.gcConverter.
253             toSVG(transformStack).trim();
254         if (transform.length() > 0)
255             element.setAttributeNS(null, SVG_TRANSFORM_ATTRIBUTE, transform);
256     }
257
258     /**
259      * Processes the difference between two graphic contexts. The values
260      * in gc that are different from the values in referenceGc will be
261      * present in the delta. Other values will no.
262      */

263     protected SVGGraphicContext processDeltaGC(SVGGraphicContext gc,
264                                              SVGGraphicContext referenceGc) {
265         Map JavaDoc groupDelta = processDeltaMap(gc.getGroupContext(),
266                                          referenceGc.getGroupContext());
267         Map JavaDoc graphicElementDelta = gc.getGraphicElementContext();
268
269         TransformStackElement gcTransformStack[] = gc.getTransformStack();
270         TransformStackElement referenceStack[] = referenceGc.getTransformStack();
271         int deltaStackLength = gcTransformStack.length - referenceStack.length;
272         TransformStackElement deltaTransformStack[] =
273             new TransformStackElement[deltaStackLength];
274
275         System.arraycopy(gcTransformStack, referenceStack.length,
276                          deltaTransformStack, 0, deltaStackLength);
277
278         /**
279            System.err.println("gc transform stack length: " +
280            gc.getTransformStack().length);
281            System.err.println("reference stack length : " +
282            referenceGc.getTransformStack().length);
283            System.err.println("delta stack length : " +
284            deltaTransformStack.length);
285         */

286
287         /*
288           TransformStackElement gcStack[] = gc.getTransformStack();
289           for(int i=0; i<gcStack.length; i++)
290           System.err.println("gcStack[" + i + "] = " + gcStack[i].toString());
291
292           TransformStackElement refStack[] = referenceGc.getTransformStack();
293           for(int i=0; i<refStack.length; i++)
294           System.err.println("refStack[" + i + "] = " + refStack[i].toString());
295
296           for(int i=0; i<deltaTransformStack.length; i++)
297           System.err.println("deltaStack[" + i + "] = " +
298           deltaTransformStack[i].toString());
299         */

300
301         SVGGraphicContext deltaGC = new SVGGraphicContext(groupDelta,
302                                                           graphicElementDelta,
303                                                           deltaTransformStack);
304
305         return deltaGC;
306     }
307
308     /**
309      * Processes the difference between two Maps. The code assumes
310      * that the input Maps have the same key sets. Values in map that
311      * are different from values in referenceMap are place in the
312      * returned delta Map.
313      */

314     protected Map JavaDoc processDeltaMap(Map JavaDoc map, Map JavaDoc referenceMap) {
315         // no need to be synch => HashMap
316
Map JavaDoc mapDelta = new HashMap JavaDoc();
317         Iterator JavaDoc iter = map.keySet().iterator();
318         while (iter.hasNext()){
319             String JavaDoc key = (String JavaDoc)iter.next();
320             String JavaDoc value = (String JavaDoc)map.get(key);
321             String JavaDoc refValue = (String JavaDoc)referenceMap.get(key);
322             if (!value.equals(refValue)) {
323                 /*if(key.equals(SVG_TRANSFORM_ATTRIBUTE)){
324                   // Special handling for the transform attribute.
325                   // At this point in the processing, the transform
326                   // in map has to be a substring of the one in
327                   // referenceMap. see the addElement member.
328                   value = value.substring(refValue.length()).trim();
329                   }*/

330                 mapDelta.put(key, value);
331             }
332         }
333         return mapDelta;
334     }
335 }
336
Popular Tags