{"action":"created","comment":{"url":"https://api.github.com/repos/realm/realm-java/pulls/comments/52182081","id":52182081,"diff_hunk":"@@ -0,0 +1,181 @@\n+/*\n+ * Copyright 2016 Realm Inc.\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at\n+ *\n+ * http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+package io.realm.transformer\n+import com.android.SdkConstants\n+import com.android.build.api.transform.*\n+import com.google.common.collect.ImmutableSet\n+import com.google.common.collect.Sets\n+import groovy.io.FileType\n+import io.realm.annotations.Ignore\n+import javassist.ClassPool\n+import javassist.LoaderClassPath\n+import org.slf4j.Logger\n+import org.slf4j.LoggerFactory\n+\n+import java.lang.reflect.Modifier\n+import java.util.jar.JarFile\n+\n+import static com.android.build.api.transform.QualifiedContent.*\n+/**\n+ * This class implements the Transform API provided by the Android Gradle plugin\n+ */\n+class RealmTransformer extends Transform {\n+\n+    private Logger logger = LoggerFactory.getLogger('realm-logger')\n+\n+    @Override\n+    String getName() {\n+        return \"RealmTransformer\"\n+    }\n+\n+    @Override\n+    Set<ContentType> getInputTypes() {\n+        return ImmutableSet.<ContentType> of(DefaultContentType.CLASSES)\n+    }\n+\n+    @Override\n+    Set<Scope> getScopes() {\n+        return Sets.immutableEnumSet(Scope.PROJECT)\n+    }\n+\n+    @Override\n+    Set<Scope> getReferencedScopes() {\n+        return Sets.immutableEnumSet(Scope.EXTERNAL_LIBRARIES, Scope.TESTED_CODE)\n+    }\n+\n+    @Override\n+    boolean isIncremental() {\n+        return false\n+    }\n+\n+    @Override\n+    void transform(Context context, Collection<TransformInput> inputs, Collection<TransformInput> referencedInputs,\n+                   TransformOutputProvider outputProvider, boolean isIncremental)\n+            throws IOException, TransformException, InterruptedException {\n+\n+        def tic = System.currentTimeMillis()\n+\n+        // Find all the class names\n+        def classNames = getClassNames(inputs)\n+\n+        // Create and populate the Javassist class pool\n+        ClassPool classPool = createClassPool(inputs, referencedInputs)\n+\n+        logger.info \"ClassPool contains Realm classes: ${classPool.getOrNull('io.realm.RealmList') != null}\"\n+\n+        // Find the proxy classes\n+        def proxyClasses = classNames\n+                .findAll { it.endsWith('RealmProxy') }\n+                .collect { classPool.getCtClass(it)}\n+        logger.info \"Proxy Classes: ${proxyClasses*.name}\"\n+\n+        // Find the model classes\n+        def modelClasses = proxyClasses.collect { it.superclass }\n+        logger.info \"Model Classes: ${modelClasses*.name}\"\n+\n+        // Populate a list of the fields that need to be managed with bytecode manipulation\n+        def managedFields = []\n+        modelClasses.each {\n+            managedFields.addAll(it.declaredFields.findAll {\n+                it.getAnnotations()\n+                it.getAnnotation(Ignore.class) == null && !Modifier.isStatic(it.getModifiers())\n+            })\n+        }\n+        logger.info \"Managed Fields: ${managedFields*.name}\"\n+\n+        // Add accessors to the model classes\n+        modelClasses.each {\n+            BytecodeModifier.addRealmAccessors(it)\n+            def proxyInterface = classPool.get(\"io.realm.${it.getSimpleName()}RealmProxyInterface\")\n+            it.addInterface(proxyInterface)\n+        }\n+\n+        // Use accessors instead of direct field access\n+        classNames.each {\n+            logger.info \"  Modifying class ${it}\"\n+            def ctClass = classPool.getCtClass(it)\n+            BytecodeModifier.useRealmAccessors(ctClass, managedFields, modelClasses)\n+            ctClass.writeFile(outputProvider.getContentLocation(\n+                    'realm', getInputTypes(), getScopes(), Format.DIRECTORY).canonicalPath)\n+        }\n+\n+        def toc = System.currentTimeMillis()\n+        logger.info \"Realm Transform time: ${toc-tic} milliseconds\"\n+    }\n+\n+    /**\n+     * Create and populate the Javassist class pool\n+     * @param inputs The inputs provided by the Transform API\n+     * @param referencedInputs the referencedInputs provided by the Transform API\n+     * @return the populated ClassPool instance\n+     */\n+    private ClassPool createClassPool(Collection<TransformInput> inputs, Collection<TransformInput> referencedInputs) {\n+        // Don't use ClassPool.getDefault(). Doing consecutive builds in the same run (e.g. debug+release)\n+        // will use a cached object and all the classes will result frozen.\n+        ClassPool classPool = new ClassPool(null)\n+        classPool.appendSystemPath()\n+        classPool.appendClassPath(new LoaderClassPath(getClass().getClassLoader()))\n+\n+        inputs.each {\n+            it.directoryInputs.each {\n+                classPool.appendClassPath(it.file.absolutePath)\n+            }\n+\n+            it.jarInputs.stream().each {","path":"realm-transformer/src/main/groovy/io/realm/transformer/RealmTransformer.groovy","position":138,"original_position":138,"commit_id":"37e80cfa380a91849d9da3dc33908293c40fff26","original_commit_id":"37e80cfa380a91849d9da3dc33908293c40fff26","user":{"login":"cmelchior","id":406066,"avatar_url":"https://avatars.githubusercontent.com/u/406066?v=3","gravatar_id":"","url":"https://api.github.com/users/cmelchior","html_url":"https://github.com/cmelchior","followers_url":"https://api.github.com/users/cmelchior/followers","following_url":"https://api.github.com/users/cmelchior/following{/other_user}","gists_url":"https://api.github.com/users/cmelchior/gists{/gist_id}","starred_url":"https://api.github.com/users/cmelchior/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cmelchior/subscriptions","organizations_url":"https://api.github.com/users/cmelchior/orgs","repos_url":"https://api.github.com/users/cmelchior/repos","events_url":"https://api.github.com/users/cmelchior/events{/privacy}","received_events_url":"https://api.github.com/users/cmelchior/received_events","type":"User","site_admin":false},"body":"It this adding all jars to the pool as well? Is that necessary?","created_at":"2016-02-08T15:26:55Z","updated_at":"2016-02-08T15:26:55Z","html_url":"https://github.com/realm/realm-java/pull/2196#discussion_r52182081","pull_request_url":"https://api.github.com/repos/realm/realm-java/pulls/2196","_links":{"self":{"href":"https://api.github.com/repos/realm/realm-java/pulls/comments/52182081"},"html":{"href":"https://github.com/realm/realm-java/pull/2196#discussion_r52182081"},"pull_request":{"href":"https://api.github.com/repos/realm/realm-java/pulls/2196"}}},"pull_request":{"url":"https://api.github.com/repos/realm/realm-java/pulls/2196","id":57630467,"html_url":"https://github.com/realm/realm-java/pull/2196","diff_url":"https://github.com/realm/realm-java/pull/2196.diff","patch_url":"https://github.com/realm/realm-java/pull/2196.patch","issue_url":"https://api.github.com/repos/realm/realm-java/issues/2196","number":2196,"state":"open","locked":false,"title":"Allow custom methods and public fields","user":{"login":"emanuelez","id":473050,"avatar_url":"https://avatars.githubusercontent.com/u/473050?v=3","gravatar_id":"","url":"https://api.github.com/users/emanuelez","html_url":"https://github.com/emanuelez","followers_url":"https://api.github.com/users/emanuelez/followers","following_url":"https://api.github.com/users/emanuelez/following{/other_user}","gists_url":"https://api.github.com/users/emanuelez/gists{/gist_id}","starred_url":"https://api.github.com/users/emanuelez/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/emanuelez/subscriptions","organizations_url":"https://api.github.com/users/emanuelez/orgs","repos_url":"https://api.github.com/users/emanuelez/repos","events_url":"https://api.github.com/users/emanuelez/events{/privacy}","received_events_url":"https://api.github.com/users/emanuelez/received_events","type":"User","site_admin":false},"body":"This PR introduces the support for custom methods and public fields. This is achieved with bytecode manipulation which will perform two tasks:\n\n * For every field add accessors named `<fieldName>$realmGetter()` and `<fieldName>$realmSetter()`\n * Substitute all field accesses (apart from those in the new accessors) with the invocation of the new accessors\n\nSince the bytecode manipulation step need to happen after the annotation processor execution, the new accessors have been added to the model classes of the unit tests of `realm/realm-library`.\n\nAlso, the annotation processor has been modified to generate accessors with the new name, instead of having to guess what the user did.\n\nFixes #909","created_at":"2016-01-29T10:32:31Z","updated_at":"2016-02-08T15:26:55Z","closed_at":null,"merged_at":null,"merge_commit_sha":"56d504f9791915953f88a0bf7fbeee1b024fd91d","assignee":{"login":"emanuelez","id":473050,"avatar_url":"https://avatars.githubusercontent.com/u/473050?v=3","gravatar_id":"","url":"https://api.github.com/users/emanuelez","html_url":"https://github.com/emanuelez","followers_url":"https://api.github.com/users/emanuelez/followers","following_url":"https://api.github.com/users/emanuelez/following{/other_user}","gists_url":"https://api.github.com/users/emanuelez/gists{/gist_id}","starred_url":"https://api.github.com/users/emanuelez/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/emanuelez/subscriptions","organizations_url":"https://api.github.com/users/emanuelez/orgs","repos_url":"https://api.github.com/users/emanuelez/repos","events_url":"https://api.github.com/users/emanuelez/events{/privacy}","received_events_url":"https://api.github.com/users/emanuelez/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/realm/realm-java/pulls/2196/commits","review_comments_url":"https://api.github.com/repos/realm/realm-java/pulls/2196/comments","review_comment_url":"https://api.github.com/repos/realm/realm-java/pulls/comments{/number}","comments_url":"https://api.github.com/repos/realm/realm-java/issues/2196/comments","statuses_url":"https://api.github.com/repos/realm/realm-java/statuses/37e80cfa380a91849d9da3dc33908293c40fff26","head":{"label":"realm:feature/better-objects","ref":"feature/better-objects","sha":"37e80cfa380a91849d9da3dc33908293c40fff26","user":{"login":"realm","id":7575099,"avatar_url":"https://avatars.githubusercontent.com/u/7575099?v=3","gravatar_id":"","url":"https://api.github.com/users/realm","html_url":"https://github.com/realm","followers_url":"https://api.github.com/users/realm/followers","following_url":"https://api.github.com/users/realm/following{/other_user}","gists_url":"https://api.github.com/users/realm/gists{/gist_id}","starred_url":"https://api.github.com/users/realm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/realm/subscriptions","organizations_url":"https://api.github.com/users/realm/orgs","repos_url":"https://api.github.com/users/realm/repos","events_url":"https://api.github.com/users/realm/events{/privacy}","received_events_url":"https://api.github.com/users/realm/received_events","type":"Organization","site_admin":false},"repo":{"id":4084908,"name":"realm-java","full_name":"realm/realm-java","owner":{"login":"realm","id":7575099,"avatar_url":"https://avatars.githubusercontent.com/u/7575099?v=3","gravatar_id":"","url":"https://api.github.com/users/realm","html_url":"https://github.com/realm","followers_url":"https://api.github.com/users/realm/followers","following_url":"https://api.github.com/users/realm/following{/other_user}","gists_url":"https://api.github.com/users/realm/gists{/gist_id}","starred_url":"https://api.github.com/users/realm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/realm/subscriptions","organizations_url":"https://api.github.com/users/realm/orgs","repos_url":"https://api.github.com/users/realm/repos","events_url":"https://api.github.com/users/realm/events{/privacy}","received_events_url":"https://api.github.com/users/realm/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/realm/realm-java","description":"Realm is a mobile database: a replacement for SQLite & ORMs","fork":false,"url":"https://api.github.com/repos/realm/realm-java","forks_url":"https://api.github.com/repos/realm/realm-java/forks","keys_url":"https://api.github.com/repos/realm/realm-java/keys{/key_id}","collaborators_url":"https://api.github.com/repos/realm/realm-java/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/realm/realm-java/teams","hooks_url":"https://api.github.com/repos/realm/realm-java/hooks","issue_events_url":"https://api.github.com/repos/realm/realm-java/issues/events{/number}","events_url":"https://api.github.com/repos/realm/realm-java/events","assignees_url":"https://api.github.com/repos/realm/realm-java/assignees{/user}","branches_url":"https://api.github.com/repos/realm/realm-java/branches{/branch}","tags_url":"https://api.github.com/repos/realm/realm-java/tags","blobs_url":"https://api.github.com/repos/realm/realm-java/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/realm/realm-java/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/realm/realm-java/git/refs{/sha}","trees_url":"https://api.github.com/repos/realm/realm-java/git/trees{/sha}","statuses_url":"https://api.github.com/repos/realm/realm-java/statuses/{sha}","languages_url":"https://api.github.com/repos/realm/realm-java/languages","stargazers_url":"https://api.github.com/repos/realm/realm-java/stargazers","contributors_url":"https://api.github.com/repos/realm/realm-java/contributors","subscribers_url":"https://api.github.com/repos/realm/realm-java/subscribers","subscription_url":"https://api.github.com/repos/realm/realm-java/subscription","commits_url":"https://api.github.com/repos/realm/realm-java/commits{/sha}","git_commits_url":"https://api.github.com/repos/realm/realm-java/git/commits{/sha}","comments_url":"https://api.github.com/repos/realm/realm-java/comments{/number}","issue_comment_url":"https://api.github.com/repos/realm/realm-java/issues/comments{/number}","contents_url":"https://api.github.com/repos/realm/realm-java/contents/{+path}","compare_url":"https://api.github.com/repos/realm/realm-java/compare/{base}...{head}","merges_url":"https://api.github.com/repos/realm/realm-java/merges","archive_url":"https://api.github.com/repos/realm/realm-java/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/realm/realm-java/downloads","issues_url":"https://api.github.com/repos/realm/realm-java/issues{/number}","pulls_url":"https://api.github.com/repos/realm/realm-java/pulls{/number}","milestones_url":"https://api.github.com/repos/realm/realm-java/milestones{/number}","notifications_url":"https://api.github.com/repos/realm/realm-java/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/realm/realm-java/labels{/name}","releases_url":"https://api.github.com/repos/realm/realm-java/releases{/id}","deployments_url":"https://api.github.com/repos/realm/realm-java/deployments","created_at":"2012-04-20T09:18:42Z","updated_at":"2016-02-08T11:06:51Z","pushed_at":"2016-02-08T13:55:37Z","git_url":"git://github.com/realm/realm-java.git","ssh_url":"git@github.com:realm/realm-java.git","clone_url":"https://github.com/realm/realm-java.git","svn_url":"https://github.com/realm/realm-java","homepage":"http://realm.io","size":46014,"stargazers_count":3561,"watchers_count":3561,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":402,"mirror_url":null,"open_issues_count":213,"forks":402,"open_issues":213,"watchers":3561,"default_branch":"master"}},"base":{"label":"realm:master","ref":"master","sha":"2e000b912b61041da4fe5e679c08a53f903b174d","user":{"login":"realm","id":7575099,"avatar_url":"https://avatars.githubusercontent.com/u/7575099?v=3","gravatar_id":"","url":"https://api.github.com/users/realm","html_url":"https://github.com/realm","followers_url":"https://api.github.com/users/realm/followers","following_url":"https://api.github.com/users/realm/following{/other_user}","gists_url":"https://api.github.com/users/realm/gists{/gist_id}","starred_url":"https://api.github.com/users/realm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/realm/subscriptions","organizations_url":"https://api.github.com/users/realm/orgs","repos_url":"https://api.github.com/users/realm/repos","events_url":"https://api.github.com/users/realm/events{/privacy}","received_events_url":"https://api.github.com/users/realm/received_events","type":"Organization","site_admin":false},"repo":{"id":4084908,"name":"realm-java","full_name":"realm/realm-java","owner":{"login":"realm","id":7575099,"avatar_url":"https://avatars.githubusercontent.com/u/7575099?v=3","gravatar_id":"","url":"https://api.github.com/users/realm","html_url":"https://github.com/realm","followers_url":"https://api.github.com/users/realm/followers","following_url":"https://api.github.com/users/realm/following{/other_user}","gists_url":"https://api.github.com/users/realm/gists{/gist_id}","starred_url":"https://api.github.com/users/realm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/realm/subscriptions","organizations_url":"https://api.github.com/users/realm/orgs","repos_url":"https://api.github.com/users/realm/repos","events_url":"https://api.github.com/users/realm/events{/privacy}","received_events_url":"https://api.github.com/users/realm/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/realm/realm-java","description":"Realm is a mobile database: a replacement for SQLite & ORMs","fork":false,"url":"https://api.github.com/repos/realm/realm-java","forks_url":"https://api.github.com/repos/realm/realm-java/forks","keys_url":"https://api.github.com/repos/realm/realm-java/keys{/key_id}","collaborators_url":"https://api.github.com/repos/realm/realm-java/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/realm/realm-java/teams","hooks_url":"https://api.github.com/repos/realm/realm-java/hooks","issue_events_url":"https://api.github.com/repos/realm/realm-java/issues/events{/number}","events_url":"https://api.github.com/repos/realm/realm-java/events","assignees_url":"https://api.github.com/repos/realm/realm-java/assignees{/user}","branches_url":"https://api.github.com/repos/realm/realm-java/branches{/branch}","tags_url":"https://api.github.com/repos/realm/realm-java/tags","blobs_url":"https://api.github.com/repos/realm/realm-java/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/realm/realm-java/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/realm/realm-java/git/refs{/sha}","trees_url":"https://api.github.com/repos/realm/realm-java/git/trees{/sha}","statuses_url":"https://api.github.com/repos/realm/realm-java/statuses/{sha}","languages_url":"https://api.github.com/repos/realm/realm-java/languages","stargazers_url":"https://api.github.com/repos/realm/realm-java/stargazers","contributors_url":"https://api.github.com/repos/realm/realm-java/contributors","subscribers_url":"https://api.github.com/repos/realm/realm-java/subscribers","subscription_url":"https://api.github.com/repos/realm/realm-java/subscription","commits_url":"https://api.github.com/repos/realm/realm-java/commits{/sha}","git_commits_url":"https://api.github.com/repos/realm/realm-java/git/commits{/sha}","comments_url":"https://api.github.com/repos/realm/realm-java/comments{/number}","issue_comment_url":"https://api.github.com/repos/realm/realm-java/issues/comments{/number}","contents_url":"https://api.github.com/repos/realm/realm-java/contents/{+path}","compare_url":"https://api.github.com/repos/realm/realm-java/compare/{base}...{head}","merges_url":"https://api.github.com/repos/realm/realm-java/merges","archive_url":"https://api.github.com/repos/realm/realm-java/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/realm/realm-java/downloads","issues_url":"https://api.github.com/repos/realm/realm-java/issues{/number}","pulls_url":"https://api.github.com/repos/realm/realm-java/pulls{/number}","milestones_url":"https://api.github.com/repos/realm/realm-java/milestones{/number}","notifications_url":"https://api.github.com/repos/realm/realm-java/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/realm/realm-java/labels{/name}","releases_url":"https://api.github.com/repos/realm/realm-java/releases{/id}","deployments_url":"https://api.github.com/repos/realm/realm-java/deployments","created_at":"2012-04-20T09:18:42Z","updated_at":"2016-02-08T11:06:51Z","pushed_at":"2016-02-08T13:55:37Z","git_url":"git://github.com/realm/realm-java.git","ssh_url":"git@github.com:realm/realm-java.git","clone_url":"https://github.com/realm/realm-java.git","svn_url":"https://github.com/realm/realm-java","homepage":"http://realm.io","size":46014,"stargazers_count":3561,"watchers_count":3561,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":402,"mirror_url":null,"open_issues_count":213,"forks":402,"open_issues":213,"watchers":3561,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/realm/realm-java/pulls/2196"},"html":{"href":"https://github.com/realm/realm-java/pull/2196"},"issue":{"href":"https://api.github.com/repos/realm/realm-java/issues/2196"},"comments":{"href":"https://api.github.com/repos/realm/realm-java/issues/2196/comments"},"review_comments":{"href":"https://api.github.com/repos/realm/realm-java/pulls/2196/comments"},"review_comment":{"href":"https://api.github.com/repos/realm/realm-java/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/realm/realm-java/pulls/2196/commits"},"statuses":{"href":"https://api.github.com/repos/realm/realm-java/statuses/37e80cfa380a91849d9da3dc33908293c40fff26"}}}}