All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart Maven / Gradle / Ivy

Go to download

docx4j is a library which helps you to work with the Office Open XML file format as used in docx documents, pptx presentations, and xlsx spreadsheets.

There is a newer version: 11.4.11
Show newest version
/*
 *  Copyright 2007-2008, Plutext Pty Ltd.
 *   
 *  This file is part of docx4j.

    docx4j is licensed under the Apache License, Version 2.0 (the "License"); 
    you may not use this file except in compliance with the License. 

    You may obtain a copy of the License at 

        http://www.apache.org/licenses/LICENSE-2.0 

    Unless required by applicable law or agreed to in writing, software 
    distributed under the License is distributed on an "AS IS" BASIS, 
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
    See the License for the specific language governing permissions and 
    limitations under the License.

 */

package org.docx4j.openpackaging.parts.WordprocessingML;


import org.docx4j.XmlUtils;
import org.docx4j.jaxb.Context;
import org.docx4j.jaxb.McIgnorableNamespaceDeclarator;
import org.docx4j.model.PropertyResolver;
import org.docx4j.model.listnumbering.AbstractListNumberingDefinition;
import org.docx4j.model.listnumbering.Emulator;
import org.docx4j.model.listnumbering.ListLevel;
import org.docx4j.model.listnumbering.ListNumberingDefinition;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.openpackaging.exceptions.InvalidOperationException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.JaxbXmlPartXPathAware;
import org.docx4j.openpackaging.parts.PartName;
import org.docx4j.openpackaging.parts.relationships.Namespaces;
import org.docx4j.utils.ResourceUtils;
import org.docx4j.wml.Lvl;
import org.docx4j.wml.Numbering;
import org.docx4j.wml.Numbering.Num;
import org.docx4j.wml.Numbering.Num.AbstractNumId;
import org.docx4j.wml.Numbering.Num.LvlOverride;
import org.docx4j.wml.Numbering.Num.LvlOverride.StartOverride;
import org.docx4j.wml.PPrBase.Ind;
import org.docx4j.wml.PPrBase.NumPr;
import org.docx4j.wml.Style;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.bind.JAXBException;
import java.io.IOException;
import java.math.BigInteger;
import java.util.HashMap;



public final class NumberingDefinitionsPart extends JaxbXmlPartXPathAware {
	
	private static Logger log = LoggerFactory.getLogger(NumberingDefinitionsPart.class);	
	
	public NumberingDefinitionsPart(PartName partName) throws InvalidFormatException {
		super(partName);
		init();
	}
	
	public NumberingDefinitionsPart() throws InvalidFormatException {
		super(new PartName("/word/numbering.xml"));
		init();
	}

	public void init() {	
		// Used if this Part is added to [Content_Types].xml 
		setContentType(new  org.docx4j.openpackaging.contenttype.ContentType( 
				org.docx4j.openpackaging.contenttype.ContentTypes.WORDPROCESSINGML_NUMBERING));

		// Used when this Part is added to a rels 
		setRelationshipType(Namespaces.NUMBERING);
	}
	
	@Override
    protected void setMceIgnorable(McIgnorableNamespaceDeclarator namespacePrefixMapper) {

		// NB if you add ignorable content, it is up to you to jaxbElement.setIgnorable correctly; see further McIgnorableNamespaceDeclarator
		// You don't need to worry about this if you are merely loading an existing part.
		namespacePrefixMapper.setMcIgnorable(
				this.getJaxbElement().getIgnorable() );
	}

	@Override
	public String getMceIgnorable() {
    	return this.getJaxbElement().getIgnorable();
    }
	
	
    HashMap abstractListDefinitions; 
	public HashMap getAbstractListDefinitions() {
		if (abstractListDefinitions==null) initialiseMaps();
		return abstractListDefinitions;
	}

    HashMap instanceListDefinitions; 
	public HashMap getInstanceListDefinitions() {
		
		if (instanceListDefinitions==null) initialiseMaps();
		
		return instanceListDefinitions;
	}
	
    public void initialiseMaps()
    {
    	Numbering numbering = getJaxbElement();
    	
        // count the number of different list numbering schemes
    	if (numbering.getNum().size() == 0)
        {
    		log.debug("No num defined");
    		// Don't return; init empty lists.
        }
        
        // initialize the abstract number list
        abstractListDefinitions 
        	= new HashMap(numbering.getAbstractNum().size() );
                
        // initialize the instance number list
        instanceListDefinitions 
        	= new HashMap( numbering.getNum().size() );

        // store the abstract list type definitions
        for (Numbering.AbstractNum abstractNumNode : numbering.getAbstractNum() )
        {
            AbstractListNumberingDefinition absNumDef 
            	= new AbstractListNumberingDefinition(abstractNumNode);

            abstractListDefinitions.put(absNumDef.getID(), absNumDef);

        }

        // instantiate the list number definitions
        for( Numbering.Num numNode : numbering.getNum() )
        {
            ListNumberingDefinition listDef 
            	= new ListNumberingDefinition(numNode, abstractListDefinitions);

            instanceListDefinitions.put(listDef.getListNumberId(), listDef);
//            log.debug("Added list: " + listDef.getListNumberId() );
        }

    }
    
