blob: ff6ececdd3a97e716d07e91b6678b7b39052260b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.e4.core.services.internal.context;
import java.lang.reflect.*;
import org.eclipse.e4.core.services.context.IEclipseContext;
import org.eclipse.e4.core.services.context.spi.IJavaInjection;
/**
* The first character of the service name is not case-sensitive; rest is case-sensitive:
* <default_prefix>Log <-> Log <-> log
*/
public class ContextInjectionImpl implements IJavaInjection {
abstract private class Processor {
protected boolean isSetter;
public Processor(boolean isSetter) {
this.isSetter = isSetter;
}
// true if a field was set
abstract void processMethod(Method method);
// true if a field was set
abstract void processField(Field field);
}
final static private String JAVA_OBJECT = "java.lang.Object"; //$NON-NLS-1$
final static private Class[] contextNotifySignature = new Class[] {IEclipseContext.class};
final protected String fieldPrefix;
final protected String setMethodPrefix;
final protected int fieldPrefixLength;
public ContextInjectionImpl() {
this(FIELD_PREFIX, SET_METHOD_PREFIX);
}
public ContextInjectionImpl(String fieldPrefix, String setMethodPrefix) {
this.fieldPrefix = (fieldPrefix != null) ? fieldPrefix : FIELD_PREFIX;
this.setMethodPrefix = (setMethodPrefix != null) ? setMethodPrefix : SET_METHOD_PREFIX;
fieldPrefixLength = this.fieldPrefix.length();
}
public void injectInto(final Object userObject, final IEclipseContext context) {
final Processor processor = new Processor(true /*setter*/) {
public void processField(final Field field) {
final String candidateName = field.getName();
if (!candidateName.startsWith(fieldPrefix))
return;
FieldLink link = new FieldLink(userObject, context, candidateName.substring(fieldPrefixLength), field);
context.runAndTrack(link, "Java Field Reflection Injection");
}
public void processMethod(final Method method) {
final String candidateName = method.getName();
if (!candidateName.startsWith(SET_METHOD_PREFIX))
return;
MethodLink link = new MethodLink(userObject, context, candidateName.substring(SET_METHOD_PREFIX.length()), method);
context.runAndTrack(link, "Java Method Reflection Injection");
}
};
walkClassHierarchy(userObject.getClass(), processor);
// trigger post-injection processing
notifyUserMethod(CONTEXT_SET_METHOD, userObject, context);
}
/**
* For setters: we set fields first, them methods.
* Otherwise, clear methods first, fields next
*/
private void walkClassHierarchy(Class objectsClass, Processor processor) {
// process superclass first
Class superClass = objectsClass.getSuperclass();
if (!superClass.getName().equals(JAVA_OBJECT))
walkClassHierarchy(superClass, processor);
if (processor.isSetter) {
// fields second
Field[] fields = objectsClass.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
processor.processField(fields[i]);
}
// methods last
Method[] methods = objectsClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
processor.processMethod(methods[i]);
}
} else {
// methods second
Method[] methods = objectsClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
processor.processMethod(methods[i]);
}
// fields last
Field[] fields = objectsClass.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
processor.processField(fields[i]);
}
}
}
private void notifyUserMethod(String methodName, Object userObject, IEclipseContext newContext) {
// perform post-injection processing
Class objectClass = userObject.getClass();
boolean wasAccessible = true;
Method method = null;
try {
method = objectClass.getMethod(methodName, contextNotifySignature);
if (!method.isAccessible()) {
method.setAccessible(true);
wasAccessible = false;
}
method.invoke(userObject, new Object[] {newContext});
} catch (SecurityException e) {
logWarning(userObject, e);
} catch (NoSuchMethodException e) {
// fine - nothing to call
} catch (IllegalArgumentException e) {
logWarning(userObject, e);
} catch (IllegalAccessException e) {
logWarning(userObject, e);
} catch (InvocationTargetException e) {
logWarning(userObject, e);
} finally {
if (!wasAccessible && method != null)
method.setAccessible(false);
}
}
private void logWarning(Object destination, Exception e) {
System.out.println("Injection failed " + destination.toString());
if (e != null)
e.printStackTrace();
// TBD convert this into real logging
// String msg = NLS.bind("Injection failed", destination.toString());
// RuntimeLog.log(new Status(IStatus.WARNING, IRuntimeConstants.PI_COMMON, 0, msg, e));
}
}