This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

Bug 122441 - JMI like API is missing
Summary: JMI like API is missing
Status: NEW
Alias: None
Product: java
Classification: Unclassified
Component: Source (show other bugs)
Version: 6.x
Hardware: All All
: P3 blocker with 1 vote (vote)
Assignee: Rastislav Komara
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2007-11-20 22:27 UTC by greggwon
Modified: 2009-02-03 10:53 UTC (History)
2 users (show)

See Also:
Issue Type: ENHANCEMENT
Exception Reporter:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description greggwon 2007-11-20 22:27:38 UTC
The replacement for JMI does not provide a similar API, and this makes it necessary for all source code analysis tools
to be either completely reengineered, or rewritten, or removed.  There should be an API provided by a class in the IDE
core which allows you to pass in a Java FileObject and then use the same API (same names any ways) that JMI provided. 
This would allow many of these tools to be reintroduced with just package name changes.

The class could use visitors and in memory data structures to create the appropriate information for access by these tools.
Comment 1 Jan Lahoda 2007-11-28 12:25:17 UTC
I would like to hear more about the source code analysis tools you speak about. What are they doing, how exactly?

There was an attempt to make a JMI compatible layer over the Trees/Elements infrastructure, but it was dropped for a
number of reasons:
-a limited (no events), read-only layer would provide support for a very limited scope of features built over JMI
(moreover such features are easiest to rewrite to the new model), while still consuming developmental resources that
could be use elsewhere.
-the actual compatibility of the JMI compatible layer with javacore is uncertain (would the trees look the same for the
same situation?)
-it is likely that the JMI compatible layer would mean performance hit
-there are no JMI-like events in the new API, and introducing them in the compatibility layer would likely mean
performance hit for the whole IDE

All in all, I would like to hear more about the tools you are speaking about. Thanks.
Comment 2 pribyl 2008-04-24 13:47:07 UTC
Closing this - not enough info.

Greg, if you are stil experiencing this problem, please reopen and add some additional info that could help.

Thanks a lot
P.
Comment 3 greggwon 2008-04-24 15:41:37 UTC
The practicle issue is that there is not an API which mirrors the patterns present in the JMI API.  There is not
existing code to ask questions like is this method defined, or is there a field named xxxx, etc.  Instead you have to
write up a bunch of code to do these things, and it will be different for each person doing this, which adds
tremendously to the codebase size of the running IDE since so much code is not shared.

Here's a class that I wrote to abstract and simplify some of my use of JMI.  It's this kind of API that I think needs to
be created.

/*
 * JMI.java
 *
 * Created on September 5, 2006, 10:28 PM
 */

package com.cytetech.netbeans.jmi;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.netbeans.api.java.source.JavaSource;
//import org.netbeans.jmi.javamodel.Constructor;
//import org.netbeans.jmi.javamodel.Field;
//import org.netbeans.jmi.javamodel.JavaClass;
//import org.netbeans.jmi.javamodel.JavaSource;
//import org.netbeans.jmi.javamodel.Method;
//import org.netbeans.jmi.javamodel.Parameter;
//import org.netbeans.jmi.javamodel.PrimitiveType;
//import org.netbeans.jmi.javamodel.Type;
//import org.netbeans.jmi.javamodel.UnresolvedClass;
//import org.netbeans.modules.javacore.api.JavaModel;
//import org.netbeans.modules.javacore.internalapi.JavaModelUtil;

/**
 * This class provides a number of static methods which attempt to shield JMI applications from
 * the minutia of the APIs.  The methods here are intended to cover the most common use cases for
 * making most things happen in a single method call for creating methods, constructors and fields.
 * Filling in the details with javadoc and method bodies or field initializations should just be one or two
 * additional method calls.
 * @see com.cytetech.netbeans.jmi.ParameterInfo
 * @author gregg <a href="mailto:gregg@wonderly.org">gregg@wonderly.org</a>
 */
public class JMI {

	/**
	 * The logging instance.
	 */
	protected static final Logger log = Logger.getLogger(JMI.class.getName());

	/* Disallow instance creation except through subclassing */
	/**
	 * Don't allow construction.
	 */
	protected JMI() {
	}

