/*
* 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 Jul 10, 2009.
*/
package com.scriptographer.list;
import com.scriptographer.CommitManager;
import com.scriptographer.Committable;
/**
* An abstract list to reflect simple struct based native lists, as used by
* GradientStop and Artboard.
*
* @author lehni
*/
public abstract class AbstractStructList<R, E extends AbstractStructList.Entry<R>>
extends AbstractNativeList<E> {
protected R reference;
protected int version = -1;
protected AbstractStructList(R reference) {
this.reference = reference;
update();
}
protected abstract int nativeGetSize();
protected abstract int nativeRemove(int fromIndex, int toIndex);
protected abstract E createEntry(int index);
/**
* Updates the synchronization between the cached segments in java
* and the underlying Illustrator object.
* Only called from Gradient.getStops()
*/
public void update() {
if (reference != null && version != CommitManager.version) {
size = nativeGetSize();
list.setSize(size);
version = CommitManager.version;
}
}
public E get(int index) {
E element = list.get(index);
if (element == null) {
element = createEntry(index);
list.set(index, element);
}
// Always update, even when newly fetched, to make sure it is
// initialised.
return element.update() ? element : null;
}
public E add(int index, E element) {
// Add to internal structure
list.add(index, element);
// Update version:
element.version = CommitManager.version;
element.index = index;
element.reference = reference;
// And add to illustrator as well
if (element.insert()) {
// Increase size
size++;
// Update indices
for (int i = index + 1; i < size; i++) {
E e = list.get(i);
if (e != null)
e.index = i;
}
return element;
}
return null;
}
public E set(int index, E element) {
E ret = get(index);
list.set(index, element);
element.reference = reference;
element.index = index;
element.markDirty();
// It might be that this element was already inserted elsewhere in the
// same list.
// Only clear reference / index if it is still valid.
if (ret != null && ret.index == index) {
ret.reference = null;
ret.index = -1;
}
return ret;
}
public void remove(int fromIndex, int toIndex) {
if (fromIndex < toIndex) {
int newSize = size + fromIndex - toIndex;
for (int i = fromIndex; i < toIndex; i++) {
E obj = list.get(i);
// Again, only clear reference / index if it's actually still valid
if (obj != null && obj.index == i) {
obj.reference = null;
obj.index = -1;
}
}
if (reference != null)
size = nativeRemove(fromIndex, toIndex);
list.remove(fromIndex, toIndex);
size = newSize;
}
}
public abstract static class Entry<R> implements Committable {
protected R reference;
protected int index;
protected boolean dirty;
protected int version = -1;
protected Entry() {
reference = null;
index = -1;
// A new entry is dirty by default, since this is the base
// constructor used by initialising constructors.
dirty = true;
}
protected Entry(R reference, int index) {
this.reference = reference;
this.index = index;
// A representation for an existing entry is not dirty, since it
// will be fetched from the array.
this.dirty = false;
}
protected void markDirty() {
// Only mark it as dirty if it's attached to an object already and
// if the dirty flag is not already set.
// Put both non dirty ones that were fetched before and dirty ones
// that were never fetched so far (= newly created entries) into
// the committable list.
if (reference != null && (!dirty && version != -1 || dirty && version == -1)) {
CommitManager.markDirty(reference, this);
dirty = true;
}
}
protected abstract boolean nativeInsert();
protected abstract boolean nativeSet();
protected abstract boolean nativeGet();
protected boolean insert() {
if (reference != null && index != -1) {
CommitManager.commit(reference);
if (nativeInsert()) {
version = CommitManager.version;
dirty = false;
return true;
}
}
return false;
}
public void commit(boolean endExecution) {
if (dirty && reference != null && index != -1 && nativeSet()) {
version = CommitManager.version;
dirty = false;
}
}
protected boolean update() {
if (!dirty && reference != null && index != -1
&& version != CommitManager.version) {
if (!nativeGet())
return false;
version = CommitManager.version;
}
return true;
}
/**
* The index of the object in the list it belongs to.
*/
public int getIndex() {
return index;
}
}
}