/* * Copyright 2006-2017 ICEsoft Technologies Canada Corp. * * 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.icepdf.core.pobjects; import java.util.*; import java.util.logging.Logger; /** * This class is responsible for keeping track of which object in the document * have change. When a file is written to disk this class is used to find * the object that should be written in the body section of the file as part of * an incremental update. * <br> * Once this object is created should be added to the library so that is * accessible by any PObject. * * @since 4.0 */ public class StateManager { private static final Logger logger = Logger.getLogger(StateManager.class.getName()); // a list is all we might need. private HashMap<Reference, PObject> changes; // access to xref size and next revision number. private PTrailer trailer; private int nextReferenceNumber; /** * Creates a new instance of the state manager. * * @param trailer document trailer */ public StateManager(PTrailer trailer) { this.trailer = trailer; // cache of objects that have changed. changes = new HashMap<Reference, PObject>(); // number of objects is always one more then the current size and // thus the next available number. if (trailer != null) { nextReferenceNumber = trailer.getNumberOfObjects(); } } /** * Gets the next available reference number from the trailer. * * @return valid reference number. */ public Reference getNewReferencNumber() { // zero revision number for now but technically we can reuse // deleted references and increment the rev number. For no we // keep it simple Reference newReference = new Reference(nextReferenceNumber, 0); nextReferenceNumber++; return newReference; } /** * Add a new PObject containing changed data to the cache. * * @param pObject object to add to cache. */ public void addChange(PObject pObject) { changes.put(pObject.getReference(), pObject); int objectNumber = pObject.getReference().getObjectNumber(); // check the reference numbers if (nextReferenceNumber <= objectNumber) { nextReferenceNumber = objectNumber + 1; } } /** * Checks the state manager to see if an instance of the specified reference * already exists in the cache. * * @param reference reference to look for an existing usage. * @return true if reference is already a key in the cache; otherwise, false. */ public boolean contains(Reference reference) { return changes.containsKey(reference); } /** * Returns an instance of the specified reference * * @param reference reference to look for an existing usage * @return PObject of corresponding reference if present, false otherwise. */ public Object getChange(Reference reference) { return changes.get(reference); } /** * Remove a PObject from the cache. * * @param pObject pObject to removed from the cache. */ public void removeChange(PObject pObject) { changes.remove(pObject.getReference()); } /** * @return If there are any changes */ public boolean isChanged() { return !changes.isEmpty(); } /** * Gets the number of change object in the state manager. * * @return zero or more changed object count. */ public int getChangedSize() { return changes.size(); } /** * @return An Iterator<PObject> for all the changes objects, sorted */ public Iterator<PObject> iteratorSortedByObjectNumber() { Collection<PObject> coll = changes.values(); /* * This code allows me to force an object to be treated as modified, * so I can debug how we write out that kind of object, before we * add a ui to actually edit it. Reference ref = new Reference(10,0); Object ob = trailer.getLibrary().getObject(ref); logger.severe("Object 10: " + ob + " ob.class: " + ob.getClass().getName()); java.util.HashSet<PObject> hs = new java.util.HashSet<PObject>(coll); hs.add(new PObject(ob, ref)); coll = hs; */ PObject[] arr = coll.toArray(new PObject[coll.size()]); Arrays.sort(arr, new PObjectComparatorByReferenceObjectNumber()); List<PObject> sortedList = Arrays.asList(arr); return sortedList.iterator(); } public PTrailer getTrailer() { return trailer; } private static class PObjectComparatorByReferenceObjectNumber implements Comparator<PObject> { public int compare(PObject a, PObject b) { if (a == null && b == null) return 0; else if (a == null) return -1; else if (b == null) return 1; Reference ar = a.getReference(); Reference br = b.getReference(); if (ar == null && br == null) return 0; else if (ar == null) return -1; else if (br == null) return 1; int aron = ar.getObjectNumber(); int bron = br.getObjectNumber(); if (aron < bron) return -1; else if (aron > bron) return 1; return 0; } } }