/* * Scriptographer * * This file is part of Scriptographer, a Scripting Plugin for Adobe Illustrator * http://scriptographer.org/ * * Copyright (c) 2002-2010, Juerg Lehni * http://scratchdisk.com/ * * All rights reserved. See LICENSE file for details. * * File created on 15.02.2005. */ package com.scriptographer; import java.util.Collection; import java.util.LinkedHashMap; import com.scratchdisk.script.rhino.ExtendedJavaObject; import com.scriptographer.ai.TextItem; /** * @author lehni */ public class CommitManager { private CommitManager() { // Don't let anyone instantiate this class. } private static CommittableMap committables = new CommittableMap(); /** * The version that gets increased by one on each commit. This can be used * to keep track of values that need synching only once on each execution of * a script, or when things are changed * * It also gets increased in Item.updateIfWrapped */ public static int version = 0; /** * Commits changes to objects that are associated with the given key this is * usually a item, where path styles and segment lists use the item * as a key when calling markDirty. If key is null, all changes are * committed. * * @return true if anything was committed. */ protected static boolean commit(Object key, boolean endExecution) { boolean committed = false; if (key != null) { Committable obj = committables.get(key); if (obj != null) { obj.commit(endExecution); committed = true; // case it's a text, use the story as a key as well. it's used // like that in CharacterAttributes if (obj instanceof TextItem) commit(((TextItem) obj).getStory(), endExecution); // Remove object after committing committables.remove(key); } } else if (committables.size() > 0) { for (Committable committable : committables.values()) committable.commit(endExecution); committed = true; committables.clear(); } return committed; } /** * Commits changes to objects that are associated with the given key this is * usually a item, where path styles and segment lists use the item * as a key when calling markDirty. If key is null, all changes are * committed. * * @return true if anything was committed. */ public static boolean commit(Object key) { return commit(key, false); } /** * commit all changes and increases the internal commit version */ public static boolean commit() { version++; // Also set the modificationVersion to allow versioning for increased performance. ExtendedJavaObject.changeVersion = version; return commit(null, true); } public static void markDirty(Object key, Committable committable) { committables.put(key, committable); } /** * a hash map that overrides put so that it creates a CommittableList in case * there was one object under a key already Uses IdentityHashMaps * internally, to avoid calling of equals on objects */ /* * Ideally we would be using a LinkedIdentityHashMap here but that class does not exist. */ static class CommittableMap { // A linked map of committables identified by the identity hash of their // key object. If there is more than one committable per key, a // CommittableGroup is created under the key LinkedHashMap<Integer, Committable> committables = new LinkedHashMap<Integer, Committable>(); // Keep track of values that have been added already, identified by // their own identity hash. Committables that are grouped are still kept // in one list here too. LinkedHashMap<Integer, Committable> values = new LinkedHashMap<Integer, Committable>(); /** * A helper class that's needed when there are more than on object for * one key. It forwards calls to commit() It actually uses a map so that * every object can only be added once in order to void more than one * call to commit at a time * * Use a LinkedHashMap in order to preserve sequence of commits */ static class CommittableGroup implements Committable { LinkedHashMap<Integer, Committable> committables = new LinkedHashMap<Integer, Committable>(); public void commit(boolean endExecution) { for (Committable committable : committables.values()) committable.commit(endExecution); } public void add(Committable obj) { committables.put(System.identityHashCode(obj), obj); } public Collection<Committable> values() { return committables.values(); } } public Committable put(Object key, Committable obj) { int valueHash = System.identityHashCode(obj); if (values.containsKey(valueHash)) return null; values.put(valueHash, obj); int keyHash = System.identityHashCode(key); Committable prev = committables.get(keyHash); if (prev != null) { if (prev instanceof CommittableGroup) { // Add to existing group ((CommittableGroup) prev).add(obj); return prev; } // Create a new group, add both, and put back CommittableGroup list = new CommittableGroup(); list.add(prev); list.add(obj); return committables.put(keyHash, list); } // Simply add this object return committables.put(keyHash, obj); } public Committable get(Object key) { return committables.get(System.identityHashCode(key)); } public Collection<Committable> values() { return committables.values(); } public int size() { return committables.size(); } public Committable remove(Object key) { int keyHash = System.identityHashCode(key); Committable obj = committables.remove(keyHash); if (obj instanceof CommittableGroup) { for (Committable committable : ((CommittableGroup) obj).values()) { values.remove(System.identityHashCode(committable)); } } else { values.remove(System.identityHashCode(obj)); } return obj; } public void clear() { committables.clear(); values.clear(); } } }