/*
* 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 24.03.2005.
*/
package com.scriptographer.ai;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import com.scratchdisk.util.SoftIntMap;
import com.scriptographer.ScriptographerException;
/**
* @author lehni
*/
abstract class NativeObject implements ValidationObject {
// used for storing the native handle for this object
protected int handle;
protected NativeObject() {
handle = 0;
}
protected NativeObject(int handle) {
this.handle = handle;
}
protected static NativeObject wrapHandle(Class cls, int handle,
Document document) {
if (handle == 0)
return null;
WrapFactory factory = factories.get(cls);
if (factory == null) {
factory = new WrapFactory(cls);
factories.put(cls, factory);
}
return factory.wrapHandle(handle, document);
}
protected static NativeObject wrapHandle(Class cls, int handle) {
return wrapHandle(cls, handle, null);
}
protected boolean nativeRemove() {
return false;
}
/**
* protected scaffold function for removing. subclasses that want to use it
* need to override nativeRemove and make remove public
*
*/
protected boolean remove() {
boolean ret = false;
if (handle != 0) {
ret = nativeRemove();
if (ret) {
WrapFactory factory = factories.get(getClass());
if (factory != null)
factory.wrappers.remove(handle);
handle = 0;
}
}
return ret;
}
public int hashCode() {
// Return the native handle here as hashCode, as we use equals() in
// quite a few places to see if wrapper objects are actually
// representing the same native object. For example this is used when
// reusing live effects and menu groups / items after the plug-in was
// reloaded.
return handle != 0 ? handle : super.hashCode();
}
public boolean isValid() {
return handle != 0;
}
public boolean equals(Object obj) {
// Some objects subclass NativeObject and do not use handle,
// use a fallback scenario for these!
if (handle == 0) {
return this == obj;
} else if (obj instanceof NativeObject) {
return this == obj ||
handle == ((NativeObject) obj).handle
&& getClass().equals(obj.getClass());
}
return false;
}
/**
* @jshide
*/
public Object getId() {
return "@" + Integer.toHexString(hashCode());
}
public String toString() {
return getClass().getSimpleName() + " "
+ (isValid() ? getId() : "<invalid>");
}
// Cache the factories for the various wrapper classes which use this base class
private static HashMap<Class, WrapFactory> factories
= new HashMap<Class, WrapFactory>();
private static class WrapFactory {
// Use a SoftIntMap to keep track of already wrapped objects per factory:
SoftIntMap<NativeObject> wrappers = new SoftIntMap<NativeObject>();
boolean hasDocument;
Constructor ctor;
WrapFactory(Class<?> cls) {
// Get the constructor for this class
try {
// Use the (int, Document) constructor for DocumentObjects,
// (int) otherwise
hasDocument = DocumentObject.class.isAssignableFrom(cls);
ctor = cls.getDeclaredConstructor(hasDocument
? new Class[] { Integer.TYPE, Document.class }
: new Class[] { Integer.TYPE });
} catch (Exception e) {
throw new ScriptographerException(e);
}
}
NativeObject wrapHandle(int handle, Document document) {
if (handle == 0)
return null;
NativeObject obj = wrappers.get(handle);
if (obj == null) {
try {
// Now create a new instance, passing the handle as a
// parameter
obj = (NativeObject) ctor.newInstance(hasDocument
? new Object[] { handle, document != null
? document
: Document.getWorkingDocument() }
: new Object[] { handle });
wrappers.put(handle, obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return obj;
}
}
}