	/**
	 * Create a new method to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param name the name of the method
	 * @param mods the modifier mask {@link Modifier}.
	 * @param retType The type of the returned value
	 * @return a Method object with all the indicated parameterization.
	 */
	public static Method newMethod(JavaSource jmp, JavaClass cls, String name, int mods, Type retType) {
		return newMethod(jmp, cls, name, mods, retType, null, null);
	}

	/**
	 * Create a new method to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param name the name of the method
	 * @param mods the modifier mask {@link Modifier}.
	 * @param retType The type of the returned value
	 * @param excs An array of exception names that the method should indicate as thrown.
	 * @return a Method object with all the indicated parameterization.
	 */
	public static Method newMethod(JavaSource jmp, JavaClass cls, String name, int mods, Type retType, String[] excs) {
		return newMethod(jmp, cls, name, mods, retType, excs, null, null);
	}

	/**
	 * Create a new method to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param name the name of the method
	 * @param mods the modifier mask {@link Modifier}.
	 * @param retType The type of the returned value
	 * @param excs An array of exception names that the method should indicate as thrown.
	 * @param imports An array of class/interface names that this method will refer to.  These names will be
	 * resolved and appropriate imports added to the indicated Class definition.
	 * @return a Method object with all the indicated parameterization.
	 */
	public static Method newMethod(JavaSource jmp, JavaClass cls, String name, int mods, Type retType, String[] excs,
String[] imports) {
		return newMethod(jmp, cls, name, mods, retType, excs, imports, null);
	}

	/**
	 *
	 * Create a new method to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param name the name of the method
	 * @param mods the modifier mask {@link Modifier}.
	 * @return a Method object with all the indicated parameterization.
	 */
	public static Method newMethod(JavaSource jmp, JavaClass cls, String name, int mods) {
		return newMethod(jmp, cls, name, mods, (Type) null, null, null);
	}

	/**
	 *
	 * Create a new method to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param name the name of the method
	 * @param mods the modifier mask {@link Modifier}.
	 * @param retTypeName The type name of the returned value
	 * @return a Method object with all the indicated parameterization.
	 */
	public static Method newMethod(JavaSource jmp, JavaClass cls, String name, int mods, String retTypeName) {
		return newMethod(jmp, cls, name, mods, retTypeName, null, null);
	}

	/**
	 * Create a new method to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param name the name of the method
	 * @param mods the modifier mask {@link Modifier}.
	 * @param retTypeName The type name of the returned value
	 * @param excs An array of exception names that the method should indicate as thrown.
	 * @return a Method object with all the indicated parameterization.
	 */
	public static Method newMethod(JavaSource jmp, JavaClass cls, String name, int mods, String retTypeName, String[] excs) {
		return newMethod(jmp, cls, name, mods, retTypeName, excs, null, null);
	}

	/**
	 * Create a new method to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param name the name of the method
	 * @param mods the modifier mask {@link Modifier}.
	 * @param retTypeName The type name of the returned value
	 * @param excs An array of exception names that the method should indicate as thrown.
	 * @param imports An array of class/interface names that this method will refer to.  These names will be
	 * resolved and appropriate imports added to the indicated Class definition.
	 * @return a Method object with all the indicated parameterization.
	 */
	public static Method newMethod(JavaSource jmp, JavaClass cls, String name, int mods, String retTypeName, String[] excs,
String[] imports) {
		return newMethod(jmp, cls, name, mods, retTypeName, excs, imports, null);
	}

	/**
	 * Create a new method to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param name the name of the method
	 * @param mods the modifier mask {@link Modifier}.
	 * @param retTypeName The type name of the returned value
	 * @param excs An array of exception names that the method should indicate as thrown.
	 * @param imports An array of class/interface names that this method will refer to.  These names will be
	 * resolved and appropriate imports added to the indicated Class definition.
	 * @param parms An array of ParameterInfo objects indicating the parameters to the Method.
	 * @return a Method object with all the indicated parameterization.
	 */
	public static Method newMethod(JavaSource jmp, JavaClass cls, String name, int mods, String retTypeName, String[] excs,
String[] imports, ParameterInfo[] parms) {
		return newMethod(jmp, cls, name, mods, jmp.getType().resolve(retTypeName), excs, imports, parms);
	}

