/*
* This file is part of JOP, the Java Optimized Processor
* see <http://www.jopdesign.com/>
*
* Copyright (C) 2010, Stefan Hepp (stefan@stefant.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jopdesign.common;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import java.util.HashMap;
import java.util.Map;
/**
* @author Stefan Hepp (stefan@stefant.org)
*/
public class KeyManager {
public enum KeyType {
// If required, types 'Annotation' and 'Attribute' could be added too, keyname would
// represent annotation-typename or attribute-name
STRUCT, CODE, BOTH;
public boolean isStruct() { return this == STRUCT || this == BOTH; }
public boolean isCode() { return this == CODE || this == BOTH; }
}
private static final Object KEY_INSTRUCTION_VALUE = "KeyManager.InstructionValue";
private static final Object KEY_BLOCK_VALUE = "KeyManager.BlockValue";
// Clearing registeredKeys is not a good idea, since many keys are stored as static field!
private final Map<String, CustomKey> registeredKeys;
private int maxStructKeyID;
/**
* A class for custom attribute key.
* To create a new key, use {@link #registerKey(KeyType,String, AppEventHandler)}.
*/
public static final class CustomKey {
private final String keyname;
private final KeyType type;
private final AppEventHandler manager;
private boolean clearOnInvalidate;
private final int id;
private CustomKey(KeyType type, String keyname, AppEventHandler manager, int id) {
this.type = type;
this.keyname = keyname;
this.manager = manager;
this.id = id;
clearOnInvalidate = false;
}
public KeyType getType() {
return type;
}
public String getKeyname() {
return keyname;
}
public AppEventHandler getManager() {
return manager;
}
/* not yet implemented
public boolean doClearOnInvalidate() {
return clearOnInvalidate;
}
public void setClearOnInvalidate(boolean clearOnInvalidate) {
this.clearOnInvalidate = clearOnInvalidate;
}
*/
public int hashCode() {
return keyname.hashCode();
}
/**
* Two CustomKeys are equal, if their keynames are equal.
* @param o the object to test.
* @return true if equal.
*/
public boolean equals(Object o) {
if ( o instanceof CustomKey ) {
return ((CustomKey)o).getKeyname().equals(keyname);
}
return false;
}
int getId() {
return id;
}
}
//////////////////////////////////////////////////////////////////////////////
// Singleton
//////////////////////////////////////////////////////////////////////////////
private static final KeyManager singleton = new KeyManager();
public static KeyManager getSingleton() {
return singleton;
}
private KeyManager() {
registeredKeys = new HashMap<String, CustomKey>();
maxStructKeyID = 0;
}
public AppInfo getAppInfo() {
return AppInfo.getSingleton();
}
//////////////////////////////////////////////////////////////////////////////
// Key registration
//////////////////////////////////////////////////////////////////////////////
/**
* Register a new key which can be used to attach values to structure elements (i.e. all {@link MemberInfo}).
*
* @see #registerKey(KeyType, String, AppEventHandler)
* @param keyname a unique keyname for the key
* @return the new key
*/
public CustomKey registerStructKey(String keyname) {
return registerKey(KeyType.STRUCT, keyname, null);
}
/**
* Register a new key which can be used to attach values to instructions or code blocks).
*
* @see #registerKey(KeyType, String, AppEventHandler)
* @param keyname a unique keyname for the key
* @return the new key
*/
public CustomKey registerCodeKey(String keyname) {
return registerKey(KeyType.CODE, keyname, null);
}
/**
* Register a new key which can be attached to memberInfos or code or both.
* @param type the type of the key.
* @param keyname the name of the key.
* @return a key by that name.
*/
public CustomKey registerKey(KeyType type, String keyname) {
return registerKey(type, keyname, null);
}
public CustomKey registerKey(KeyType type, String keyname, AppEventHandler manager) {
// check if exists
CustomKey k = registeredKeys.get(keyname);
if ( k != null ) {
if (k.getType() != type || k.getManager() != manager) {
throw new IllegalArgumentException("CustomKey "+keyname+" already exists but has a different definition!");
}
return k;
}
// currently there is no way to unregister a key and storing struct-keys might get replaced by maps for
// memory-reasons, so a single ID-counter is fine; ID is only used for values attached to memberinfos
int id = type.isStruct() ? maxStructKeyID++ : -1 ;
k = new CustomKey(type, keyname, manager, id);
registeredKeys.put(keyname, k);
return k;
}
public CustomKey getRegisteredKey(String key) {
return registeredKeys.get(key);
}
//////////////////////////////////////////////////////////////////////////////
// Key management
//////////////////////////////////////////////////////////////////////////////
public void clearAllValues(CustomKey key) {
// do we need a version of this method with more fine-grained control (clear only from methods,..)?
for ( ClassInfo cls : getAppInfo().getClassInfos() ) {
clearAllValues(key, cls);
}
}
public void clearAllValues(CustomKey key, ClassInfo classInfo) {
boolean fromStruct = key.getType().isStruct();
boolean fromCode = key.getType().isCode();
if ( fromStruct ) {
classInfo.removeCustomValue(key);
for ( FieldInfo field : classInfo.getFields() ) {
field.removeCustomValue(key);
}
}
for ( MethodInfo method : classInfo.getMethods() ) {
if ( fromStruct ) {
method.removeCustomValue(key);
}
if ( fromCode && method.hasCode() ) {
MethodCode code = method.getCode();
InstructionList il = code.getInstructionList();
for (InstructionHandle ih : il.getInstructionHandles()) {
code.clearCustomKey(ih, key);
}
}
}
}
public void updateKeys(CustomKey[] keep) {
// TODO invalidate all keys which have not been handled/kept
// for each invalidated key: try manager first if set, invalidate
// for each key not in keep: clear if requested by key
}
public void addedKeys(CustomKey[] added) {
// TODO find better name
// TODO add keys to list of up-to-date-keys (e.g. as a result of an analysis)
}
public boolean isSet(CustomKey keys) {
// TODO check list of up-to-date-keys
return false;
}
//////////////////////////////////////////////////////////////////////////////
// Internal Affairs
//////////////////////////////////////////////////////////////////////////////
/**
* Get number of registered struct keys. Should be accessed only by MemberInfo.
*
* @return number of currently registered keys which can be attached to a MemberInfo
*/
int getNumStructKeys() {
return maxStructKeyID;
}
}