/*
* Copyright(C) 2001 Mika Riekkinen, Joni Suominen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or(at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package alt.jiapi.event;
import alt.jiapi.Rule;
/**
* Base class for event producers.
*
* @author Mika Riekkinen
* @author Joni Suominen
* @version $Revision: 1.9 $ $Date: 2004/03/22 09:04:17 $
*/
public class EventProducer {
private String[] resolutions;
private Rule[] rules;
/**
* IdentityList is used for locks to avoid StackOverflow
* in cases where the lock object implements hashCode method
* which has been instrumented.
*/
private IdentityList locks = new IdentityList();
/**
* Empty constructor. This constructor will set up resolution
* to its default value "*".
*/
public EventProducer() {
this(new String[] {"*"});
}
/**
* This constructor will set up resolution to given value.
*
* @param resolution A resolution that is to be used.
*/
public EventProducer(String resolution) {
this(new String[] {resolution});
}
/**
* This constructor will set up resolution to given values.
*
* @param resolution A resolutions that is to be used.
*/
public EventProducer(String[] resolutions) {
this.resolutions = resolutions;
this.rules = new Rule[resolutions.length];
for (int i = 0; i < rules.length; i++) {
try {
rules[i] = new Rule(resolutions[i]);
}
catch(Exception e) {
System.out.println("Invalid rule: " + e);
}
}
}
/**
* Matches a given String into resolution String this EventProducer has.
* If at least one resolution matches, a true is returned.
*/
public boolean match(String s) {
for (int i = 0; i < rules.length; i++) {
if (rules[i].match(s) == true) {
return true;
}
}
return false;
}
/**
* Get the resolutions of this EventProducer.
*
* @return An array of Strings. Each String represents one
* resolution String, that is used in matching.
*/
public String[] getResolutions() {
return resolutions;
}
/**
* Checks whether a given sourceObject is in protected mode.
* if sourceObject is in protected mode, corresponding JiapiEvent
* should not be fired.
*
* @param sourceObject sourceObject to check
*/
public boolean isProtected(Object sourceObject) {
return locks.contains(sourceObject);
}
/**
* Checks whether a given JiapiEvent is in protected mode.
* if JiapiEvent is in protected mode, that event should not
* be fired.
*
* @param je JiapiEvent to check
*/
public boolean isProtected(JiapiEvent je) {
Object sourceObject = je.getSourceObject();
return locks.contains(sourceObject);
}
/**
* This method protects application from entering into
* recursive event loop. This situation may occur, if a method
* has been instrumented, and instrumentation produces an
* event. If that method is called, directly or indirectly,
* with the aid of JiapiEvent, application will enter to an
* endless event loop, eventually crashing Virtual Machine.<p>
*
* Calling this method prevents <code>EventProducer</code>
* from producing further events for the same sourceObject.
* This protection mechanism allows applications to call
* methods of sourceObject and targetObject without worrying
* about event loops.<p>
*
* To enable events again, one will have to release
* <code>EventProducer</code> with method <code>release</code>.<p>
*
* @see #release(JiapiEvent)
* @see JiapiEvent#protect()
*/
public void protect(JiapiEvent je) {
Object sourceObject = je.getSourceObject();
locks.add(sourceObject);
}
/**
* This method releases <code>EventProducer</code> so, that
* it is able to produce more events for the sourceObject.
*
* @see #protect(JiapiEvent)
* @see JiapiEvent#release()
*/
public void release(JiapiEvent je) {
Object sourceObject = je.getSourceObject();
locks.remove(sourceObject);
}
}
/**
* A List-like implementation where reference-equality is used instead
* of object-equality (i.e. == instead of equals()).
* <p>
* Since only a partial List functionality is needed it was easier
* (and safer) not to implement a List or extend AbstractList. This
* way we don't have to care how the unused methods use the
* entries in the set.
*/
class IdentityList {
private Object []elementData;
private int elementCount;
public IdentityList() {
elementData = new Object[10];
elementCount = 0;
}
public synchronized void add(Object o) {
checkCapacity();
elementData[elementCount++] = o;
}
public synchronized void remove(Object o) {
int index = indexOf(o);
if (index != -1) {
removeElementAt(index);
}
}
public synchronized boolean contains(Object o) {
return indexOf(o) >= 0;
}
private int indexOf(Object o) {
for (int i = 0; i < elementCount; i++) {
if (o == elementData[i]) {
return i;
}
}
return -1;
}
private void removeElementAt(int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1;
if (j > 0) {
System.arraycopy(elementData, index + 1, elementData, index, j);
}
elementCount--;
elementData[elementCount] = null;
}
private void checkCapacity() {
int curCapacity = elementData.length;
if (elementCount >= curCapacity) {
Object oldData[] = elementData;
int newCapacity = curCapacity * 2;
elementData = new Object[newCapacity];
System.arraycopy(oldData, 0, elementData, 0, elementCount);
}
}
}