	public void userTask( JavaSource javeSource ) {
		try {
			javaSource.runUserActionTask(new Task<CompilationController>() {

                public void run(CompilationController compilationController) throws Exception {

                      compilationController.toPhase(Phase.ELEMENTS_RESOLVED);
new MemberVisitor(compilationController).scan(compilationController.getCompilationUnit(), null);
                      Document document = compilationController.getDocument();
                      if (document != null) {
                         StatusDisplayer.getDefault().setStatusText("Hurray, the Java file is open!");
                      } else {
                         StatusDisplayer.getDefault().setStatusText("The Java file is closed!");
                      }

                }
			}, true);
		} catch (IOException ex) {
			Exceptions.printStackTrace(ex);
		}
	}

	/**
	 * Create a new method to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param name the name of the method
	 * @param mods the modifier mask {@link Modifier}.
	 * @param retType The type of the returned value
	 * @param excs An array of exception names that the method should indicate as thrown.
	 * @param imports An array of class/interface names that this method will refer to.  These names will be
	 * resolved and appropriate imports added to the indicated Class definition.
	 * @param parms An array of ParameterInfo objects indicating the parameters to the Method.
	 * @return a Method object with all the indicated parameterization.
	 */
	public static Method newMethod(JavaSource jmp, JavaClass cls, String name, int mods, Type retType, String[] excs,
String[] imports, ParameterInfo[] parms) {
		Method method = jmp.getMethod().createMethod();
		method.setName(name);
		method.setModifiers(mods); //| Modifier.STATIC);
		if (retType != null) {
			method.setType(retType);
		}
		if (excs != null) {
			for (String ex : excs) {
				if (ex != null) {

					JavaClass excls = (JavaClass) JavaModel.getDefaultExtent().getType().resolve( ex );
					log.info("resolving exception for import: " + ex + ", cls: " + excls);
					if (excls != null && excls instanceof UnresolvedClass == false) {
						method.getExceptionNames().add(JavaModelUtil.resolveImportsForClass(cls, excls));
					}
				}
			}
		}
		if (imports != null) {
			for (String clnm : imports) {
				JavaModelUtil.resolveImportsForClass(cls, (JavaClass) JavaModel.getDefaultExtent().getType().resolve( clnm ));
			}
		}
		if (parms != null) {
			for (ParameterInfo pi : parms) {
				Parameter pr = pi.getParameter(jmp, cls);
				method.getParameters().add(pr);
			}
		}
		return method;
	}

	/**
	 *  Create a new constructor to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param mods the modifier mask {@link Modifier}.
	 * @return a Constructor object with all the indicated parameterization.
	 */
	public static Constructor newConstructor(JavaSource jmp, JavaClass cls, int mods) {
		return newConstructor(jmp, cls, mods, null, null);
	}

	/**
	 *  Create a new constructor to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param mods the modifier mask {@link Modifier}.
	 * @param excs An array of exception names that the constructor should indicate as thrown.
	 * @return a Constructor object with all the indicated parameterization.
	 */
	public static Constructor newConstructor(JavaSource jmp, JavaClass cls, int mods, String[] excs) {
		return newConstructor(jmp, cls, mods, excs, null, null);
	}

	/**
	 *  Create a new constructor to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param mods the modifier mask {@link Modifier}.
	 * @param excs An array of exception names that the constructor should indicate as thrown.
	 * @param imports An array of class/interface names that this method will refer to.  These names will be
	 * resolved and appropriate imports added to the indicated Class definition.
	 * @return a Constructor object with all the indicated parameterization.
	 */
	public static Constructor newConstructor(JavaSource jmp, JavaClass cls, int mods, String[] excs, String[] imports) {
		return newConstructor(jmp, cls, mods, excs, imports, null);
	}

