source: trunk/docx4j/src/main/java/org/docx4j/TraversalUtil.java @ 1764

Revision 1764, 18.6 KB checked in by jharrop, 2 months ago (diff)

Traverse to DML pictures

Line 
1/*
2 *  Copyright 2010-2011, Plutext Pty Ltd.
3 *   
4 *  This file is part of docx4j.
5
6    docx4j is licensed under the Apache License, Version 2.0 (the "License");
7    you may not use this file except in compliance with the License.
8
9    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, software
14    distributed under the License is distributed on an "AS IS" BASIS,
15    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16    See the License for the specific language governing permissions and
17    limitations under the License.
18
19 */
20package org.docx4j;
21
22import java.lang.reflect.Method;
23import java.util.ArrayList;
24import java.util.List;
25
26import javax.xml.bind.JAXBElement;
27
28import org.apache.log4j.Logger;
29import org.docx4j.dml.diagram.CTDataModel;
30import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
31import org.docx4j.openpackaging.parts.WordprocessingML.CommentsPart;
32import org.docx4j.openpackaging.parts.WordprocessingML.EndnotesPart;
33import org.docx4j.openpackaging.parts.WordprocessingML.FooterPart;
34import org.docx4j.openpackaging.parts.WordprocessingML.FootnotesPart;
35import org.docx4j.openpackaging.parts.WordprocessingML.HeaderPart;
36import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
37import org.docx4j.openpackaging.parts.relationships.Namespaces;
38import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
39import org.docx4j.relationships.Relationship;
40import org.docx4j.utils.CompoundTraversalUtilVisitorCallback;
41import org.docx4j.utils.SingleTraversalUtilVisitorCallback;
42import org.docx4j.utils.TraversalUtilVisitor;
43import org.docx4j.wml.Body;
44import org.docx4j.wml.CTFtnEdn;
45import org.docx4j.wml.CTObject;
46import org.docx4j.wml.Pict;
47import org.docx4j.wml.Comments.Comment;
48
49
50/**
51 * Traverse a list of JAXB objects (eg document.xml's document/body
52 * children), and do something to them.
53 *
54 * This is similar to what one could do via XSLT,
55 * but avoids marshalling/unmarshalling.  The downside is that
56 * not everything will necessarily get traversed here
57 * since visitChildren is not (yet) comprehensive.
58 *
59 * @author jharrop, alberto
60 *
61 */
62public class TraversalUtil {
63
64        private static Logger log = Logger.getLogger(TraversalUtil.class);
65       
66        public interface Callback {
67
68                void walkJAXBElements(Object parent);
69
70                List<Object> getChildren(Object o);
71
72                /**
73                 * Visits a node in pre order (before its children have been visited).
74                 *
75                 * A node is visited only if all its parents have been traversed (
76                 * {@link #shouldTraverse(Object)}).</p>
77                 *
78                 * <p>
79                 * Implementations can have side effects.
80                 * </p>
81                 */
82                List<Object> apply(Object o);
83
84                /**
85                 * Decide whether this node's children should be traversed.
86                 *
87                 * @return whether the children of this node should be visited
88                 */
89                boolean shouldTraverse(Object o);
90
91        }       
92
93        public static abstract class CallbackImpl implements Callback {
94
95                // Depth first
96                public void walkJAXBElements(Object parent) {
97                       
98                        List children = getChildren(parent);
99                        if (children != null) {
100
101                                for (Object o : children) {
102
103                                        // if its wrapped in javax.xml.bind.JAXBElement, get its
104                                        // value; this is ok, provided the results of the Callback
105                                        // won't be marshalled
106                                        o = XmlUtils.unwrap(o);
107                                       
108                                        this.apply(o);
109
110                                        if (this.shouldTraverse(o)) {
111                                                walkJAXBElements(o);
112                                        }
113
114                                }
115                        }
116                }
117
118                public List<Object> getChildren(Object o) {
119                        return TraversalUtil.getChildrenImpl(o);
120                }
121
122                /**
123                 * Visits a node in pre order (before its children have been visited).
124
125                 *
126                 * A node is visited only if all its parents have been traversed (
127                 * {@link #shouldTraverse(Object)}).</p>
128                 *
129                 * <p>
130                 * Implementations can have side effects.
131                 * </p>
132                 */
133                public abstract List<Object> apply(Object o);
134
135                /**
136                 * Decide whether this node's children should be traversed.
137                 *
138                 * @return whether the children of this node should be visited
139                 */
140                public boolean shouldTraverse(Object o) {
141                        return true;
142                }
143
144        }
145
146        Callback cb;
147
148        public TraversalUtil(Object parent, Callback cb) {
149
150                this.cb = cb;
151                cb.walkJAXBElements(parent);
152        }
153
154        static void visitChildrenImpl(Object o) {
155
156        }
157
158        private static List<Object> handleGraphicData(org.docx4j.dml.GraphicData graphicData) {
159                // Its not graphicData.getAny() we're typically interested in
160                if (graphicData.getPic()!=null
161                        && graphicData.getPic().getBlipFill()!=null
162                        && graphicData.getPic().getBlipFill().getBlip()!=null) {
163                        log.info("found CTBlip");
164                        List<Object> artificialList = new ArrayList<Object>();
165                        artificialList.add(graphicData.getPic().getBlipFill().getBlip());
166                        return artificialList;
167                } else {
168                        // Chart is in here
169                        return graphicData.getAny();                                           
170                }               
171        }
172        public static List<Object> getChildrenImpl(Object o) {
173
174                log.debug("getting children of " + o.getClass().getName() );
175                if (o instanceof org.docx4j.wml.Text) return null;
176               
177                // Short circuit for common elements
178                if (o instanceof List) {
179                        // Handy if you have your own list of objects you wish to process
180                        return (List<Object>) o;
181                } else if (o instanceof org.docx4j.wml.ContentAccessor) {
182                        return ((org.docx4j.wml.ContentAccessor) o).getContent();
183                } else if (o instanceof org.docx4j.wml.SdtElement) {
184                        return ((org.docx4j.wml.SdtElement) o).getSdtContent().getContent();
185                } else if (o instanceof org.docx4j.dml.wordprocessingDrawing.Anchor) {
186                        org.docx4j.dml.wordprocessingDrawing.Anchor anchor = 
187                                (org.docx4j.dml.wordprocessingDrawing.Anchor)o;
188                        if (anchor.getGraphic()!=null) {
189                                log.info("found a:graphic");
190                                org.docx4j.dml.Graphic graphic = anchor.getGraphic();
191                                if (graphic.getGraphicData()!=null) {
192                                        return handleGraphicData(graphic.getGraphicData());
193                                }
194                        }                       
195                } else if (o instanceof org.docx4j.dml.wordprocessingDrawing.Inline) {
196                        org.docx4j.dml.wordprocessingDrawing.Inline inline = (org.docx4j.dml.wordprocessingDrawing.Inline)o;
197                        if (inline.getGraphic()!=null) {
198                                log.info("found a:graphic");
199                                org.docx4j.dml.Graphic graphic = inline.getGraphic();
200                                if (graphic.getGraphicData()!=null) {
201                                        return handleGraphicData(graphic.getGraphicData());
202                                }
203                        }
204                } else if (o instanceof Pict) {
205                        return ((Pict)o).getAnyAndAny(); // (why didn't the reflection below find this?)
206                       
207                } else if (o instanceof org.docx4j.dml.picture.Pic) { // Post 2.7.1; untested
208                       
209                        org.docx4j.dml.picture.Pic dmlPic = ((org.docx4j.dml.picture.Pic)o);
210                        if (dmlPic.getBlipFill()!=null
211                                        && dmlPic.getBlipFill().getBlip()!=null) {
212                                        log.info("found DML Blip");
213                                        List<Object> artificialList = new ArrayList<Object>();
214                                        artificialList.add(dmlPic.getBlipFill().getBlip());
215                                        return artificialList;
216                        } else {
217                                return null;                                           
218                        }               
219                } else if (o instanceof org.docx4j.dml.CTGvmlPicture) {  // Post 2.7.1
220                       
221                        org.docx4j.dml.CTGvmlPicture dmlPic = ((org.docx4j.dml.CTGvmlPicture)o);
222                        if (dmlPic.getBlipFill()!=null
223                                        && dmlPic.getBlipFill().getBlip()!=null) {
224                                        log.info("found DML Blip");
225                                        List<Object> artificialList = new ArrayList<Object>();
226                                        artificialList.add(dmlPic.getBlipFill().getBlip());
227                                        return artificialList;
228                        } else {
229                                return null;                                           
230                        }               
231                       
232                } else if (o instanceof org.docx4j.vml.CTShape) {                               
233//                      return ((org.docx4j.vml.CTShape)o).getAny();
234                        List<Object> artificialList = new ArrayList<Object>();
235                        for (JAXBElement<?> j : ((org.docx4j.vml.CTShape)o).getPathOrFormulasOrHandles() ) {
236                                artificialList.add(j);                         
237                        }
238                        return artificialList;
239                } else if (o instanceof CTDataModel) {
240                        CTDataModel dataModel = (CTDataModel)o;
241                        List<Object> artificialList = new ArrayList<Object>();
242                        // We're going to create a list merging two children ..                 
243                        artificialList.addAll(dataModel.getPtLst().getPt());
244                        artificialList.addAll(dataModel.getCxnLst().getCxn());                 
245                        return artificialList;
246                } else if (o instanceof org.docx4j.dml.diagram2008.CTDrawing) {
247                        return ((org.docx4j.dml.diagram2008.CTDrawing)o).getSpTree().getSpOrGrpSp();
248                } else if (o instanceof org.docx4j.vml.CTTextbox) {                             
249//                      return ((org.docx4j.vml.CTTextbox)o).getAny();
250                        return ((org.docx4j.vml.CTTextbox)o).getTxbxContent().getEGBlockLevelElts();
251                                // grandchildren
252
253//              } else if (o instanceof org.docx4j.wml.CTTxbxContent) {                         
254//                      return ((org.docx4j.wml.CTTxbxContent)o).getEGBlockLevelElts();
255                } else if (o instanceof CTObject) {
256                        return ((CTObject)o).getAnyAndAny();
257                } else if (o instanceof org.docx4j.dml.CTGvmlGroupShape) {
258                        return ((org.docx4j.dml.CTGvmlGroupShape)o).getTxSpOrSpOrCxnSp();
259                }
260
261                // OK, what is this? Use reflection ..
262                // This should work for things including w:drawing
263                log.debug(".. looking for method which returns list "  );
264                try {
265                        Method[] methods = o.getClass().getDeclaredMethods();
266                        for (int i = 0; i<methods.length; i++) {
267                                Method m = methods[i];
268                                if (m.getReturnType().getName().equals("java.util.List") ) {                                   
269                                        return (List<Object>)m.invoke(o);                                       
270                                }
271                        }
272                       
273                } catch (Exception e) {
274                        // TODO Auto-generated catch block
275                        e.printStackTrace();
276                }
277                log.debug(".. no list member");
278                return null;
279        }
280       
281
282        public static void main(String[] args) throws Exception {
283
284                String inputfilepath = System.getProperty("user.dir")
285                                + "/sample-docs/sample-docx.xml";
286
287                WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage
288                                .load(new java.io.File(inputfilepath));
289                MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
290
291                org.docx4j.wml.Document wmlDocumentEl = (org.docx4j.wml.Document) documentPart
292                                .getJaxbElement();
293                Body body = wmlDocumentEl.getBody();
294
295                new TraversalUtil(body,
296
297                new Callback() {
298
299                        String indent = "";
300                       
301                        @Override
302                        public List<Object> apply(Object o) {
303                               
304                                String text = "";
305                                if (o instanceof org.docx4j.wml.Text)
306                                        text = ((org.docx4j.wml.Text)o).getValue();
307                               
308                                System.out.println(indent + o.getClass().getName() + "  \"" + text + "\"");
309                                return null;
310                        }
311
312                        @Override
313                        public boolean shouldTraverse(Object o) {
314                                return true;
315                        }
316
317                        // Depth first
318                        @Override
319                        public void walkJAXBElements(Object parent) {
320
321                                indent += "    ";
322                               
323                                List children = getChildren(parent);
324                                if (children != null) {
325
326                                        for (Object o : children) {
327
328                                                // if its wrapped in javax.xml.bind.JAXBElement, get its
329                                                // value; this is ok, provided the results of the Callback
330                                                // won't be marshalled
331                                                o = XmlUtils.unwrap(o);
332
333                                                this.apply(o);
334
335                                                if (this.shouldTraverse(o)) {
336                                                        walkJAXBElements(o);
337                                                }
338
339                                        }
340                                }
341                               
342                                indent = indent.substring(0, indent.length()-4);
343                        }
344
345                        @Override
346                        public List<Object> getChildren(Object o) {
347                                return TraversalUtil.getChildrenImpl(o);
348                        }
349
350                }
351
352                );
353
354        }
355
356        public static void replaceChildren(Object o, List<Object> newChildren) {
357               
358                // Available if you need something like this.  Would be used with
359                // something like:
360               
361                /*
362                 *        public void walkJAXBElements(Object parent){
363                  // Breadth first
364                               
365                          List<Object> newChildren = new ArrayList<Object>();
366                         
367                                List children = getChildren(parent);
368                                if (children==null) {
369                                        log.warn("no children: " + parent.getClass().getName() );
370                                } else {
371                                        for (Object o : getChildren(parent) ) {
372
373                                                // if its wrapped in javax.xml.bind.JAXBElement, get its value
374                                                o = XmlUtils.unwrap(o);
375                                               
376                                                newChildren.addAll(this.apply(o));
377                                        }
378                                }                               
379                                // Replace list, so we'll traverse all the new sdts we've just created
380                                replaceChildren(parent, newChildren);
381                               
382                               
383                                for (Object o : getChildren(parent) ) {
384                                       
385                                        this.apply(o);
386                                       
387                                        if ( this.shouldTraverse(o) ) {
388                                                walkJAXBElements(o);
389                                        }
390                                       
391                                }
392                               
393                        }
394
395                 */
396
397                log.debug("Clearing " + o.getClass().getName() );
398               
399                if (o instanceof org.docx4j.wml.ContentAccessor) {
400
401                        ((org.docx4j.wml.ContentAccessor) o).getContent().clear();
402                        ((org.docx4j.wml.ContentAccessor) o).getContent().addAll(newChildren);
403                       
404                } else if (o instanceof org.docx4j.wml.SdtElement) {
405                       
406                        ((org.docx4j.wml.SdtElement) o).getSdtContent().getContent().clear();
407                        ((org.docx4j.wml.SdtElement) o).getSdtContent().getContent().addAll(newChildren);
408                       
409                } else if (o instanceof org.docx4j.wml.CTTxbxContent) {
410                       
411                        ((org.docx4j.wml.CTTxbxContent) o).getEGBlockLevelElts().clear();
412                        ((org.docx4j.wml.CTTxbxContent) o).getEGBlockLevelElts().addAll(newChildren);                   
413
414                } else {
415                       
416                        log.warn("Don't know how to replaceChildren in " + o.getClass().getName() ); 
417
418                        if (o instanceof org.w3c.dom.Node) {
419                                log.warn(" IGNORED " + ((org.w3c.dom.Node) o).getNodeName());
420                        } else {
421//                              log.warn(" IGNORED " + o.getClass().getName());
422                        }
423
424                }
425        }
426       
427        /**
428         * Use this if there is only a single object type (eg just P's)
429         * you are interested in doing something with. 
430         *
431         * This method allows you to traverse just the main document
432         * part, or also headers/footers, footnotes/endnotes, and comments
433         * as well.
434         *
435         * @param wmlPackage
436         * @param bodyOnly
437         * @param visitor
438         */
439        public static void visit(WordprocessingMLPackage wmlPackage, 
440                        boolean bodyOnly, TraversalUtilVisitor visitor) {
441               
442                if (visitor != null) {
443                        visit(wmlPackage, bodyOnly, new SingleTraversalUtilVisitorCallback(visitor));
444                }
445        }
446
447        /**
448         * Use this if there is only a single object type (eg just P's)
449         * you are interested in doing something with.
450         *
451         * This method is for traversing an arbitrary WML object (eg a table), as opposed to
452         * eg the main document part, or a header.
453         *
454         * @param parent
455         * @param visitor
456         */
457        public static void visit(Object parent, TraversalUtilVisitor visitor) {
458               
459                if (visitor != null) {
460                        visit(parent, new SingleTraversalUtilVisitorCallback(visitor));
461                }
462        }
463       
464        /**
465         * Use this if there is more than one object type (eg Tables and Paragraphs)
466         * you are interested in doing something with during the traversal.
467         *
468         * This method allows you to traverse just the main document
469         * part, or also headers/footers, footnotes/endnotes, and comments
470         * as well.
471         *
472         * @param wmlPackage
473         * @param bodyOnly
474         * @param visitorList
475         */
476        public static void visit(WordprocessingMLPackage wmlPackage,
477                        boolean bodyOnly, List<TraversalUtilVisitor> visitorList) {
478               
479                CompoundTraversalUtilVisitorCallback callback = null;
480                if ((visitorList != null) && (!visitorList.isEmpty())) {
481                        if (visitorList.size() > 1) {
482                                visit(wmlPackage, bodyOnly,
483                                                new CompoundTraversalUtilVisitorCallback(visitorList));
484                        } else {
485                                visit(wmlPackage, bodyOnly, visitorList.get(0));
486                        }
487                }
488        }
489
490        /**
491         * Use this if there is more than one object type (eg Tables and Paragraphs)
492         * you are interested in doing something with during the traversal.
493         *
494         * This method is for traversing an arbitrary WML object (eg a table), as opposed to
495         * eg the main document part, or a header.
496         *
497         * @param parent
498         * @param visitorList
499         */
500        public static void visit(Object parent,
501                        List<TraversalUtilVisitor> visitorList) {
502               
503                CompoundTraversalUtilVisitorCallback callback = null;
504                if ((visitorList != null) && (!visitorList.isEmpty())) {
505                        if (visitorList.size() > 1) {
506                                visit(parent, new CompoundTraversalUtilVisitorCallback(
507                                                visitorList));
508                        } else {
509                                visit(parent, visitorList.get(0));
510                        }
511                }
512        }
513
514        public static void visit(Object parent, Callback callback) {
515               
516                if ((parent != null) && (callback != null)) {
517                        callback.walkJAXBElements(parent);
518                }
519        }
520
521        public static void visit(WordprocessingMLPackage wmlPackage,
522                        boolean bodyOnly, Callback callback) {
523               
524                MainDocumentPart mainDocument = null;
525                RelationshipsPart relPart = null;
526                List<Relationship> relList = null;
527                List<Object> elementList = null;
528               
529                if ((wmlPackage != null) && (callback != null)) {
530                        mainDocument = wmlPackage.getMainDocumentPart();
531                        callback.walkJAXBElements(mainDocument.getJaxbElement().getBody());
532                        if (!bodyOnly) {
533                                relPart = mainDocument.getRelationshipsPart();
534                                relList = relPart.getRelationships().getRelationship();
535                                for (Relationship rs : relList) {
536                                        elementList = null;
537                                        if (Namespaces.HEADER.equals(rs.getType())) {
538                                                elementList = ((HeaderPart) relPart.getPart(rs))
539                                                                .getJaxbElement().getContent();
540                                        } else if (Namespaces.FOOTER.equals(rs.getType())) {
541                                                elementList = ((FooterPart) relPart.getPart(rs))
542                                                                .getJaxbElement().getContent();
543                                        } else if (Namespaces.ENDNOTES.equals(rs.getType())) {
544                                                elementList = new ArrayList();
545                                                for (CTFtnEdn endnote : ((EndnotesPart) relPart
546                                                                .getPart(rs)).getJaxbElement().getEndnote()) {
547                                                        elementList.addAll(endnote.getEGBlockLevelElts());
548                                                }
549                                        } else if (Namespaces.FOOTNOTES.equals(rs.getType())) {
550                                                elementList = new ArrayList();
551                                                for (CTFtnEdn footnote : ((FootnotesPart) relPart
552                                                                .getPart(rs)).getJaxbElement().getFootnote()) {
553                                                        elementList.addAll(footnote.getEGBlockLevelElts());
554                                                }
555                                        } else if (Namespaces.COMMENTS.equals(rs.getType())) {
556                                                elementList = new ArrayList();
557                                                for (Comment comment : ((CommentsPart) relPart
558                                                                .getPart(rs)).getJaxbElement().getComment()) {
559                                                        elementList.addAll(comment.getEGBlockLevelElts());
560                                                }
561                                        }
562                                        if ((elementList != null) && (!elementList.isEmpty())) {
563                                                System.out.println("Processing target: "
564                                                                + rs.getTarget() + ", type: " + rs.getType());
565                                                callback.walkJAXBElements(elementList);
566                                        }
567                                }
568                        }
569                }
570        }
571       
572        // private void describeDrawing( org.docx4j.wml.Drawing d) {
573        //                     
574        // log.info("In wml.Drawing" );
575        //                     
576        // if ( d.getAnchorOrInline().get(0) instanceof Anchor ) {
577        //                             
578        // log.info(" ENCOUNTERED w:drawing/wp:anchor " );
579        // // That's all for now...
580        //                             
581        // } else if ( d.getAnchorOrInline().get(0) instanceof Inline ) {
582        //                             
583        // // Extract
584        // w:drawing/wp:inline/a:graphic/a:graphicData/pic:pic/pic:blipFill/a:blip/@r:embed
585        //                             
586        // Inline inline = (Inline )d.getAnchorOrInline().get(0);
587        //                             
588        // Pic pic = inline.getGraphic().getGraphicData().getPic();
589        //                                     
590        // if (pic!=null) {
591        // log.info("image relationship: " + pic.getBlipFill().getBlip().getEmbed()
592        // );
593        // }
594        //                             
595        //                             
596        // } else {
597        //                             
598        // log.info(" Didn't get Inline :(  How to handle " +
599        // d.getAnchorOrInline().get(0).getClass().getName() );
600        // }
601        //                     
602        // }
603
604}
Note: See TracBrowser for help on using the repository browser.