Skip to content

Commit

Permalink
Add ScopeFactory API
Browse files Browse the repository at this point in the history
  • Loading branch information
Leland Takamine committed Oct 4, 2019
1 parent d27718c commit aa8933d
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 0 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,13 @@ interface MainScope extends Creatable<MainDependencies> {
interface MainDependencies {}
```

Extending `Creatable<D>` also enables instantiation of a "root" `Scope` without referencing generated code using Motif's `ScopeFactory.create` API:

```java
MainDependencies dependencies = ...;
MainScope mainScope = ScopeFactory.create(MainScope.class, dependencies)
```

## Convenience APIs

Factory methods that pass parameters through to a constructor without modification can be converted to parameterless abstract methods:
Expand Down
66 changes: 66 additions & 0 deletions lib/src/main/java/motif/ScopeFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package motif;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class ScopeFactory {

private ScopeFactory() {}

private static final Map<Class<?>, Constructor<?>> scopeClassToCreateMethod = new HashMap<>();

public static <S extends Creatable<D>, D> S create(Class<S> scopeClass, D dependencies) {
Constructor<?> constructor = getConstructor(scopeClass);
try {
return (S) constructor.newInstance(dependencies);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
}

private synchronized static Constructor<?> getConstructor(Class<?> scopeClass) {
Constructor<?> constructor = scopeClassToCreateMethod.get(scopeClass);
if (constructor == null) {
Class<?> scopeImplClass = getScopeImplClass(scopeClass);
constructor = scopeImplClass.getDeclaredConstructors()[0];
scopeClassToCreateMethod.put(scopeClass, constructor);
}
return constructor;
}

private static Class<?> getScopeImplClass(Class<?> scopeClass) {
String scopeImplClassName = getScopeImplClassName(scopeClass);
try {
return Class.forName(scopeImplClassName, false, scopeClass.getClassLoader());
} catch (ClassNotFoundException e) {
throw new RuntimeException("Could not find Scope implementation class " + scopeImplClassName + ". Ensure " +
"that the Motif annotation processor is enabled.");
}
}

// Inspired by https://github.com/square/javapoet/issues/295
private static String getScopeImplClassName(Class<?> scopeClass) {
StringBuilder sb = new StringBuilder();
sb.append("Impl");
Class<?> clazz = scopeClass;
while (true) {
sb.insert(0, clazz.getSimpleName());
Class<?> enclosing = clazz.getEnclosingClass();
if (enclosing == null) break;
clazz = enclosing;
}
int lastDot = clazz.getName().lastIndexOf('.');
if (lastDot != -1) {
sb.insert(0, '.');
sb.insert(0, clazz.getName().substring(0, lastDot));
}
return sb.toString();
}
}
32 changes: 32 additions & 0 deletions tests/src/main/java/testcases/T068_scope_factory/GRAPH.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
########################################################################
# #
# This file is auto-generated by running the Motif compiler tests and #
# serves a as validation of graph correctness. IntelliJ plugin tests #
# also rely on this file to ensure that the plugin graph understanding #
# is equivalent to the compiler's. #
# #
# - Do not edit manually. #
# - Commit changes to source control. #
# - Since this file is autogenerated, code review changes carefully to #
# ensure correctness. #
# #
########################################################################

-------
| Scope |
-------

==== Required ====

---- String ----
[ Provided By ]
[ Consumed By ]
* Scope | Scope.string()

==== Provides ====

---- Scope | implicit ----
[ Required ]
[ Consumed By ]


29 changes: 29 additions & 0 deletions tests/src/main/java/testcases/T068_scope_factory/Scope.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2018-2019 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package testcases.T068_scope_factory;

import motif.Creatable;

@motif.Scope
public interface Scope extends Creatable<Scope.Dependencies> {

String string();

interface Dependencies {

String s();
}
}
33 changes: 33 additions & 0 deletions tests/src/main/java/testcases/T068_scope_factory/Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2019 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package testcases.T068_scope_factory;

import motif.ScopeFactory;

import static com.google.common.truth.Truth.assertThat;

public class Test {

public static void run() {
Scope scope = ScopeFactory.create(Scope.class, new Scope.Dependencies() {
@Override
public String s() {
return "s";
}
});
assertThat(scope.string()).isEqualTo("s");
}
}
9 changes: 9 additions & 0 deletions tests/src/main/java/testcases/T068_scope_factory/config.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-dontshrink
-keep class **Test {
public static void run();
}

-keepnames @motif.Scope interface *
-keepnames @motif.ScopeImpl class * {
<init>(...);
}

0 comments on commit aa8933d

Please sign in to comment.