    public void resolveLinkedAbstractNum(StyleDefinitionsPart sdp) {
    	
    	if (sdp==null) {
    		log.warn("No StyleDefinitionsPart found");
    		return;
    	}
    	
        for (Numbering.AbstractNum abstractNum : getJaxbElement().getAbstractNum() ) {
        	
        	// 
        	if (abstractNum.getNumStyleLink()==null) continue;
        	
        	// there is also abstractNum.getStyleLink(), but ignore that
        	
        	String numStyleId = abstractNum.getNumStyleLink().getVal();
        	
        	Style s = sdp.getStyleById(numStyleId);
        	if (s==null) {
        		log.warn("For w:numStyleLink, couldn't find style " + numStyleId);
        		continue;
        	}
        	if (s.getPPr()==null || s.getPPr().getNumPr()==null) {
        		log.warn("For w:numStyleLink, style " + numStyleId + " has no w:numPr");
        		continue;        		
        	}
        	
        	NumPr styleNumPr = s.getPPr().getNumPr();
        	
        	// Get the concrete list this point to
        	if (styleNumPr.getNumId()==null) {
        		log.warn("For w:numStyleLink, style " + numStyleId + " w:numPr has no w:numId");
        		continue;        		        		
        	}
        	BigInteger concreteListId = styleNumPr.getNumId().getVal();
        	
        	// Get the target abstract num
        	ListNumberingDefinition lnd = getInstanceListDefinitions().get(concreteListId.toString());
        	if (lnd==null) {
        		log.warn("No ListNumberingDefinition entry with ID " + concreteListId.toString());
        	}
        	Numbering.AbstractNum linkedNum = lnd.getAbstractListDefinition().getAbstractNumNode();

        	// OK, update
            AbstractListNumberingDefinition absNumDef 
            	= abstractListDefinitions.get(abstractNum.getAbstractNumId().toString());
            
            absNumDef.updateDefinitionFromLinkedStyle(linkedNum);
            
            // Also update the underlying abstract list
            if (abstractNum.getLvl().size()>0) {
            	log.warn("Cowardly refusing to overwrite existing List" );
            } else {
            	abstractNum.getLvl().clear();
            	abstractNum.getLvl().addAll(linkedNum.getLvl());
            		// This list is treated as a separate list by Word (ie its numbers are incremented
            		// independently), and this code honours that.
            }
            
            
            log.info("Updated abstract list def " + abstractNum.getAbstractNumId().toString() + " based on w:numStyleLink " + numStyleId );
        }
    	
    	
    }
    
    /**
     * For the given *concrete* list numId, restart the numbering on the specified
     * level at value val.  This is done by creating a new list (ie <w:num>)
     * which uses the existing w:abstractNum.
     * @param numId
     * @param ilvl
     * @param val
     * @return 
     */
    public long restart(long numId, long ilvl, long val) 
    	throws InvalidOperationException {
    	
    	// Find the abstractNumId
    	
    	// (Ensure maps are initialised)
    	if (em == null ) { 
    		getEmulator();
    	}
    	
    	ListNumberingDefinition existingLnd = instanceListDefinitions.get( Long.toString(numId) );
    	if (existingLnd==null) {
    		throw new InvalidOperationException("Concrete/instance list " + numId + " does not exist");
    	}
    	
    	return restart(existingLnd.getNumNode(), ilvl, val);
    }
    

    private long restart(Num num, long ilvl, long val) 
    	throws InvalidOperationException {

    	if (num==null) {
    		throw new InvalidOperationException("Abstract List does not exist!");
    	}
    	
    	// (Ensure maps are initialised)
    	if (em == null ) { 
    		getEmulator();
    	}
    	
    	// Get the abstract list    	
    	BigInteger abstractNumIdVal = num.getAbstractNumId().getVal();
    	
    	// Generate the new 
		//	  
		//	"        
        Numbering.Num num = Context.getWmlObjectFactory().createNumberingNum();
        Numbering.Num.AbstractNumId abstractNumId = Context.getWmlObjectFactory().createNumberingNumAbstractNumId();
        abstractNumId.setVal(BigInteger.valueOf(nextId) );
        num.setAbstractNumId(abstractNumId);
        
        nextId = getInstanceListDefinitions().size();		
    	do {
    		nextId++;    		
    	} while (getInstanceListDefinitions().containsKey( "" + nextId ));    	
    	num.setNumId( BigInteger.valueOf(nextId) );  
    	
    	// Add it to our JAXB object
    	this.getJaxbElement().getNum().add(num);
    	
    	// Add it to our hashmap
        ListNumberingDefinition listDef = new ListNumberingDefinition(num, abstractListDefinitions);
        instanceListDefinitions.put(listDef.getListNumberId(), listDef);
        
        // 
    	return num;
		
	}

	public void addAbstractListNumberingDefinitionLevel(Numbering.AbstractNum abstractNum, Lvl lvl) {
		
		abstractNum.getLvl().add( lvl ); 
		
		// update the corresponding structure
		AbstractListNumberingDefinition absNumDef = abstractListDefinitions.get(abstractNum.getAbstractNumId().toString());
		absNumDef.readLevel(lvl);

	}
	
    public Numbering unmarshalDefaultNumbering() throws JAXBException {
    	    	    	 
		java.io.InputStream is = null;
		try {
			is = ResourceUtils.getResourceViaProperty(
					"docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart.DefaultNumbering",
					"org/docx4j/openpackaging/parts/WordprocessingML/numbering.xml");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}    		
    	
    	return unmarshal( is );    // side-effect is to set jaxbElement 	
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy