Git Product home page Git Product logo

jcodemodel's Introduction

jcodemodel

A fork of the com.sun.codemodel 2.7-SNAPSHOT. The classes in this project use a different package name com.helger.jcodemodel to avoid conflicts with other com.sun.codemodel instances that might be floating around in the classpath. That of course implies, that this artefact cannot directly be used with JAXB, since the configuration of this would be very tricky.

A site with the links to the API docs etc. is available.

Maven usage

Add the following to your pom.xml to use this artifact:

<dependency>
  <groupId>com.helger</groupId>
  <artifactId>jcodemodel</artifactId>
  <version>3.4.0</version>
</dependency>

News and noteworthy

  • v4.0.0 - work in progress
    • Added ph-commons as a dependency for common stuff
    • Moved all exceptions to exceptions package
  • v3.4.0 - 2020-05-25
    • Added special top-level construct JResourceDir to represent pure resource directories (issue #74 from @guiguilechat)
    • Added new class JCodeModelException as the base class for JClassAlreadyExistsException and the new class JResourceAlreadyExistsException
    • Existing APIs were changed to throw JCodeModelException instead of JClassAlreadyExistsException
    • JCNameUtilities.getFullName works with classes in the default package
    • Extended JCodeModel with (get|set)FileSystemConvention to make the creation more flexible (also issue #74 from @guiguilechat)
    • Added mutable overloads to methods that only return an unmodifiable collection (issue #86)
    • Fixed an issue with generating generics from anonymous classes (issue #84)
  • v3.3.0 - 2019-11-24
    • Added check for package names so that no invalid package names can be created (issue #70 from @guiguilechat)
    • Added check to avoid creating classes existing in the "java.lang" package (issue #71 from @guiguilechat)
    • JLambdaMethodRef now works with arbitrary expressions as the left hand side
  • v3.2.4 - 2019-07-15
    • Made class JavaUnicodeEscapeWriter publicly accessible
    • Extended enum constant ref API (issue #68 from @guiguilechat)
  • v3.2.3 - 2019-03-31
    • Extended JTryBlock API to have more control.
    • Added support for try-with-resources support (issue #67 from @gmcfall)
  • v3.2.2 - 2019-02-25
    • Using jsr305 instead of annotations in POM (issue #66 from @jjYBdx4IL)
  • v3.2.1 - 2019-01-23
    • Added var as reserved word
    • Made JReturn constructor public
    • Added JInvocation._this static method
    • Added IJExpression.castTo method
    • Added support to create final variables in for each loops
    • JExpr.dotClass now takes AbstractJType and not just AbstractJClass
    • Made constructors of subclasses of IJStatement public
    • No line breaks for annotations to parameters
    • Put each method parameter on a separate line if more than 3 parameters are present
  • v3.2.0 - 2018-10-20
    • Introduced class JCMWriter that should be used to emit the outgoing Java files. This replaces codemodel.build and offers a more consistent API. Most existing method remain existing and deprecated and just forward to JCMWriter.
      • Instead of cm.build (...) use new JCMWriter (cm).build (...)
    • Extracted IJFormatter interface for better separation of concerns. JFormatter was moved to a sub-package
    • ProgressCodeWriter no longer needs an explicit PrintStream but a ProgressCodeWriter.IProgressTracker instead.
    • Default charset for Java classes is now UTF-8.
    • Added new JAnnotationUse method overloads that automatically pass value as the annotation parameter name (issue #64)
  • v3.1.0 - 2018-08-22
    • Added AbstractJType._new()
    • Change return types of special JBlock methods to void to avoid chaining (issue #62) - incompatible change!
    • Added new JExpr.invokeThis and JExpr.invokeSuper static methods
  • v3.0.3 - 2018-06-12
    • Improved API access to inner classes (issue #60)
    • Changed order of emitted modifiers (final static -> static final)
    • Flush needed when writing resources fixed (issue #61 from @fbaro)
  • v3.0.2 - 2018-04-11
    • Fixed method resolution using direct class references (issue #58)
    • Added some additional JInvocation.arg... sanity methods
    • Enum constant argument list is now accessible
  • v3.0.1 - 2017-10-25
    • Added explicit support for invoking super - thx to @heruan for pointing this out
    • Added possibility to create a lambda reference from an invocation (issue #56 and PR #57 from @heruan)
  • v3.0.0 - 2017-08-06
    • Requires Java 8
    • Reworked #41 so that it is finally working in all cases
    • Add option for classes to not be imported (issue #51)
    • Fixed extra semicolon on Lambdas (issue #53)
  • v2.8.6 2016-07-19
  • v2.8.5 - 2016-05-13
    • improved comment handling (issue #47)
    • improved API checks (issue #45)
    • extended API (issue #46)
  • v2.8.4 - 2016-04-25
    • Enum values in switch statements are no longer fully qualified (issue #41)
    • fixed generation of narrowed classes without parameters (as in HashMap<>)
    • added support for strictfp keyword.
  • v2.8.3 - 2016-02-26
    • Added support for single line comments in blocks
    • improved generation of Lambdas
  • v2.8.2 - 2016-01-19
    • Customizable new line string and character set
    • extensions by @sviperll
  • v2.8.1 - 2015-12-03
    • Extensions by @sviperll
  • v2.8.0 - 2015-10-12
    • Requires Java 1.6
    • fixed potential double imports
    • added virtual blocks
    • integrated sviperll's metachicory
  • v2.7.11 - 2015-09-24
    • Bugfix release
    • removed half done CSE implementation (issue #18)
    • improved handling of directClasses
    • added enumConstantReference
  • v2.7.10 - 2015-06-30
    • Synchronized block added
    • initial support for lambda expressions
  • v2.7.9 - 2015-03-19
    • Minor extensions for error types
  • v2.7.8 - 2015-02-05
    • Enum constants for annotation parameters
  • v2.7.7 - 2014-09-17
    • mainly API extensions
  • v2.7.6 - 2014-09-02
    • Extended annotation parameter handling API
  • v2.7.5 - 2014-08-14
    • Support for multiple boundaries added (like T extends AnyClass & Serializable)
  • v2.7.4 - 2014-06-12
    • Bugfix release
  • v2.7.3 - 2014-05-23
    • Bugfix release
  • v2.7.2 - 2014-05-21
    • now on Maven Central
  • v2.7.1 - 2014-05-19
    • now as OSGi bundle
  • v2.7.0 - 2014-05-16
    • API extensions
  • v2.6.4 - 2014-04-10
  • 2013-09-23: Changes from https://github.com/UnquietCode/JCodeModel have been incorporated.

Contribution

Pull requests must follow my personal Coding Styleguide

Tabs vs spaces

This project uses double-space for indentation. If you want to use tabs, you can ask git to modify the files when commiting them and when pulling them. For this, from your project directory :

  • edit the file .git/info/attributes to make it contain *.java filter=tabspace . This will tell git to apply the script tabspace on the *.java files
  • run git config filter.tabspace.clean 'expand --tabs=2 --initial' to ask git to replace tabs with two spaces on commit of *.java files.
  • run git config filter.tabspace.smudge 'unexpand --tabs=2 --first-only' to request git to replace double spaces with two tabs on checking a *.java file out.

Eclipse

For eclipse, a formatter xml and a cleanup xml are present in the meta/formatter/eclipse/ directory. You can load them from the "project properties > java code style" settings. Check "Enable project specific settings", then load them.

NOTE : you also need to change the save actions to make them meet the clean up actions. Save actions are done even when they are not present in the clean up.


On Twitter: @philiphelger | Kindly supported by YourKit Java Profiler

jcodemodel's People

Contributors

fbaro avatar glelouet avatar leventov avatar phax avatar sviperll avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jcodemodel's Issues

synchronized block generation

I've found no way to generate synchronized block like this:

void method1() {
    synchronized(lock) {
         // some synchronized data access here
    }
}

Add option for classes to never be imported

In some cases, the convention for a certain class is to never be imported, but always use its fully qualified class name when referencing it or any of its members. In the Android world, such a class is the android.R class, which contains resource identifiers for the system, and it should not be confused with the project R class which gets imported. For example:

package example.my.project;

public class MyActivity extends Activity {

  void someMethod() {
    String system = getString(android.R.string.some_string);
  }
}

It would be nice to add an option to JCodeModel to support this.

Extra semicolon when returning lambda

I don't know if this is a bug or I'm doing something wrong, but I can't find examples for lambdas so this is it:

JMethod method = definedClass.method(...);
JLambdaMethodRef methodLambda = new JLambdaMethodRef(...);
JLambda lambda = new JLambda();
lambda.body().lambdaExpr(
    JExpr._this().invoke("getValueProvider").invoke("andThen").arg(methodLambda)
);
method.body()._return(lambda);

This produces:

public default StringFilter<T> description() {
    return () -> this.getValueProvider().andThen(Category::getDescription);
    ; // <- Unreachable code error!
}

with the extra ; resulting in an "Unreachable code" error in Eclipse. How can I avoid the extra semicolon?

Retrieve inner class reference

I was looking for a way to get a reference to an inner class from another class, but I was not able to find it. I was looking for something like codeModel.ref("x.y.z.AClass").getInner("Inner"), which would give a reference to x.y.z.AClass$Inner.
Would it be possible to add it? Or it's already there and I did not find it?

Add method to AbstractJAnnotationValue to access value

Annotation parameters are compile-time values. It would be convinient to access them all the way through code generation. AnnotationUse class should be extended to provide access to parameter values

Add

Object getParameterValue(String parameterName);

method or add collection of methods corresponding to AnnotationUse.param methods:

int getParameterIntValue(String parameterName);
long getParameterLongValue(String parameterName);
...
int[] getParameterIntArrayValue(String parameterName);
long[] getParameterLongArrayValue(String parameterName);
...
String getParameterStringValue(String parameterName);
Annotation getParameterAnnotationValue(String parameterName);
Class getParameterClassValue(String parameterName); Enum getParameterEnumValue(String parameterName);
...
String[] getParameterStringArrayValue(String parameterName);
...

Code analysis, Common subexpression elimination

Does the CodeModel project or particularly this fork contain any functionality like code analysis and performing simple optimizations, for exampe common subexpression elimination? If not, how do you think how difficult it could be to implement this and how friendly for such analysis/transformations the current API/library design/impl is?

Clash when importing inner class with the same name as package suffix

Importing inner classes can introduce not expected compilation problems

For example in AndroidAnnotations, the user can have a following annotated class.

package id.mypapp;
public class R {
  public static class id {
     public static final int myItem = 1;
  }
   public static class menu {
     public static final int menu = 2;
  }
}
import id.myapp.R;
// other imports

@OptionsMenu(R.menu.menu)
public class HelloAndroidActivity extends Activity {
    @OptionsItem(R.id.myItem)
    void myItemClicked() {

    }
}

This is generated:

import id.myapp.R.id;
// other imports

public final class HelloAndroidActivity_ extends HelloAndroidActivity {

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // compilation error:
        // id is a package name part here, but compiler thinks its
        // inner class id.myapp.R.id
        menuInflater.inflate(id.myapp.R.menu.mymenu, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (itemId_ == id.mymenuitem) { // id.myapp.R.id
            //
        }
        return super.onOptionsItemSelected(item);
    }
}

So the main problem is that jcodemodel imports id.myapp.R.id inner class, however when it references id.myapp.R.menu.mymenu field , the compiler will think the first part of it is not a package part, but the inner class id.myapp.R.id.

The most easier solution would be to never import inner classes or maybe add an option to not to do so. (BTW, in Android, it is conventional to only import the id.myapp.R class, and not its inner classes.)

[proposal] check packages names

Issue

The JDefinedClass constructor checks the validity of the class name, starting here :

https://github.com/phax/jcodemodel/blob/master/src/main/java/com/helger/jcodemodel/JDefinedClass.java#L214

The JPackage constructor, does not, here :

https://github.com/phax/jcodemodel/blob/master/src/main/java/com/helger/jcodemodel/JPackage.java#L116

Proposal

I propose to make the JPackage make a verification test

https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html

according to this :

  • sName must not be a reserved keyword
  • sName must not contain non-alphanumerical value besides the underscore
  • sName must not start with a number.
  • (optional) sName must not contain an upper-case character.

Implementation

Here is how I personally do :

Reserved keyword

I keep a list from https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html

/** java keywords we can't use as a name for a package or a class */
public static final Set<String> JAVA_RESERVED_KEYWORDS = Collections.unmodifiableSet(new HashSet<>(
		Arrays.asList("abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const",
				"continue", "default", "do", "double", "else", "extends", "false", "final", "finally", "float", "for", "goto",
				"if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "null", "package",
				"private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized",
				"this", "throw", "throws", "transient", "true", "try", "void", "volatile", "while")));

Then I just check if the name is contained in that set.

All alphanumerical, start with non-numerical

public static final Pattern VALID_PACKAGE_NAME = Pattern.compile("[A-Za-z_][A-Za-z0-9_]*");

Then I just check if sName matches this pattern. It can accept "_" as a package name, not sure if correct.

Optional check lowercase

Add a boolean option in JCodeModel::ensurePackagesLowerCase(). default value should be false.
Then create a new Pattern to match the package name

public static final Pattern VALID_LOWERCASE_PACKAGE_NAME = Pattern.compile("[a-z_][a-z0-9_]*");

To test the name against.

create resource directory

Since the check on packages name ( #70 ) , it's not possible anymore to create a resource in a resource dir that does not respect a package name.
Example, I can't create a new META-INF/data.txt file, because the META-INF does not respect the package name.

This was explained till #70 (comment) ; I create a separate issue for convenience.

The solution I think would be correct is

  1. create the JResourceDirectory class (same level as JPackage)
  2. move the method addResourceFile from JPackage to JResourceDirectory.
  3. add sub-dir creation in this class, and the root JResourceDirectory in JCodemodel - basically like the package. Also the JCodemodel should have a Map of JResourceDirectory, by name.
  4. add JPackage to JResourceDir translation, typically with public JResourceDir JPackage::asResourceDir(){return m_aOwner.getResourceDir(m_sName);}
  5. add again addResourceFile in JPackage , but calling corresponding resource dir
    public AbstractJResourceFile JPackage::addResourceFile (@Nonnull final AbstractJResourceFile rsrc){return asResourceDir().addResourceFile(rsrc);} ; and same for the other methods.

Possibly you can make JPackage extend JResourceDir. Still, overload the methods to call the resource dir when creating resources.

Add API to reference enum constants of a JDirectClass

Sometimes in code generation, we have to reference an enum constant of a class which is not generated, just referenced. Currently there is no way to do this, only for defined (generated classes). Currently we have to use an ugly workaround:

AbstractJClass referencedClass = ...
IJExpression expression = JExpr.direct(referencedClass.fullName() + "." + "ONE_CONSTANT");

Referencing inner classes

In current version of jcodemodel all inner classes are explicitly imported. This is incorrect:

package pkg;

// This is incorrect: you can't import private inner classes!!!
import pkg.SomeClass.SomeInnerClass;

class SomeClass {
    SomeInnerClass field1;

    private static class SomeInnerClass {
    };
}

You can always use
SomeClass.SomeInnerClass
to reference to inner-class instead of bare
SomeInnerClass

The example above will become:

package pkg;

class SomeClass {
    SomeClass.SomeInnerClass field1;

    private static class SomeInnerClass {
    };
}

add direct enum ref in JCodemodel

sorry if this is already present, did not find it.

I already have this code in a common class :

class A {
A(MyEnum param){}
}

I want to use JCodeModel to create the class B:

class B extends A{
B(){
super(MyEnum.enum1);
}
}

I create the definedclass bclass, create the constructor, but when I want to set super(enum1) I need to call JExpr.enumConstantRef(cm.ref(MyEnum.class), enum1.name())

I think it would be easier to add an enumRef in JCodeModel that allow to refer to an enum constant more easily.
eg

/** 
* refer to clazz.ref
*/
	public <T extends Enum<T>> JEnumConstantRef enumRef(Class<T> clazz, T ref) {
		return JExpr.enumConstantRef(ref(clazz), ref.name());
	}

(did not test this code, maybe the idea is bad - it compiles but :P )

expose JFieldVar init expression

right now JFieldVar.m_aInitExpr has private scope, as result it's not possible to read init statement.
Please expose getter for that init statement.

[feature] preprocessors

I am thinking of a new feature, the preprocessor that can add data in the JCM before it is built.

Use cases

use cases I can think of :

  • add @Override on the created classes that declare a method that overrides a parent's.
  • add @author on classes.
  • for a set of the classes generated, automatically add getter for the fields, and setters for non-final fields
  • for the classes that don't have it and declare fields, override the hashcode() and/or equals(Object) methods.
  • add @Immutable on classes that inherit from an @Immutable class or a java class that does not have mutable field ; and all fields are final with type immutable.
  • (not sure) implement methods as calls to specific static class methods in abstract methods that implement interface.
    eg if class CClass extends abstract class AAbstract, and implements IInterface{int one();}, with a class SInterfaceHelper{int one(IInterface _this){return 1;}} ;
    Then the processor would add the method @Override int one(){return SInterfaceHelper.one(this);} in CClass .

Code usage

use code I can see :

  • The user loads the OverrideProcessor with jcm.processor(OverrideProcessor.class) . On the preprocess phase (in the build() ) the processor does its job. Same for the ImmutableProcessor.
  • The user loads the AuthorProcessor and sets the author with jcm.processor(AuthorProcessor.class).authors("me", "another") . This processor will adds the corresponding author annotations to the classes that don't have them already.
  • The user loads the BeanProcessor with beanproc = jcm.processor(BeanProcessor.class). He then declares classes as bean with beanproc.add(myJCMClass); . This processor will add the missing methods.
  • The user loads the EqualityProcessor and asks to add equals and hashcode with jcm.processor(EqualityProcessor.class).addEquals().addHashCode();. If a class must not have the methods added, eg because one field can be set to a recursive self reference (like a node in a tree, its children can ref their parent), that class , or its package, can be ignore with eg EqualityProcessor::ignore(JDefinedClass) ; or the processor can be limited to a set of classed and package, with eg EqualityProcessor::limit(JPackage)

Issues

Multiple Processors

If several processors are added, the order in which they are applied can modify the final result. If a processor adds a field, and the BeanProcessor adds methods depending on the fields, the method will only be added IFF the beanprocessor is applied after the fieldprocessor.

To resolve this, as long as at least one processor did modify the JCM during the application, each processor must be applied again. This process will be repeated until no processor has an impact on the JCM.

This however requires that the processors make optional changes, eg the addition of @author is only done if not already present - but also, the addition of setter and getter would not be done, and would not thrown an exception, if the setter/getter is already here. This specific getter/setter issue can be avoided, by passing a boolean parameter as true for the initial pass, and therefore throwing an exception only if a method already exists and during the initial pass.

Processors cleanup

Another issue is, that after a build the JCM is modified. That means that a JCM is dirty after the build, and can't be used anymore. Typically adding the getter once will be fine, but building the JCM again would make the BeanProcessor throw an exception.

Ideally we would want the JCM to be able to revert the additions made by the processors.That means, the processors would store the modification they made, and would enable to undo them (in the reverse order they were made). However this is not easy task, and can lead to issues. We could also lock the JCM and make all the classes delegate modifications to the JCM, when it is locked, to a system that would store them. This would however need a lot of tests, and again prone to errors.

Another possibility is to make the processors work on a copy of the JCM. However since the variables (eg the Jclasses that need to be beanified) stored in the processors refer to the real JCM, this would require a lot of work, and potentially to lock the classes again.

Two solutions seems however good for me. The first one, is to accept that a JCM can only be built once. The second one, is to store the JCM and restore it after the build(). Of course, the first one is the easiest.

Generation errors

What happens if a processor is asked to create a JElement that already exists ? Should it fail ? I think it depends on the processor, and the pass it is being used on. IMO a getter that can't be created because a fucntion already exists with that name, should make the full build fail.

chained invoke is not applied

here is my code. I am using jcodemodel 3.0.4


JLambda lambdaSet = new JLambda();
JLambdaParam arr = lambdaSet.addParam("arr");
JBlock setBody = lambdaSet.body().synchronizedBlock(holder).body();
(..)
setBody.invoke(holder, "entrySet").invoke("retainAll").arg(newmap.invoke("entrySet"));

source

this is what is produced (inside the synchronized)

holder.entrySet();
code example is here

I expected

holder.entrySet().retainAll(newmap.entrySet());

IT seems that chaining invoke() results in only the first one being added to the block.

Issue with JFormatter class

As of now i am using 2.8.3 API. But i am always getting an issue NullPointerIssue in the above mentioned class. When i am trying to open the class with decompiler i am getting issue as Internal Error as could you please look into the issue. As my final class is generated but there is no code inside my generated class.

Thanks
Gaurav

https://github.com/eclipse-ee4j/jaxb-ri

The original codemodel project is maintained now at https://github.com/eclipse-ee4j/jaxb-ri
Not sure where the differences are between

What was the base of your fork?

equals/hashCode for IJExpressions

Seems that implementing equals() and hashCode() methods for IJExpression subclasses is an absolute requirement for starting any analysis (#18). What way of implementing this do you prefer for the lib? Some particular auto generation utility, such as from Apache Commons, or Lombok, or Google's auto-value? Generating from Eclipse/Intellij? Or writing by hand?

(Of cause I would be very grateful if you implement this, but if don't want to, I will be forced to do it myself, and will try to accout your advice.)

in-memory and jar/zip platform

follow #74

Should create platform file specifications in EFileSpecification for JAR/ZIP and in-memory file systems. Those platform specs would allow to create files that match the restriction of their respective file system.

JAR is used eg to distribute the source files of a project. Can also be used to export the .class files though this is not really the use of JCodeModel. The specification is here

Inmemory is used to directly compile the .java files into the .class files, which can then be placed in another file system, or fed to a ClassLoader . I don't know the limitations of such a system, should work on them.

priority : very low.

Invocation with type-arguments.

We need to explicitly state type-arguments sometimes when invoking methods, like in

List.<Integer>nil(); // Static-invocation

I've found no way to generate this with codemodel.

add no-param "value" for annotation.

Hello.

When I annotate eg a field with an annotation.
If this annotation accept one parameter which has name "value".
I can use this annotation with @annotation(myvalue)

In JCM I need to specify a name in the annotation. However, if I specify "value" it generates the correct expected result.
Could we have another function /** add a value to the default parameter of the annotation*/JAnnotationUse#param(String value){return param("value", value);}

I guess the issue is, that there are already many different methods for param(), eg with String, double, etc.

Allow direct import

Sometimes is useful to be able to write code directly from a user specified String, and JDirectClass.directClass(String) is a great example of that. But in this case we must provide a fully qualified name for that type, and this is something we maybe don't know at that time, but it could compile because of user defined import definitions. So, I think a method like JCodeModel._import(String definition, boolean isStatic) would be very useful. Some examples:

myClass._import("java.util.*", false);
myClass._import("java.lang.Math.PI", true);

support for scala

it will be nice if this library can be used to generate scala code...

Support for header comments

Please add support for header comments like this one from the google protobuf compiler:

// Generated by the protocol buffer compiler.  DO NOT EDIT!
// source: htmlmessages.proto
package some.package;

public class SomeGeneratedClassWithNoContent {
    // no content
}

[proposal] check class name against keywords and java.lang

Keywords

see #70 for the keywords

java.lang

Issue

This is an issue because this package is already imported and as such not referenced to.

Typically if I create a my.package.Object class and then I create a method

@Override
boolean equals(java.lang.Object other) 

The object other actually refers to the class I am crating, and not the java.lang.Object class.
This issue literally happened to me when compiling against a openapi web service that produces "Object" responses.

Detection

This issue is very complicated : since the java reflection works bottom-up, that is the classes know their package but the package doesn't know its classes, it's difficult to find if the class exists without a java.lang.class.forName("java.lang."+sName), with trycatch around. This expensive call requires to cache the result in a hasmap<String, Boolean>

here is my code

private static final HashMap<String, Boolean> resolvedJavaLangNames = new HashMap<>();

     /**
 * check if a name already represents a class in the java.lang package. eg
 * String should return true, but LeetH4X0R should not.
 * 
 * @param className
 *          name to test
 * @return the existence of such a class in the java.lang (cached)
 */
public static boolean isJavaLangClass(String className) {
	if (resolvedJavaLangNames.containsKey(className)) {
		return resolvedJavaLangNames.get(className);
	}
	boolean isJavaLang = true;
	try {
		Class.forName("java.lang." + className);
	} catch (Exception e) {
		isJavaLang = false;
	}
	resolvedJavaLangNames.put(className, isJavaLang);
	return isJavaLang;
}

Looking at how fast it is to use JCodeModel, I consider the use of synchronization to be worthless (I usually don't and thus would have synced over the hashmap in the method)

It should check for specific exception (security exception means I can't tell)

Reaction

In the case the class C is in java.lang already, two possibilities :

  • internally mark the class C as javaLangMisleading, so that all call to a java.lang.C are specifically written as such
  • throw an exception (bad - unless in the option)

IMO both should be available. mark the class C as misleading , AND allow users to avoid this with a compilation option.

Especially, I'm not sure how to make the misleading part, since the writing of the class names may be done without the knowledge of where that name is used.

Unnecessary outer block created around two simple blocks

The following code:

JCodeModel cm = new JCodeModel();

JPackage aPkg1 = cm._package("id.myapp.activity");

JDefinedClass testClass = aPkg1._class("TestClass");

JMethod init_ = testClass.method(JMod.PUBLIC, cm.VOID, "init_");
JBlock first = init_.body().blockSimple();
JBlock second = init_.body().blockSimple();

first.decl(cm.INT, "var");
second.decl(cm.INT, "va2");

cm.build (new SingleStreamCodeWriter(System.out));

Generates the following:

public class TestClass {

    public void init_() {
        {
            int var;
        }
        {
            int va2;
        }
    }
}

I do not really understand why this happening... ๐Ÿ˜•

Add Travis CI and snapshot deployment

It would be nice if this project have continuous Integration, which would also deploy a release to Sonatype OSS snapshot repo after every push to master.

Unable to build jcodemodel-2.7.6-SNAPSHOT

I've got following error:

[DEBUG] Configuring mojo 'com.helger.maven:ph-buildinfo-maven-plugin:1.2.0:generate-buildinfo' -->
[DEBUG]   (s) formatProperties = false
[DEBUG]   (s) formatXML = true
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Failed to configure plugin parameters for: com.helger.maven:ph-buildinfo-maven-plugin:1.2.0



Cause: Class 'java.util.Set' cannot be instantiated
[INFO] ------------------------------------------------------------------------
[DEBUG] Trace
org.apache.maven.lifecycle.LifecycleExecutionException: Error configuring: com.helger.maven:ph-buildinfo-maven-plugin. Reason: Unable to parse the created DOM for plugin configuration
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:723)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalWithLifecycle(DefaultLifecycleExecutor.java:556)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:535)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:387)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:348)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:180)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:328)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:138)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:362)
    at org.apache.maven.cli.compat.CompatibleMain.main(CompatibleMain.java:60)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315)
    at org.codehaus.classworlds.Launcher.launch(Launcher.java:255)
    at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430)
    at org.codehaus.classworlds.Launcher.main(Launcher.java:375)
Caused by: org.apache.maven.plugin.PluginConfigurationException: Error configuring: com.helger.maven:ph-buildinfo-maven-plugin. Reason: Unable to parse the created DOM for plugin configuration
    at org.apache.maven.plugin.DefaultPluginManager.populatePluginFields(DefaultPluginManager.java:1363)
    at org.apache.maven.plugin.DefaultPluginManager.getConfiguredMojo(DefaultPluginManager.java:724)
    at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:468)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:694)
    ... 17 more
Caused by: org.codehaus.plexus.component.configurator.ComponentConfigurationException: Class 'java.util.Set' cannot be instantiated
    at org.codehaus.plexus.component.configurator.converters.AbstractConfigurationConverter.instantiateObject(AbstractConfigurationConverter.java:121)
    at org.codehaus.plexus.component.configurator.converters.composite.CollectionConverter.fromConfiguration(CollectionConverter.java:73)
    at org.codehaus.plexus.component.configurator.converters.ComponentValueSetter.configure(ComponentValueSetter.java:207)
    at org.codehaus.plexus.component.configurator.converters.composite.ObjectWithFieldsConverter.processConfiguration(ObjectWithFieldsConverter.java:137)
    at org.codehaus.plexus.component.configurator.BasicComponentConfigurator.configureComponent(BasicComponentConfigurator.java:56)
    at org.apache.maven.plugin.DefaultPluginManager.populatePluginFields(DefaultPluginManager.java:1357)
    ... 20 more
Caused by: java.lang.InstantiationException: java.util.Set
    at java.lang.Class.newInstance(Class.java:359)
    at org.codehaus.plexus.component.configurator.converters.AbstractConfigurationConverter.instantiateObject(AbstractConfigurationConverter.java:111)
    ... 25 more

Ternary operator

There is no ternary operator in codemodel, like

return a != null ? a : b;

If-statements are too heavyweight sometimes.

JMod for default method

Java8 lets one declare default methods on interfaces but codemodel doesn't have a way to specify this construct.

Enum values in switch()

When generating a switch on an enum, the case labels should not include the enum class name, but only the enum label.

    public enum E { A, B }
    ...
    switch (e) {
        case A: // Not case E.A
        ...
    }

It's an old issue with the original codemodel.
Unfortunately, I have no idea how to fix this with the generate() method architecture. I solved it on my local codemodel copy with an ugly instanceof in JCase.

add @deprecated explanations

Hello.

Last time you deprecated some chained method in jbblock, also the JCodemodel.write() . First one had to call the JBlock.add(Jexpr.X) and wirte is now I think in a dedicated class. All is good.
However I had a hard time getting what I exactly had to do to correct my code.

Could you, whenever a method is deprecated, refer to the issue and the correction in the javadoc ?
as recommanded in https://docs.oracle.com/javase/1.5.0/docs/guide/javadoc/deprecation/deprecation.html#how

You are strongly recommended to use the Javadoc @deprecated tag with appropriate comments explaining how to use the new API. This ensures developers will have a workable migration path from the old API to the new API. For more information, see Using the @deprecated Javadoc Tag.

Javadoc on the web

It's convenient to have javadoc on the web. You can use github pages (gh-pages branch) to publish javadoc with minimum efforts.

Add convenience method JBlock#block(boolean, boolean)

In #26, the behavior of JBlock#block() method changed in a breaking-way: now a new block is added with braces and indent. The previous behavior was adding a block with no indent and no braces.

In AndroidAnnotations, we are using the old behavior extensively: we create no-indent no-braces blocks as placeholders, so we can easily insert code in the desired order later. To achieve this now, we have to create the blocks in the following way:

JBlock block = body.block().bracesRequired(false).indentRequired(false);

It would be nice if the API would provide a much shorter convenience method, for example:

JBlock block = body.block(false, false);

Or maybe this, too:

JBlock block = body.blockWithoutBraces(); // with a better method name :)

Add support for java 8 annotation targets TYPE_USE and TYPE_PARAMETER

Java 8 adds two new targets for annotations.

  • TYPE_USE - here are some examples
class MyData implements MyTypedInterface<@Limit(2) MyType> {}

new @Immutable Rock()

(@NonNull String) calculateValue()

void doSomething() throws @Critical IOException();

For all possible 16 type uses refer to https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.11

  • TYPE_PARAMETER - is for declaring type parameters like
class MyClass<@NonNull T> {}

<@Immutable T> void process(T message);

JDefinedClass.getMethod does not work when a parameter is JDirectClass

The JDefinedClass.getMethod() method works by searching a method with matching name and parameter types. As JDirectClass does not implement the equals method, if getMethod() is provided with a JDirectClass freshly obtained from JCodeModel.ref(), it will not return the required method.

JPackage.parent() cannot walk to root package

When producing a code model using JCodeModel.ref(), the following operation doesn't work as expected:

// Create JCodeModel

JCodeModel wModel = new JCodeModel();

// Reflect into class

AbstractJClass wClass = wModel.ref(com.redfish.prod.Boomer.class);

// Walk up to the root package

JPackage wCurrentPackage = null;

for (wCurrentPackage = wClass._package(); wCurrentPackage.parent() != null; wCurrentPackage = wCurrentPackage.parent());

JPackage wRoot = wCurrentPackage;

The problem occurs in JPackage.parent() on the package just above the root package. The name is just "com", and so the existing code:

/** - Gets the parent package, or null if this class is the root package. */

@Nullable

public JPackage parent ()

{

if (m_sName.length () == 0)
return null;
final int idx = m_sName.lastIndexOf ('.');

return m_aOwner._package (m_sName.substring (0, idx));

}

The result in the lowest level non-root package is a StringIndexOutOfBoundsException as "idx" is set to -1.

This seems to be correct usage based on what I seei in the method's documentation, but clearly it can't handle it.

Double imports and bad generics when using anonymous inner classes

I have a code generation need where I have an abstract outer class instance with generics that needs to add an instance of the inner class within the anonymous class definition

in codemodel i defined what I was doing with:

AbstractJClass jtype = generator.ref(String.class);
AbstractJClass aspect = generator.ref(ValueHolderInstanceImpl.class);
AbstractJClass abstractFieldClass = generator.ref(AbstractFieldInstanceImpl.class).narrow(jtype);
JDefinedClass basefield = generator.anonymousClass(abstractFieldClass);
JFieldVar apectfield = basefield.field(JMod.PRIVATE, aspect, "_valueHolder_");
JMethod initfield = basefield.method(JMod.PROTECTED, generator.VOID, "_initialize_");
initfield.body().assign(apectfield,JExpr._new(aspect).arg(jtype.dotclass()).arg(JExpr._null()));

The output is:

package impl;

import impl.AbstractFieldInstanceImpl;
import impl.AbstractFieldInstanceImpl;
import impl.ClassInstanceImpl;

public class TestClass1Impl
    extends ClassInstanceImpl
{
    private AbstractFieldInstanceImpl<String>_testField_ = new AbstractFieldInstanceImpl<String>("testField", 0, null) {
        private AbstractFieldInstanceImpl.ValueHolderInstanceImpl _valueHolder_;
        protected void _initialize_() {
            _valueHolder_ = new AbstractFieldInstanceImpl.ValueHolderInstanceImpl(String.class, null);
        }
            //.... more stuff ....
    }
    ;
}

so there are two general issues here:

  1. The generics on of AbstractFieldInstanceImpl.ValueHolderInstanceImpl ie
    AbstractFieldInstanceImpl<String>.ValueHolderInstanceImpl
    are getting dropped in the definition above. While I this probably has no code impact, it looks really bad and I would rather not add a @SuppressWarnings({ "rawtypes" }) annotation to remove the warning on the field and another @SuppressWarnings({ "unchecked", "rawtypes" }) on the initialization method. Ideally it should generate too:
    private AbstractFieldInstanceImpl<String>_testField_ = new AbstractFieldInstanceImpl<String>("testField", 0, null) {
        private ValueHolderInstanceImpl _valueHolder_;
        protected void _initialize_() {
            _valueHolder_ = new ValueHolderInstanceImpl(String.class, null);
        }
            //.... more stuff ....
    }
    ;
  1. it is adding two imports of the AbstractFieldInstanceImpl
    I assume that if I generated another generic subtype it would add another import per subtype.

While all of these are inconsequential and I could fix it all with a second pass generator really quickly it seems that you are actively working on this project and may wish to fix this.

Thanks

Construct JLambdaMethodRef with JInvocation

I need to generate this field signature:

public final StringFilter<E> firstName = this.andThen(Person::getFirstName)::apply;

I can easily generate the this.andThen(Person::getFirstName) part with:

JLambdaMethodRef lambdaMethod = new JLambdaMethodRef(personType, getterName);
JInvocation jInvocation = JExpr._this().invoke("andThen").arg(lambdaMethod);

but I can't generate a lambda method reference from this JInvocation, since I can't construct a new JLambdaMethodRef(jInvocation, "apply") nor I can generate a lambda reference with any JInvocation method, e.g. jInvocation.ref("apply") generates .apply instead of ::apply.

Backward compatibility 2.8 โ†” 3.0

Hi,

I'm considering switching from com.sun.codemodel to your implementation since I need a couple of features not present in the original sources.

You are currently doing a refactoring from 2.8 โ†’ 3.0 therefore I'm hesitant to switch just yet. Can you inform me about the planned API stability between 2.8 and 3.0 e.g. what will a user of your library have to change when using 2.8 and switching to 3.0?

-- ooxi

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.