	/**
	 *   Create a new constructor to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param mods the modifier mask {@link Modifier}.
	 * @param excs An array of exception names that the constructor should indicate as thrown.
	 * @param imports An array of class/interface names that this method will refer to.  These names will be
	 * resolved and appropriate imports added to the indicated Class definition.
	 * @param parms An array of ParameterInfo objects indicating the parameters to the Method.
	 * @return a Constructor object with all the indicated parameterization.
	 */
	public static Constructor newConstructor(JavaSource jmp, JavaClass cls, int mods, String[] excs, String[] imports,
ParameterInfo[] parms) {
		Constructor cons = jmp.getConstructor().createConstructor();
		cons.setModifiers(mods); //| Modifier.STATIC);
		if (excs != null) {
			for (String ex : excs) {
				if (ex != null) {
					cons.getExceptionNames().add(JavaModelUtil.resolveImportsForClass(cls, (JavaClass)
JavaModel.getDefaultExtent().getType().resolve( ex )));
				}
			}
		}

		if (imports != null) {
			for (String clnm : imports) {
				JavaModelUtil.resolveImportsForClass(cls, (JavaClass) JavaModel.getDefaultExtent().getType().resolve( clnm ));
			}
		}

		if (parms != null) {
			for (ParameterInfo pi : parms) {
				Parameter pr = pi.getParameter(jmp, cls);
				cons.getParameters().add(pr);
			}
		}
		return cons;
	}

	/**
	 * Check if the passed JavaClass object already has a definition of the passed Method.
	 * @param cls The class to look for the method definition in.
	 * @param method The method definition to look for, including parameters and return type information.
	 * @return true if the method is defined in the passed class, false if not.
	 */
	public static boolean classContainsMethod(JavaClass cls, Method method) {
		List<Type> v = new ArrayList<Type>();
		for (Object pi : method.getParameters()) {
			Parameter p = (Parameter) pi;
			v.add(p.getType());
		}
		return cls.getMethod(method.getName(), v, false) != null;
	}

	/**
	 *  Create a new Field to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param name the name of the field
	 * @param mods the modifier mask {@link Modifier}.
	 * @param fldType The type of the Field value
	 * @return a Field object with all the indicated parameterization.
	 */
	public static Field newField(JavaSource jmp, JavaClass cls, String name, int mods, Type fldType) {
		return newField(jmp, cls, name, mods, fldType, null);
	}

	/**
	 *  Create a new Field to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param name the name of the field
	 * @param mods the modifier mask {@link Modifier}.
	 * @param fldType The type of the Field value
	 * @param imports An array of class/interface names that this field will refer to.  These names will be
	 * resolved and appropriate imports added to the indicated Class definition.
	 * @return a Field object with all the indicated parameterization.
	 */
	public static Field newField(JavaSource jmp, JavaClass cls, String name, int mods, Type fldType, String[] imports) {
		Field field = jmp.getField().createField();
		field.setName(name);
		field.setModifiers(mods); //| Modifier.STATIC);
		if (fldType != null) {
			field.setType(fldType);
		}
		log.info("adding field: " + name + ", checking imports: " + imports);
		if (imports != null) {
			for (String clnm : imports) {
				log.info("Check and add import for: \"" + cls + "\"");
				JavaModelUtil.resolveImportsForClass(cls, (JavaClass) JavaModel.getDefaultExtent().getType().resolve( clnm ));
			}
		}
		return field;
	}

	/**
	 *  Create a new Field to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param name the name of the field
	 * @param mods the modifier mask {@link Modifier}.
	 * @param fldType The type of the Field value
	 * @return a Field object with all the indicated parameterization.
	 */
	public static Field newField(JavaSource jmp, JavaClass cls, String name, int mods, String fldType) {
		return newField(jmp, cls, name, mods, fldType, null);
	}

	/**
	 *  Create a new Field to be added to the passed class.
	 * @param jmp The JavaSource to use for references to types etc.
	 * @param cls The class this instance will belong to.
	 * @param name the name of the field
	 * @param mods the modifier mask {@link Modifier}.
	 * @param fldType The type of the Field value
	 * @param imports An array of class/interface names that this Field will refer to.  These names will be
	 * resolved and appropriate imports added to the indicated Class definition.
	 * @return a Field object with all the indicated parameterization.
	 */
	public static Field newField(JavaSource jmp, JavaClass cls, String name, int mods, String fldType, String[] imports) {
		return newField(jmp, cls, name, mods, jmp.getType().resolve(fldType), imports);
	}

