source: trunk/docx4j/src/main/java/org/docx4j/openpackaging/parts/WordprocessingML/NumberingDefinitionsPart.java @ 1761

Revision 1761, 12.8 KB checked in by jharrop, 3 months ago (diff)

a null check and some logging

  • Property svn:eol-style set to native
  • Property svn:executable set to *
Line 
1/*
2 *  Copyright 2007-2008, 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 */
20
21package org.docx4j.openpackaging.parts.WordprocessingML;
22
23
24import java.io.IOException;
25import java.math.BigInteger;
26import java.util.HashMap;
27
28import javax.xml.bind.JAXBException;
29import javax.xml.bind.UnmarshalException;
30import javax.xml.bind.Unmarshaller;
31import javax.xml.bind.util.JAXBResult;
32import javax.xml.transform.Templates;
33import javax.xml.transform.dom.DOMResult;
34import javax.xml.transform.stream.StreamSource;
35
36import org.apache.log4j.Logger;
37import org.docx4j.XmlUtils;
38import org.docx4j.jaxb.Context;
39import org.docx4j.jaxb.JaxbValidationEventHandler;
40import org.docx4j.model.PropertyResolver;
41import org.docx4j.model.listnumbering.AbstractListNumberingDefinition;
42import org.docx4j.model.listnumbering.Emulator;
43import org.docx4j.model.listnumbering.ListLevel;
44import org.docx4j.model.listnumbering.ListNumberingDefinition;
45import org.docx4j.openpackaging.exceptions.Docx4JException;
46import org.docx4j.openpackaging.exceptions.InvalidFormatException;
47import org.docx4j.openpackaging.exceptions.InvalidOperationException;
48import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
49import org.docx4j.openpackaging.parts.JaxbXmlPart;
50import org.docx4j.openpackaging.parts.PartName;
51import org.docx4j.openpackaging.parts.relationships.Namespaces;
52import org.docx4j.wml.Lvl;
53import org.docx4j.wml.Numbering;
54import org.docx4j.wml.Numbering.Num;
55import org.docx4j.wml.Numbering.Num.AbstractNumId;
56import org.docx4j.wml.Numbering.Num.LvlOverride;
57import org.docx4j.wml.Numbering.Num.LvlOverride.StartOverride;
58import org.docx4j.wml.PPrBase.Ind;
59import org.docx4j.wml.PPrBase.NumPr;
60import org.docx4j.wml.PPrBase.NumPr.Ilvl;
61import org.docx4j.wml.PPrBase.NumPr.NumId;
62
63
64
65public final class NumberingDefinitionsPart extends JaxbXmlPart<Numbering> {
66       
67        private static Logger log = Logger.getLogger(NumberingDefinitionsPart.class);   
68       
69        public NumberingDefinitionsPart(PartName partName) throws InvalidFormatException {
70                super(partName);
71                init();
72        }
73       
74        public NumberingDefinitionsPart() throws InvalidFormatException {
75                super(new PartName("/word/numbering.xml"));
76                init();
77        }
78
79        public void init() {   
80                // Used if this Part is added to [Content_Types].xml
81                setContentType(new  org.docx4j.openpackaging.contenttype.ContentType( 
82                                org.docx4j.openpackaging.contenttype.ContentTypes.WORDPROCESSINGML_NUMBERING));
83
84                // Used when this Part is added to a rels
85                setRelationshipType(Namespaces.NUMBERING);
86        }
87       
88    HashMap<String, AbstractListNumberingDefinition> abstractListDefinitions; 
89        public HashMap<String, AbstractListNumberingDefinition> getAbstractListDefinitions() {
90                if (abstractListDefinitions==null) initialiseMaps();
91                return abstractListDefinitions;
92        }
93
94    HashMap<String, ListNumberingDefinition> instanceListDefinitions; 
95        public HashMap<String, ListNumberingDefinition> getInstanceListDefinitions() {
96               
97                if (instanceListDefinitions==null) initialiseMaps();
98               
99                return instanceListDefinitions;
100        }
101       
102    public void initialiseMaps()
103    {
104        Numbering numbering = jaxbElement;
105       
106        // count the number of different list numbering schemes
107        if (numbering.getNum().size() == 0)
108        {
109                log.debug("No num defined");
110            return;
111        }
112       
113        // initialize the abstract number list
114        abstractListDefinitions
115                = new HashMap<String, AbstractListNumberingDefinition>(numbering.getAbstractNum().size() );
116               
117        // initialize the instance number list
118        instanceListDefinitions
119                = new HashMap<String, ListNumberingDefinition>( numbering.getNum().size() );
120
121        // store the abstract list type definitions
122        for (Numbering.AbstractNum abstractNumNode : numbering.getAbstractNum() )
123        {
124            AbstractListNumberingDefinition absNumDef
125                = new AbstractListNumberingDefinition(abstractNumNode);
126
127            abstractListDefinitions.put(absNumDef.getID(), absNumDef);
128
129            // now go through the abstract list definitions and update those that are linked to other styles
130            if (absNumDef.hasLinkedStyle() )
131            {
132//                String linkStyleXPath = "/w:document/w:numbering/w:abstractNum/w:styleLink[@w:val=\"" + absNumDef.Value.LinkedStyleId + "\"]";
133//                XmlNode linkedStyleNode = mainDoc.SelectSingleNode(linkStyleXPath, nsm);
134//
135//                if (linkedStyleNode != null)
136//                {
137//                    absNumDef.Value.UpdateDefinitionFromLinkedStyle(linkedStyleNode.ParentNode, nsm);
138//                }
139               
140                // find the linked style
141                // TODO - review
142                absNumDef.UpdateDefinitionFromLinkedStyle(abstractNumNode);
143            }
144        }
145
146        // instantiate the list number definitions
147        for( Numbering.Num numNode : numbering.getNum() )
148        {
149            ListNumberingDefinition listDef
150                = new ListNumberingDefinition(numNode, abstractListDefinitions);
151
152            instanceListDefinitions.put(listDef.getListNumberId(), listDef);
153            log.debug("Added list: " + listDef.getListNumberId() );
154        }
155
156    }
157   
158    /**
159     * For the given list numId, restart the numbering on the specified
160     * level at value val.  This is done by creating a new list (ie <w:num>)
161     * which uses the existing w:abstractNum.
162     * @param numId
163     * @param ilvl
164     * @param val
165     * @return
166     */
167    public long restart(long numId, long ilvl, long val) 
168        throws InvalidOperationException {
169       
170        // Find the abstractNumId
171       
172        // (Ensure maps are initialised)
173        if (em == null ) { 
174                getEmulator();
175        }
176        ListNumberingDefinition existingLnd = instanceListDefinitions.get( Long.toString(numId) );
177        if (existingLnd==null) {
178                throw new InvalidOperationException("List " + numId + " does not exist");
179        }
180        BigInteger abstractNumIdVal = existingLnd.getNumNode().getAbstractNumId().getVal();
181       
182        // Generate the new <w:num
183        long newNumId = instanceListDefinitions.size() + 1;
184       
185                org.docx4j.wml.ObjectFactory factory = Context.getWmlObjectFactory();
186               
187                Num newNum = factory.createNumberingNum();
188                newNum.setNumId( BigInteger.valueOf(newNumId) );
189                AbstractNumId abstractNumId = factory.createNumberingNumAbstractNumId();
190                abstractNumId.setVal(abstractNumIdVal);
191                newNum.setAbstractNumId(abstractNumId);
192               
193                LvlOverride lvlOverride = factory.createNumberingNumLvlOverride();
194                lvlOverride.setIlvl(BigInteger.valueOf(ilvl));
195                newNum.getLvlOverride().add(lvlOverride);
196               
197                StartOverride start = factory.createNumberingNumLvlOverrideStartOverride();
198                start.setVal(BigInteger.valueOf(val));
199                lvlOverride.setStartOverride(start);
200       
201        // Add it to the jaxb object and our hashmap
202                ((Numbering)jaxbElement).getNum().add(newNum);
203        ListNumberingDefinition listDef
204                = new ListNumberingDefinition(newNum, abstractListDefinitions);
205        instanceListDefinitions.put(listDef.getListNumberId(), listDef);               
206       
207        // Return the new numId
208        return newNumId;
209       
210    }
211       
212       
213        private Emulator em;
214//      public void setEmulator(Emulator em) {
215//              this.em = em;
216//      }
217        public Emulator getEmulator() {
218               
219        if (em == null ) { 
220                initialiseMaps();
221                em = new Emulator();                   
222        }
223               
224                return em;
225        }
226
227        public Ind getInd(NumPr numPr) { //, StyleDefinitionsPart sdp, String styleId) {
228               
229                String ilvlString = "0";
230                if (numPr.getIlvl()!=null) ilvlString = numPr.getIlvl().getVal().toString();
231               
232                if (numPr.getNumId()==null) {
233                        log.warn("numPr without numId: " + XmlUtils.marshaltoString(numPr, true, true));
234                                               
235                        return null;
236                } else {               
237                        return getInd(numPr.getNumId().getVal().toString(), ilvlString );
238                }
239        }
240       
241        public Ind getInd(String numId, String ilvl) {
242
243                // Operating on the docx4j.listnumbering plane,
244                // not the JAXB plane..
245                ListNumberingDefinition lnd = getInstanceListDefinitions().get(numId );
246                if (lnd==null) {
247                        log.debug("couldn't find list for numId: " + numId);
248                        return null;
249                }
250                if (ilvl==null) ilvl = "0";
251                ListLevel ll = lnd.getLevel(ilvl);
252               
253                // OK, now on the JAXB plane
254                Lvl jaxbOverrideLvl = ll.getJaxbOverrideLvl();
255               
256                log.debug("Looking at override/instance definition..");
257                if (jaxbOverrideLvl!=null) {
258                       
259                        Ind ind = getIndFromLvl(jaxbOverrideLvl);
260                        if (ind!=null) {
261                                log.debug("Got it..");
262                                return ind;
263                        }
264                }
265               
266                // Now do the same for the abstract definition
267                log.debug("Looking at abstract definition..");
268                Lvl abstractLvl = ll.getJaxbAbstractLvl();
269                Ind ind = getIndFromLvl(abstractLvl);
270               
271                return ind;
272        }
273       
274        private Ind getIndFromLvl(Lvl lvl) {
275               
276                // If there is a style reference in the instance,
277                // as a sibling of pPr,
278                // use any w:ind in it (or TODO styles it is based on)
279                if (lvl.getPStyle()!=null) {
280                       
281                        // Get the style
282//                      StyleDefinitionsPart stylesPart = ((WordprocessingMLPackage)this.getPackage()).
283//                              getMainDocumentPart().getStyleDefinitionsPart();
284                        PropertyResolver propertyResolver
285                                = ((WordprocessingMLPackage)this.getPackage()).getMainDocumentPart().getPropertyResolver(); 
286                       
287                        log.debug("override level has linked style: " + lvl.getPStyle().getVal() );
288                       
289                        org.docx4j.wml.Style style = propertyResolver.getStyle( lvl.getPStyle().getVal() );
290                       
291                        // If the style has a w:ind, return it.
292                        // Otherwise, continue
293                        if (style.getPPr() != null
294                                        && style.getPPr().getInd()!=null ) {
295                                return style.getPPr().getInd();
296                        }
297                }
298               
299                // If there is a style reference in pPr,
300                // but not also one as a sibling of pPr,
301                // then no number appears at all!
302               
303                        // TODO: throw ShouldNotBeNumbered??
304               
305                // If there is a w:ind in the instance use that
306                if ( lvl.getPPr()!=null
307                                && lvl.getPPr().getInd() !=null ) {
308                        return lvl.getPPr().getInd();
309                }
310               
311                return null;           
312               
313        }
314       
315        /**
316         * Add the specified definition, allocating it a new w:abstractNumId.
317         *
318         * Also create and add an associated ListNumberingDefinition, and return
319         * the w:numId of this associated ListNumberingDefinition (since that is
320         * what you are likely to use in the document).
321         *
322         * @param abstractNum
323         * @return
324         */
325        public Numbering.Num addAbstractListNumberingDefinition(Numbering.AbstractNum abstractNum) {
326               
327                //////////////////////////////////////////////
328                // Numbering.AbstractNum abstractNum
329               
330                // Generate a unique w:abstractNumId for it
331                int nextId = getAbstractListDefinitions().size();               
332        do {
333                nextId++;               
334        } while (getAbstractListDefinitions().containsKey( "" + nextId ));     
335        abstractNum.setAbstractNumId( BigInteger.valueOf(nextId) );
336       
337        // Add it to our JAXB object
338        this.jaxbElement.getAbstractNum().add(abstractNum);
339       
340        // Add it to our hashmap
341        AbstractListNumberingDefinition absNumDef = new AbstractListNumberingDefinition(abstractNum);
342        abstractListDefinitions.put(absNumDef.getID(), absNumDef);
343
344                //////////////////////////////////////////////
345                // Numbering.Num num
346       
347        // Now make an associated ListNumberingDefinition
348                //      <w:num w:numId="1">
349                //        <w:abstractNumId w:val="1"/>
350                //      </w:num>"       
351        Numbering.Num num = Context.getWmlObjectFactory().createNumberingNum();
352        Numbering.Num.AbstractNumId abstractNumId = Context.getWmlObjectFactory().createNumberingNumAbstractNumId();
353        abstractNumId.setVal(BigInteger.valueOf(nextId) );
354        num.setAbstractNumId(abstractNumId);
355       
356        nextId = getInstanceListDefinitions().size();           
357        do {
358                nextId++;               
359        } while (getInstanceListDefinitions().containsKey( "" + nextId ));     
360        num.setNumId( BigInteger.valueOf(nextId) ); 
361       
362        // Add it to our JAXB object
363        this.jaxbElement.getNum().add(num);
364       
365        // Add it to our hashmap
366        ListNumberingDefinition listDef = new ListNumberingDefinition(num, abstractListDefinitions);
367        instanceListDefinitions.put(listDef.getListNumberId(), listDef);
368       
369        //
370        return num;
371               
372        }
373       
374    public Numbering unmarshalDefaultNumbering() throws JAXBException {
375                         
376                java.io.InputStream is = null;
377                try {
378                        is = org.docx4j.utils.ResourceUtils.getResource(
379                                        "org/docx4j/openpackaging/parts/WordprocessingML/numbering.xml");
380                } catch (IOException e) {
381                        // TODO Auto-generated catch block
382                        e.printStackTrace();
383                }               
384       
385        return unmarshal( is );    // side-effect is to set jaxbElement         
386    }
387
388}
Note: See TracBrowser for help on using the repository browser.