	/**
	 * This method looks to see if the passed class already defines the indicated inner class.
	 * @param cls The class to look for the inner class in.
	 * @param nm the name of the inner class to look for
	 * @return true if the inner class is already defined, false if not.
	 */
	public static boolean classAlreadyHasInnerClass(JavaClass cls, String nm) {
		List l = cls.getFeatures();
		for (Object o : l) {
			log.info("o is a " + o.getClass().getName());
			if (o instanceof JavaClass) {
				JavaClass jc = (JavaClass) o;
				log.info("checking inner class: " + jc.getName() + " vs " + nm);
				if (jc.getName().equals(nm)) {
					//|| jc.getName().endsWith("."+nm) )
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * This method checks to see if the passed class already implements the passed interface.
	 * @param cls The class to look at.
	 * @param rnm the name of the interface to look for.
	 * @return true if the interface is already implemented, false if not.
	 */
	public static boolean hasInterface(JavaClass cls, String rnm) {
		List<JavaClass> intfs = (List<JavaClass>) cls.getInterfaces();
		boolean found = false;

		for (JavaClass iface : intfs) {
			String ifnm = iface.getName();
			log.fine("ifnm=" + ifnm + ", rnm=" + rnm + ", cls.getName()=" + cls.getName());
			if (ifnm.equals(rnm)) {
				found = true;
				break;
			}
		}

		return found;
	}

	/**
	 * This method checks to see if the passed method already declares the indicated exception as thrown.
	 * @param meth The method to look at.
	 * @param rnm the name of the exception to look for.
	 * @return true if the method already declares the passed exception as thrown, false if not.
	 */
	public static boolean methodThrowsException(Method meth, String rnm) {
		List<JavaClass> excs = (List<JavaClass>) meth.getExceptions();
		boolean found = false;

		for (JavaClass excl : excs) {
			String exnm = excl.getName();
			log.info("exnm=" + exnm + ", meth.getName()=" + meth.getName());
			if (exnm.equals(rnm)) {
				found = true;
			}
		}

		return found;
	}

	/**
	 * Changes the first character of the string to upper case.
	 * @param str The string to convert.
	 * @return The passed string with the first character as an upper case letter, if it is a letter.
	 */
	public static String fixCase(String str) {
		return Character.toUpperCase(str.charAt(0)) + str.substring(1);
	}
}
Comment 4 Jan Lahoda 2008-04-24 17:08:37 UTC
Sorry, but I am a bit confused - I think that the given class can be converted to use the new API (in some cases making
the code simpler). Is the proposal to make a support API similar to the API provided by this class? We can certainly
discuss that, but I think we should be careful in order not to make the API too big and impossible to understand.
Comment 5 greggwon 2008-04-24 17:21:36 UTC
The point is that the new vistor model is completely NOT like this kind of API, at least not last I looked at what was
available at the 6.0 release.  Anyone using the old JMI API has to completely rewrite their software.  For me, I don't
have the time to do this, and it keeps me from moving to 6.X because I need my existing modules to work.  I feel like
the platform should provide the kind of API shown.  I don't know if it makes sense to use something like this, or
perhaps change some things around to reduce derivative signatures and number of arguments.  

As a side note, this is not the current version that I am using.  It contains some initial hacking at using the vistor
model to replace the JMI code needed.  It's the signatures of the methods here that demonstrate the type of actions and
parameterations that the old JMI facilitated.
Comment 6 greggwon 2008-12-02 18:16:22 UTC
I don't see any more comments here on this issue.  Is there any chance that something like this can be done?  It's
really much more convenient to be able to ask questions in this style of API, for me at least.  All of the object
creation and layers of interrelated calls in the new model really make it harder than it needs to be.  APIs that
simplify particular usages of more powerful APIs should be available.
Comment 7 Rastislav Komara 2009-02-03 10:53:58 UTC
Overtake.