diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b588b5a9..b51b24054 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: run: > sudo apt-get install -y git cmake make g++ gcc-7-plugin-dev libboost-all-dev llvm-10-dev clang-10 libclang-10-dev default-jdk libssl1.0-dev libgraphviz-dev - libmagic-dev libgit2-dev ctags libgtest-dev npm libldap2-dev + libmagic-dev libgit2-dev ctags libgtest-dev npm libldap2-dev unzip - name: Install Postgresql Ubuntu 18 if: ${{ matrix.os == 'ubuntu-18.04' && matrix.db == 'postgresql' }} @@ -127,7 +127,7 @@ jobs: run: > sudo apt-get install -y git cmake make g++ libboost-all-dev llvm-10-dev clang-10 libclang-10-dev odb libodb-dev thrift-compiler libthrift-dev default-jdk libssl-dev - libgraphviz-dev libmagic-dev libgit2-dev ctags libgtest-dev npm libldap2-dev + libgraphviz-dev libmagic-dev libgit2-dev ctags libgtest-dev npm libldap2-dev unzip - name: Install Postgresql Ubuntu 20 if: ${{ matrix.os == 'ubuntu-20.04' && matrix.db == 'postgresql' }} @@ -256,7 +256,7 @@ jobs: sudo apt-get install -y git cmake make g++ gcc-7-plugin-dev libgraphviz-dev libboost-filesystem-dev libboost-log-dev libboost-program-options-dev llvm-10-dev clang-10 libclang-10-dev default-jre libssl1.0-dev libmagic-dev - libgit2-dev ctags libgtest-dev libldap-2.4-2 + libgit2-dev ctags libgtest-dev libldap-2.4-2 unzip - name: Install Postgresql Ubuntu 18 if: ${{ matrix.os == 'ubuntu-18.04' && matrix.db == 'postgresql' }} @@ -296,7 +296,7 @@ jobs: libboost-log-dev libboost-program-options-dev llvm-10-dev clang-10 libclang-10-dev libgraphviz-dev libgtest-dev odb libodb-dev libthrift-dev default-jre libssl1.1 libmagic-dev libgit2-dev ctags - libldap-2.4-2 + libldap-2.4-2 unzip - name: Install Postgresql Ubuntu 20 if: ${{ matrix.os == 'ubuntu-20.04' && matrix.db == 'postgresql' }} diff --git a/docker/dev/Dockerfile b/docker/dev/Dockerfile index 8cfc0be5d..c206196fb 100644 --- a/docker/dev/Dockerfile +++ b/docker/dev/Dockerfile @@ -11,6 +11,7 @@ RUN set -x && apt-get update -qq \ cmake make \ default-jdk \ ctags \ + unzip \ gcc-9 gcc-9-plugin-dev g++-9 \ libboost-filesystem-dev \ libboost-log-dev \ diff --git a/docker/runtime/Dockerfile b/docker/runtime/Dockerfile index 7e62f4567..243eba09d 100644 --- a/docker/runtime/Dockerfile +++ b/docker/runtime/Dockerfile @@ -71,6 +71,7 @@ RUN set -x && apt-get update -qq && \ libldap-2.4-2 \ libmagic-dev \ libthrift-dev \ + unzip \ ctags && \ apt-get clean && \ rm -rf /var/lib/apt/lists/ && \ diff --git a/lib/java/commons-io-2.8.0.jar b/lib/java/commons-io-2.8.0.jar new file mode 100644 index 000000000..177e58dc6 Binary files /dev/null and b/lib/java/commons-io-2.8.0.jar differ diff --git a/lib/java/eclipselink-2.7.8.jar b/lib/java/eclipselink-2.7.8.jar new file mode 100644 index 000000000..d4526c0cf Binary files /dev/null and b/lib/java/eclipselink-2.7.8.jar differ diff --git a/lib/java/jakarta.persistence-api-2.2.3.jar b/lib/java/jakarta.persistence-api-2.2.3.jar new file mode 100644 index 000000000..9c758cf5c Binary files /dev/null and b/lib/java/jakarta.persistence-api-2.2.3.jar differ diff --git a/lib/java/log4j-1.2-api-2.15.0.jar b/lib/java/log4j-1.2-api-2.15.0.jar new file mode 100644 index 000000000..d49351691 Binary files /dev/null and b/lib/java/log4j-1.2-api-2.15.0.jar differ diff --git a/lib/java/log4j-1.2.17.jar b/lib/java/log4j-1.2.17.jar deleted file mode 100644 index 1d425cf7d..000000000 Binary files a/lib/java/log4j-1.2.17.jar and /dev/null differ diff --git a/lib/java/log4j-api-2.15.0.jar b/lib/java/log4j-api-2.15.0.jar new file mode 100644 index 000000000..77f6b2bef Binary files /dev/null and b/lib/java/log4j-api-2.15.0.jar differ diff --git a/lib/java/log4j-core-2.15.0.jar b/lib/java/log4j-core-2.15.0.jar new file mode 100644 index 000000000..f7e14b822 Binary files /dev/null and b/lib/java/log4j-core-2.15.0.jar differ diff --git a/lib/java/log4j-slf4j-impl-2.15.0.jar b/lib/java/log4j-slf4j-impl-2.15.0.jar new file mode 100644 index 000000000..d12e9c4a3 Binary files /dev/null and b/lib/java/log4j-slf4j-impl-2.15.0.jar differ diff --git a/lib/java/org.eclipse.core.contenttype_3.7.600.v20200124-1609.jar b/lib/java/org.eclipse.core.contenttype_3.7.600.v20200124-1609.jar new file mode 100644 index 000000000..fee05aead Binary files /dev/null and b/lib/java/org.eclipse.core.contenttype_3.7.600.v20200124-1609.jar differ diff --git a/lib/java/org.eclipse.core.jobs_3.10.600.v20191122-2104.jar b/lib/java/org.eclipse.core.jobs_3.10.600.v20191122-2104.jar new file mode 100644 index 000000000..83289a6de Binary files /dev/null and b/lib/java/org.eclipse.core.jobs_3.10.600.v20191122-2104.jar differ diff --git a/lib/java/org.eclipse.core.resources_3.13.600.v20191122-2104.jar b/lib/java/org.eclipse.core.resources_3.13.600.v20191122-2104.jar new file mode 100644 index 000000000..cd8fadfd1 Binary files /dev/null and b/lib/java/org.eclipse.core.resources_3.13.600.v20191122-2104.jar differ diff --git a/lib/java/org.eclipse.core.runtime_3.17.100.v20200203-0917.jar b/lib/java/org.eclipse.core.runtime_3.17.100.v20200203-0917.jar new file mode 100644 index 000000000..d714c3afd Binary files /dev/null and b/lib/java/org.eclipse.core.runtime_3.17.100.v20200203-0917.jar differ diff --git a/lib/java/org.eclipse.equinox.common_3.10.600.v20191004-1420.jar b/lib/java/org.eclipse.equinox.common_3.10.600.v20191004-1420.jar new file mode 100644 index 000000000..32f511a37 Binary files /dev/null and b/lib/java/org.eclipse.equinox.common_3.10.600.v20191004-1420.jar differ diff --git a/lib/java/org.eclipse.equinox.preferences_3.7.500.v20190815-1535.jar b/lib/java/org.eclipse.equinox.preferences_3.7.500.v20190815-1535.jar new file mode 100644 index 000000000..2bd881a6d Binary files /dev/null and b/lib/java/org.eclipse.equinox.preferences_3.7.500.v20190815-1535.jar differ diff --git a/lib/java/org.eclipse.jdt.core_3.24.0.v20201123-0742.jar b/lib/java/org.eclipse.jdt.core_3.24.0.v20201123-0742.jar new file mode 100644 index 000000000..589117ff3 Binary files /dev/null and b/lib/java/org.eclipse.jdt.core_3.24.0.v20201123-0742.jar differ diff --git a/lib/java/org.eclipse.osgi_3.15.0.v20190830-1434.jar b/lib/java/org.eclipse.osgi_3.15.0.v20190830-1434.jar new file mode 100644 index 000000000..262e662fb Binary files /dev/null and b/lib/java/org.eclipse.osgi_3.15.0.v20190830-1434.jar differ diff --git a/lib/java/postgresql-42.2.22.jar b/lib/java/postgresql-42.2.22.jar new file mode 100644 index 000000000..fd33f2956 Binary files /dev/null and b/lib/java/postgresql-42.2.22.jar differ diff --git a/lib/java/procyon-compilertools-0.5.36.jar b/lib/java/procyon-compilertools-0.5.36.jar new file mode 100644 index 000000000..c7584d1ec Binary files /dev/null and b/lib/java/procyon-compilertools-0.5.36.jar differ diff --git a/lib/java/procyon-core-0.5.36.jar b/lib/java/procyon-core-0.5.36.jar new file mode 100644 index 000000000..7fdcb2cfa Binary files /dev/null and b/lib/java/procyon-core-0.5.36.jar differ diff --git a/lib/java/slf4j-log4j12-1.7.25.jar b/lib/java/slf4j-log4j12-1.7.25.jar deleted file mode 100644 index 7d88a6e75..000000000 Binary files a/lib/java/slf4j-log4j12-1.7.25.jar and /dev/null differ diff --git a/lib/java/sqlite-jdbc-3.34.0.jar b/lib/java/sqlite-jdbc-3.34.0.jar new file mode 100644 index 000000000..e3374e12a Binary files /dev/null and b/lib/java/sqlite-jdbc-3.34.0.jar differ diff --git a/plugins/cpp/webgui/js/cppInfoTree.js b/plugins/cpp/webgui/js/cppInfoTree.js index e112f40b4..fb947f5df 100644 --- a/plugins/cpp/webgui/js/cppInfoTree.js +++ b/plugins/cpp/webgui/js/cppInfoTree.js @@ -291,6 +291,7 @@ function (model, viewHandler, util) { } var cppInfoTree = { + id: 'cpp-infotree', render : function (elementInfo) { var ret = []; diff --git a/plugins/dummy/parser/CMakeLists.txt b/plugins/dummy/parser/CMakeLists.txt index 8542e1aba..c85ea1811 100644 --- a/plugins/dummy/parser/CMakeLists.txt +++ b/plugins/dummy/parser/CMakeLists.txt @@ -5,7 +5,7 @@ include_directories( ${PROJECT_SOURCE_DIR}/parser/include) add_library(dummyparser SHARED - src/dummyparser.cpp) + src/dummyparser.cpp) target_compile_options(dummyparser PUBLIC -Wno-unknown-pragmas) diff --git a/plugins/dummy/service/CMakeLists.txt b/plugins/dummy/service/CMakeLists.txt index 3ab19e8ae..66d08cf05 100644 --- a/plugins/dummy/service/CMakeLists.txt +++ b/plugins/dummy/service/CMakeLists.txt @@ -33,7 +33,7 @@ add_library(dummythrift STATIC target_compile_options(dummythrift PUBLIC -fPIC) add_library(dummyservice SHARED - src/dummyservice.cpp + src/javaservice.cpp src/plugin.cpp) target_compile_options(dummyservice PUBLIC -Wno-unknown-pragmas) diff --git a/plugins/dummy/service/include/service/dummyservice.h b/plugins/dummy/service/include/service/javaservice.h similarity index 100% rename from plugins/dummy/service/include/service/dummyservice.h rename to plugins/dummy/service/include/service/javaservice.h diff --git a/plugins/dummy/service/src/dummyservice.cpp b/plugins/dummy/service/src/javaservice.cpp similarity index 93% rename from plugins/dummy/service/src/dummyservice.cpp rename to plugins/dummy/service/src/javaservice.cpp index 091618a8f..7dd57cd08 100644 --- a/plugins/dummy/service/src/dummyservice.cpp +++ b/plugins/dummy/service/src/javaservice.cpp @@ -1,4 +1,4 @@ -#include +#include #include namespace cc diff --git a/plugins/dummy/service/src/plugin.cpp b/plugins/dummy/service/src/plugin.cpp index 95b596bfd..f0fcfda65 100644 --- a/plugins/dummy/service/src/plugin.cpp +++ b/plugins/dummy/service/src/plugin.cpp @@ -1,6 +1,6 @@ #include -#include +#include /* These two methods are used by the plugin manager to allow dynamic loading of CodeCompass Service plugins. Clang (>= version 6.0) gives a warning that diff --git a/plugins/java/CMakeLists.txt b/plugins/java/CMakeLists.txt new file mode 100644 index 000000000..6cd51715e --- /dev/null +++ b/plugins/java/CMakeLists.txt @@ -0,0 +1,7 @@ +add_subdirectory(logger) +add_subdirectory(model) +add_subdirectory(parser) +# add_subdirectory(test) +add_subdirectory(service) + +install_webplugin(webgui) \ No newline at end of file diff --git a/plugins/java/logger/CMakeLists.txt b/plugins/java/logger/CMakeLists.txt new file mode 100644 index 000000000..84714f033 --- /dev/null +++ b/plugins/java/logger/CMakeLists.txt @@ -0,0 +1,12 @@ +set(CMAKE_JAVA_INCLUDE_PATH + ${PROJECT_SOURCE_DIR}/lib/java/* + ${PLUGIN_DIR}/lib/java/*) + +add_jar(javalogger + SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/Logger.java + META-INF/logging.properties + MANIFEST ${CMAKE_CURRENT_SOURCE_DIR}/META-INF/MANIFEST.MF + OUTPUT_NAME javalogger) + +install_jar(javalogger "${INSTALL_JAVA_LIB_DIR}") \ No newline at end of file diff --git a/plugins/java/logger/Logger.java b/plugins/java/logger/Logger.java new file mode 100644 index 000000000..2e98f2bc9 --- /dev/null +++ b/plugins/java/logger/Logger.java @@ -0,0 +1,25 @@ +package logger; + +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.LogManager; + +public abstract class Logger { + public static final java.util.logging.Logger LOGGER = initLogger(); + + private static java.util.logging.Logger initLogger() { + InputStream stream = Logger.class.getClassLoader(). + getResourceAsStream("META-INF/logging.properties"); + + try { + LogManager.getLogManager().readConfiguration(stream); + return java.util.logging.Logger.getLogger(Logger.class.getName()); + + } catch (IOException e) { + System.err.println( + "Logger initialization for Java plugin has been failed." + ); + } + return null; + } +} diff --git a/plugins/java/logger/META-INF/MANIFEST.MF b/plugins/java/logger/META-INF/MANIFEST.MF new file mode 100644 index 000000000..e388f0d25 --- /dev/null +++ b/plugins/java/logger/META-INF/MANIFEST.MF @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +Class-Path: log4j-1.2-api-2.15.0.jar + log4j-api-2.15.0.jar + log4j-core-2.15.0.jar + slf4j-api-1.7.25.jar + log4j-slf4j-impl-2.15.0.jar diff --git a/plugins/java/logger/META-INF/logging.properties b/plugins/java/logger/META-INF/logging.properties new file mode 100644 index 000000000..3dc857bba --- /dev/null +++ b/plugins/java/logger/META-INF/logging.properties @@ -0,0 +1,5 @@ +handlers= java.util.logging.ConsoleHandler +.level= INFO +java.util.logging.ConsoleHandler.level = INFO +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter +java.util.logging.SimpleFormatter.format=[%4$s] %5$s%6$s%n \ No newline at end of file diff --git a/plugins/java/model/CMakeLists.txt b/plugins/java/model/CMakeLists.txt new file mode 100644 index 000000000..ec802cc1c --- /dev/null +++ b/plugins/java/model/CMakeLists.txt @@ -0,0 +1,39 @@ +set(CMAKE_JAVA_INCLUDE_PATH + ${PROJECT_SOURCE_DIR}/lib/java/* + ${PLUGIN_DIR}/lib/java/*) + +set(CMAKE_JAVA_COMPILE_FLAGS + ${CMAKE_JAVA_COMPILE_FLAGS} + -sourcepath ${CMAKE_CURRENT_SOURCE_DIR}:${PLUGIN_DIR}) + +add_jar(javamodel + SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/enums/AstType.java + ${CMAKE_CURRENT_SOURCE_DIR}/enums/InitializerKind.java + ${CMAKE_CURRENT_SOURCE_DIR}/enums/MemberTypeKind.java + ${CMAKE_CURRENT_SOURCE_DIR}/enums/RelationKind.java + ${CMAKE_CURRENT_SOURCE_DIR}/enums/SymbolType.java + ${CMAKE_CURRENT_SOURCE_DIR}/enums/Visibility.java + ${CMAKE_CURRENT_SOURCE_DIR}/EMFactory.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaAnnotation.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaAstNode.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaConstructor.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaDocComment.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaEntity.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaEnum.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaEnumConstant.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaMethod.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaImport.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaInheritance.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaInitializer.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaMemberType.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaRecord.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaRelation.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaTypedEntity.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaVariable.java + META-INF/persistence.xml + MANIFEST ${CMAKE_CURRENT_SOURCE_DIR}/META-INF/MANIFEST.MF + INCLUDE_JARS javalogger + OUTPUT_NAME javamodel) + +install_jar(javamodel "${INSTALL_JAVA_LIB_DIR}") diff --git a/plugins/java/model/EMFactory.java b/plugins/java/model/EMFactory.java new file mode 100644 index 000000000..5d673d664 --- /dev/null +++ b/plugins/java/model/EMFactory.java @@ -0,0 +1,96 @@ +package model; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; +import javax.persistence.spi.PersistenceUnitTransactionType; +import java.util.Arrays; +import java.util.HashMap; +import java.util.logging.Level; + +import static logger.Logger.LOGGER; +import static org.eclipse.persistence.config.PersistenceUnitProperties.*; + +public class EMFactory { + private final EntityManagerFactory emf; + + public EMFactory(String rawDbContext, boolean dropAndCreateTables) { + this.emf = + Persistence.createEntityManagerFactory( + "ParserPU", + initProperties(rawDbContext, dropAndCreateTables) + ); + + + if (dropAndCreateTables) { + emf.getMetamodel().getEntities().forEach( + e -> + LOGGER.log( + Level.INFO, + String.join(" ", "Dropping table", e.getName()) + ) + ); + } + } + + public EntityManager createEntityManager() + { + return emf.createEntityManager(); + } + + private HashMap initProperties( + String rawDbContext, boolean dropAndCreateTables) + { + HashMap properties = new HashMap<>(); + String[] splitByColon = rawDbContext.split(":"); + String dbType = splitByColon[0]; + String contextString = splitByColon[1]; + boolean isSqlite = dbType.equals("sqlite"); + String driver = + isSqlite ? "org.sqlite.JDBC" : "org.postgresql.Driver"; + String[] contextList = contextString.split(";"); + HashMap contextMap = new HashMap<>(); + + if (dropAndCreateTables) { + properties.put("eclipselink.ddl-generation", "drop-and-create-tables"); + } else { + properties.put("eclipselink.ddl-generation", "create-tables"); + } + + + properties.put( + TRANSACTION_TYPE, + PersistenceUnitTransactionType.RESOURCE_LOCAL.name() + ); + + Arrays.stream(contextList).forEach(c -> { + String[] splitContext = c.split("="); + contextMap.put(splitContext[0], splitContext[1]); + }); + + String connString = + isSqlite ? + "jdbc:" + "sqlite" + ":/" + + contextMap.get("database") + .replaceFirst("^~", System.getProperty("user.home")) + : + "jdbc:" + "postgresql" + "://" + + contextMap.get("host") + ":" + + contextMap.get("port") + "/" + + contextMap.get("database"); + + properties.put(JDBC_DRIVER, driver); + properties.put(JDBC_URL, connString); + + if (!isSqlite) { + properties.put(JDBC_USER, contextMap.get("user")); + properties.put(JDBC_PASSWORD, contextMap.get("password")); + } + + return properties; + } + + public EntityManagerFactory getEmf() { + return emf; + } +} diff --git a/plugins/java/model/JavaAnnotation.java b/plugins/java/model/JavaAnnotation.java new file mode 100644 index 000000000..74cc859f5 --- /dev/null +++ b/plugins/java/model/JavaAnnotation.java @@ -0,0 +1,8 @@ +package model; + +import javax.persistence.*; + +@Entity +@Table(name = "\"JavaAnnotation\"") +public class JavaAnnotation extends JavaEntity { +} diff --git a/plugins/java/model/JavaAstNode.java b/plugins/java/model/JavaAstNode.java new file mode 100644 index 000000000..84148b244 --- /dev/null +++ b/plugins/java/model/JavaAstNode.java @@ -0,0 +1,171 @@ +package model; + +import model.enums.AstType; +import model.enums.SymbolType; + +import javax.persistence.*; + +@Entity +@Table(name = "\"JavaAstNode\"") +public class JavaAstNode { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Lob + @Column(name = "\"astValue\"") + private String astValue; + + @Column(name = "location_range_start_line") + private long location_range_start_line; + + @Column(name = "location_range_start_column") + private long location_range_start_column; + + @Column(name = "location_range_end_line") + private long location_range_end_line; + + @Column(name = "location_range_end_column") + private long location_range_end_column; + + @Column(name = "location_file") + private long location_file; + + @Column(name = "\"entityHash\"") + private long entityHash; + + @Column(name = "\"defEntityHash\"") + private long defEntityHash; + + @Column(name = "\"symbolType\"") + @Enumerated(EnumType.ORDINAL) + private SymbolType symbolType; + + @Column(name = "\"astType\"") + @Enumerated(EnumType.ORDINAL) + private AstType astType; + + @Column(name = "\"visibleInSourceCode\"") + private boolean visibleInSourceCode; + + + // Getters and setters + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getAstValue() { + return astValue; + } + + public void setAstValue(String astValue) { + this.astValue = astValue; + } + + public long getLocation_range_start_line() { + return location_range_start_line; + } + + public void setLocation_range_start_line(long location_range_start_line) { + this.location_range_start_line = location_range_start_line; + } + + public long getLocation_range_start_column() { + return location_range_start_column; + } + + public void setLocation_range_start_column(long location_range_start_column) { + this.location_range_start_column = location_range_start_column; + } + + public long getLocation_range_end_line() { + return location_range_end_line; + } + + public void setLocation_range_end_line(long location_range_end_line) { + this.location_range_end_line = location_range_end_line; + } + + public long getLocation_range_end_column() { + return location_range_end_column; + } + + public void setLocation_range_end_column(long location_range_end_column) { + this.location_range_end_column = location_range_end_column; + } + + public long getLocation_file() { + return location_file; + } + + public void setLocation_file(long location_file) { + this.location_file = location_file; + } + + public long getEntityHash() { + return entityHash; + } + + public void setEntityHash(long mangledNameHash) { + this.entityHash = mangledNameHash; + } + + public long getDefEntityHash() { + return defEntityHash; + } + + public void setDefEntityHash(long defEntityHash) { + this.defEntityHash = defEntityHash; + } + + public SymbolType getSymbolType() { + return symbolType; + } + + public void setSymbolType(SymbolType symbolType) { + this.symbolType = symbolType; + } + + public AstType getAstType() { + return astType; + } + + public void setAstType(AstType astType) { + this.astType = astType; + } + + public boolean isVisibleInSourceCode() { + return visibleInSourceCode; + } + + public void setVisibleInSourceCode(boolean visibleInSourceCode) { + this.visibleInSourceCode = visibleInSourceCode; + } + + public boolean isRangeSmaller(JavaAstNode other) { + long other_start_line = other.getLocation_range_start_line(); + long other_end_line = other.getLocation_range_end_line(); + long other_start_column = other.getLocation_range_start_column(); + long other_end_column = other.getLocation_range_end_column(); + + if (location_range_start_line == other_start_line) { + if (location_range_end_line == other_end_line) { + return location_range_end_column - location_range_start_column < + other_end_column - other_start_column; + } + return location_range_end_line < other_end_line; + } else if (location_range_end_line - location_range_start_line == + other_end_line - other_start_line) { + return location_range_end_column - location_range_start_column < + other_end_column - other_start_column; + } + return location_range_end_line - location_range_start_line < + other_end_line - other_start_line; + } +} diff --git a/plugins/java/model/JavaConstructor.java b/plugins/java/model/JavaConstructor.java new file mode 100644 index 000000000..898ea92b5 --- /dev/null +++ b/plugins/java/model/JavaConstructor.java @@ -0,0 +1,54 @@ +package model; + +import javax.persistence.*; +import java.util.HashSet; +import java.util.Set; + +@Entity +@Table(name = "\"JavaConstructor\"") +public class JavaConstructor extends JavaEntity { + @ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) + @JoinTable( + name = "\"JavaConstructor_parameters\"", + joinColumns = @JoinColumn(name = "object_id"), + inverseJoinColumns = @JoinColumn(name = "value") + ) + Set javaConVarParams = new HashSet<>(); + + @ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) + @JoinTable( + name = "\"JavaConstructor_locals\"", + joinColumns = @JoinColumn(name = "object_id"), + inverseJoinColumns = @JoinColumn(name = "value") + ) + Set javaConVarLocals = new HashSet<>(); + + public void addJavaConVarParam(JavaVariable javaVariable) { + javaConVarParams.add(javaVariable); + javaVariable.getJavaConstructorParams().add(this); + } + + public void addJavaConVarLocal(JavaVariable javaVariable) { + javaConVarLocals.add(javaVariable); + javaVariable.getJavaConstructorLocals().add(this); + } + + + // Getters and setters + + public Set getJavaConVarParams() { + return javaConVarParams; + } + + public void setJavaConVarParams(Set javaConVarParams) { + this.javaConVarParams = javaConVarParams; + } + + public Set getJavaConVarLocals() { + return javaConVarLocals; + } + + public void setJavaConVarLocals(Set javaConVarLocals) { + this.javaConVarLocals = javaConVarLocals; + } +} diff --git a/plugins/java/model/JavaDocComment.java b/plugins/java/model/JavaDocComment.java new file mode 100644 index 000000000..476b1299c --- /dev/null +++ b/plugins/java/model/JavaDocComment.java @@ -0,0 +1,57 @@ +package model; + +import javax.persistence.*; + +@Entity +@Table(name = "\"JavaDocComment\"") +public class JavaDocComment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private int id; + + @Column(name = "\"contentHash\"") + private long contentHash; + + @Lob + @Column(name = "\"content\"") + private String content; + + @Column(name = "\"entityHash\"") + private long entityHash; + + // Getters and setters + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public long getContentHash() { + return contentHash; + } + + public void setContentHash(long contentHash) { + this.contentHash = contentHash; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public long getEntityHash() { + return entityHash; + } + + public void setEntityHash(long entityHash) { + this.entityHash = entityHash; + } + +} diff --git a/plugins/java/model/JavaEntity.java b/plugins/java/model/JavaEntity.java new file mode 100644 index 000000000..859dae359 --- /dev/null +++ b/plugins/java/model/JavaEntity.java @@ -0,0 +1,74 @@ +package model; + +import javax.persistence.*; + +import static javax.persistence.InheritanceType.JOINED; + +@Entity +@Table(name = "\"JavaEntity\"") +@DiscriminatorColumn( + name = "typeid", + discriminatorType = DiscriminatorType.STRING +) +@Inheritance(strategy = JOINED) +public abstract class JavaEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "\"astNodeId\"") + private long astNodeId; + + @Column(name = "\"entityHash\"") + private long entityHash; + + @Column(name = "name") + private String name; + + @Column(name = "\"qualifiedName\"") + private String qualifiedName; + + + // Getters and setters + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public long getAstNodeId() { + return astNodeId; + } + + public void setAstNodeId(long astNodeId) { + this.astNodeId = astNodeId; + } + + public long getEntityHash() { + return entityHash; + } + + public void setEntityHash(long entityHash) { + this.entityHash = entityHash; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getQualifiedName() { + return qualifiedName; + } + + public void setQualifiedName(String qualifiedName) { + this.qualifiedName = qualifiedName; + } +} diff --git a/plugins/java/model/JavaEnum.java b/plugins/java/model/JavaEnum.java new file mode 100644 index 000000000..86b50aec8 --- /dev/null +++ b/plugins/java/model/JavaEnum.java @@ -0,0 +1,33 @@ +package model; + +import javax.persistence.*; +import java.util.HashSet; +import java.util.Set; + +@Entity +@Table(name = "\"JavaEnum\"") +public class JavaEnum extends JavaEntity { + @ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) + @JoinTable( + name = "\"JavaEnum_enumConstants\"", + joinColumns = @JoinColumn(name = "object_id"), + inverseJoinColumns = @JoinColumn(name = "value") + ) + Set javaEnumConstants = new HashSet<>(); + + public void addJavaEnumConstant(JavaEnumConstant enumConstant) { + javaEnumConstants.add(enumConstant); + enumConstant.getJavaEnums().add(this); + } + + + // Getters and setters + + public Set getJavaEnumConstants() { + return javaEnumConstants; + } + + public void setJavaEnumConstants(Set javaEnumConstants) { + this.javaEnumConstants = javaEnumConstants; + } +} diff --git a/plugins/java/model/JavaEnumConstant.java b/plugins/java/model/JavaEnumConstant.java new file mode 100644 index 000000000..35e2e7810 --- /dev/null +++ b/plugins/java/model/JavaEnumConstant.java @@ -0,0 +1,34 @@ +package model; + +import javax.persistence.*; +import java.util.HashSet; +import java.util.Set; + +@Entity +@Table(name = "\"JavaEnumConstant\"") +public class JavaEnumConstant extends JavaEntity { + @Column(name = "value") + private int value; + + @ManyToMany(mappedBy = "javaEnumConstants") + Set javaEnums = new HashSet<>(); + + + // Getters and setters + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } + + public Set getJavaEnums() { + return javaEnums; + } + + public void setJavaEnums(Set javaEnums) { + this.javaEnums = javaEnums; + } +} diff --git a/plugins/java/model/JavaImport.java b/plugins/java/model/JavaImport.java new file mode 100644 index 000000000..e86ba54b8 --- /dev/null +++ b/plugins/java/model/JavaImport.java @@ -0,0 +1,55 @@ +package model; + +import javax.persistence.*; + +@Entity +@Table(name = "\"JavaImport\"") +public class JavaImport { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private int id; + + @Column(name = "importer") + private long importer; + + @Column(name = "imported") + private long imported; + + @Column(name = "\"importedSymbol\"") + private String importedSymbol; + + // Getters and setters + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public long getImporter() { + return importer; + } + + public void setImporter(long importer) { + this.importer = importer; + } + + public long getImported() { + return imported; + } + + public void setImported(long imported) { + this.imported = imported; + } + + public String getImportedSymbol() { + return importedSymbol; + } + + public void setImportedSymbol(String importedSymbol) { + this.importedSymbol = importedSymbol; + } +} diff --git a/plugins/java/model/JavaInheritance.java b/plugins/java/model/JavaInheritance.java new file mode 100644 index 000000000..5c1307a78 --- /dev/null +++ b/plugins/java/model/JavaInheritance.java @@ -0,0 +1,44 @@ +package model; + +import javax.persistence.*; + +@Entity +@Table(name = "\"JavaInheritance\"") +public class JavaInheritance { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "base") + private long base; + + @Column(name = "derived") + private long derived; + + // Getters and setters + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public long getDerived() { + return derived; + } + + public void setDerived(long derived) { + this.derived = derived; + } + + public long getBase() { + return base; + } + + public void setBase(long base) { + this.base = base; + } +} diff --git a/plugins/java/model/JavaInitializer.java b/plugins/java/model/JavaInitializer.java new file mode 100644 index 000000000..a936875b1 --- /dev/null +++ b/plugins/java/model/JavaInitializer.java @@ -0,0 +1,56 @@ +package model; + +import model.enums.InitializerKind; + +import javax.persistence.*; +import java.util.HashSet; +import java.util.Set; + +@Entity +@Table(name = "\"JavaInitializer\"") +public class JavaInitializer extends JavaEntity { + @Column(name = "\"kind\"") + private InitializerKind kind; + + @Column(name = "\"typeHash\"") + private long typeHash; + + @ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) + @JoinTable( + name = "\"JavaInitializer_locals\"", + joinColumns = @JoinColumn(name = "object_id"), + inverseJoinColumns = @JoinColumn(name = "value") + ) + Set javaInitVarLocals = new HashSet<>(); + + public void addJavaInitVarLocal(JavaVariable javaVariable) { + javaInitVarLocals.add(javaVariable); + javaVariable.getJavaInitializerLocals().add(this); + } + + // Getters and setters + + public InitializerKind getKind() { + return kind; + } + + public void setKind(InitializerKind kind) { + this.kind = kind; + } + + public long getTypeHash() { + return typeHash; + } + + public void setTypeHash(long typeHash) { + this.typeHash = typeHash; + } + + public Set getJavaInitVarLocals() { + return javaInitVarLocals; + } + + public void setJavaInitVarLocals(Set javaInitLocals) { + this.javaInitVarLocals = javaInitLocals; + } +} diff --git a/plugins/java/model/JavaMemberType.java b/plugins/java/model/JavaMemberType.java new file mode 100644 index 000000000..476e5f203 --- /dev/null +++ b/plugins/java/model/JavaMemberType.java @@ -0,0 +1,82 @@ +package model; + +import model.enums.MemberTypeKind; +import model.enums.Visibility; + +import javax.persistence.*; + +@Entity +@Table(name = "\"JavaMemberType\"") +public class JavaMemberType { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private int id; + + @Column(name = "\"typeHash\"") + private long typeHash; + + @OneToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) + @JoinColumn(name = "\"memberAstNode\"") + private JavaAstNode memberAstNode; + + @Column(name = "\"memberTypeHash\"") + private long memberTypeHash; + + @Column(name = "kind") + private MemberTypeKind kind; + + @Column(name = "visibility") + private Visibility visibility; + + + // Getters and setters + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public long getTypeHash() { + return typeHash; + } + + public void setTypeHash(long typeHash) { + this.typeHash = typeHash; + } + + public JavaAstNode getMemberAstNode() { + return memberAstNode; + } + + public void setMemberAstNode(JavaAstNode memberAstNode) { + this.memberAstNode = memberAstNode; + } + + public long getMemberTypeHash() { + return memberTypeHash; + } + + public void setMemberTypeHash(long memberTypeHash) { + this.memberTypeHash = memberTypeHash; + } + + public MemberTypeKind getKind() { + return kind; + } + + public void setKind(MemberTypeKind kind) { + this.kind = kind; + } + + public Visibility getVisibility() { + return visibility; + } + + public void setVisibility(Visibility visibility) { + this.visibility = visibility; + } +} diff --git a/plugins/java/model/JavaMethod.java b/plugins/java/model/JavaMethod.java new file mode 100644 index 000000000..6eff60410 --- /dev/null +++ b/plugins/java/model/JavaMethod.java @@ -0,0 +1,54 @@ +package model; + +import javax.persistence.*; +import java.util.HashSet; +import java.util.Set; + +@Entity +@Table(name = "\"JavaMethod\"") +public class JavaMethod extends JavaTypedEntity { + @ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) + @JoinTable( + name = "\"JavaMethod_parameters\"", + joinColumns = @JoinColumn(name = "object_id"), + inverseJoinColumns = @JoinColumn(name = "value") + ) + Set javaMetVarParams = new HashSet<>(); + + @ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) + @JoinTable( + name = "\"JavaMethod_locals\"", + joinColumns = @JoinColumn(name = "object_id"), + inverseJoinColumns = @JoinColumn(name = "value") + ) + Set javaMetVarLocals = new HashSet<>(); + + public void addJavaMetVarParam(JavaVariable javaVariable) { + javaMetVarParams.add(javaVariable); + javaVariable.getJavaMethodParams().add(this); + } + + public void addJavaMetVarLocal(JavaVariable javaVariable) { + javaMetVarLocals.add(javaVariable); + javaVariable.getJavaMethodLocals().add(this); + } + + + // Getters and setters + + public Set getJavaMetVarParams() { + return javaMetVarParams; + } + + public void setJavaMetVarParams(Set javaMetVarParams) { + this.javaMetVarParams = javaMetVarParams; + } + + public Set getJavaMetVarLocals() { + return javaMetVarLocals; + } + + public void setJavaMetVarLocals(Set javaMetVarLocals) { + this.javaMetVarLocals = javaMetVarLocals; + } +} diff --git a/plugins/java/model/JavaRecord.java b/plugins/java/model/JavaRecord.java new file mode 100644 index 000000000..be1bbbe43 --- /dev/null +++ b/plugins/java/model/JavaRecord.java @@ -0,0 +1,44 @@ +package model; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +@Entity +@Table(name = "\"JavaRecord\"") +public class JavaRecord extends JavaEntity { + @Column(name = "\"isAbstract\"") + private boolean isAbstract; + + @Column(name = "\"isFinal\"") + private boolean isFinal; + + @Column(name = "\"isStatic\"") + private boolean isStatic; + + // Getters and setters + + public boolean isAbstract() { + return isAbstract; + } + + public void setAbstract(boolean anAbstract) { + isAbstract = anAbstract; + } + + public boolean isFinal() { + return isFinal; + } + + public void setFinal(boolean aFinal) { + isFinal = aFinal; + } + + public boolean isStatic() { + return isStatic; + } + + public void setStatic(boolean aStatic) { + isStatic = aStatic; + } +} diff --git a/plugins/java/model/JavaRelation.java b/plugins/java/model/JavaRelation.java new file mode 100644 index 000000000..1095d0c87 --- /dev/null +++ b/plugins/java/model/JavaRelation.java @@ -0,0 +1,58 @@ +package model; + +import model.enums.RelationKind; + +import javax.persistence.*; + +@Entity +@Table(name = "\"JavaRelation\"") +public class JavaRelation { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private int id; + + @Column(name = "lhs") + private long lhs; + + @Column(name = "rhs") + private long rhs; + + @Column(name = "\"kind\"") + @Enumerated(EnumType.ORDINAL) + private RelationKind kind; + + // Getters and setters + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public long getLhs() { + return lhs; + } + + public void setLhs(long lhs) { + this.lhs = lhs; + } + + public long getRhs() { + return rhs; + } + + public void setRhs(long rhs) { + this.rhs = rhs; + } + + public RelationKind getKind() { + return kind; + } + + public void setKind(RelationKind kind) { + this.kind = kind; + } +} diff --git a/plugins/java/model/JavaTypedEntity.java b/plugins/java/model/JavaTypedEntity.java new file mode 100644 index 000000000..91459ba86 --- /dev/null +++ b/plugins/java/model/JavaTypedEntity.java @@ -0,0 +1,57 @@ +package model; + +import javax.persistence.*; + +import static javax.persistence.InheritanceType.JOINED; + +@Entity +@Table(name = "\"JavaTypedEntity\"") +@Inheritance(strategy = JOINED) +public abstract class JavaTypedEntity extends JavaEntity { + @Column(name = "\"isFinal\"") + private boolean isFinal; + + @Column(name = "\"isStatic\"") + private boolean isStatic; + + @Column(name = "\"typeHash\"") + private long typeHash; + + @Column(name = "\"qualifiedType\"") + private String qualifiedType; + + + // Getters and setters + + public boolean isFinal() { + return isFinal; + } + + public void setFinal(boolean aFinal) { + isFinal = aFinal; + } + + public boolean isStatic() { + return isStatic; + } + + public void setStatic(boolean aStatic) { + isStatic = aStatic; + } + + public long getTypeHash() { + return typeHash; + } + + public void setTypeHash(long typeHash) { + this.typeHash = typeHash; + } + + public String getQualifiedType() { + return qualifiedType; + } + + public void setQualifiedType(String qualifiedType) { + this.qualifiedType = qualifiedType; + } +} diff --git a/plugins/java/model/JavaVariable.java b/plugins/java/model/JavaVariable.java new file mode 100644 index 000000000..249b97386 --- /dev/null +++ b/plugins/java/model/JavaVariable.java @@ -0,0 +1,75 @@ +package model; + +import javax.persistence.Entity; +import javax.persistence.ManyToMany; +import javax.persistence.Table; +import java.util.HashSet; +import java.util.Set; + +@Entity +@Table(name = "\"JavaVariable\"") +public class JavaVariable extends JavaTypedEntity { + @ManyToMany(mappedBy = "javaMetVarParams") + Set javaMethodParams = new HashSet<>(); + + @ManyToMany(mappedBy = "javaMetVarLocals") + Set javaMethodLocals = new HashSet<>(); + + @ManyToMany(mappedBy = "javaConVarParams") + Set javaConstructorParams = new HashSet<>(); + + @ManyToMany(mappedBy = "javaConVarLocals") + Set javaConstructorLocals = new HashSet<>(); + + @ManyToMany(mappedBy = "javaInitVarLocals") + Set javaInitializerLocals = new HashSet<>(); + + + // Getters and setters + + public Set getJavaMethodParams() { + return javaMethodParams; + } + + public void setJavaMethodParams(Set javaMethodParams) { + this.javaMethodParams = javaMethodParams; + } + + public Set getJavaMethodLocals() { + return javaMethodLocals; + } + + public void setJavaMethodLocals(Set javaMethodLocals) { + this.javaMethodLocals = javaMethodLocals; + } + + public Set getJavaConstructorParams() { + return javaConstructorParams; + } + + public void setJavaConstructorParams( + Set javaConstructorParams) + { + this.javaConstructorParams = javaConstructorParams; + } + + public Set getJavaConstructorLocals() { + return javaConstructorLocals; + } + + public void setJavaConstructorLocals( + Set javaConstructorLocals) + { + this.javaConstructorLocals = javaConstructorLocals; + } + + public Set getJavaInitializerLocals() { + return javaInitializerLocals; + } + + public void setJavaInitializerLocals( + Set javaInitializerLocals) + { + this.javaInitializerLocals = javaInitializerLocals; + } +} diff --git a/plugins/java/model/META-INF/MANIFEST.MF b/plugins/java/model/META-INF/MANIFEST.MF new file mode 100644 index 000000000..92db623e5 --- /dev/null +++ b/plugins/java/model/META-INF/MANIFEST.MF @@ -0,0 +1,7 @@ +Manifest-Version: 1.0 +Class-Path: . + eclipselink-2.7.8.jar + jakarta.persistence-api-2.2.3.jar + postgresql-42.2.22.jar + sqlite-jdbc-3.34.0.jar + javalogger.jar diff --git a/plugins/java/model/META-INF/persistence.xml b/plugins/java/model/META-INF/persistence.xml new file mode 100644 index 000000000..8ddaf88c3 --- /dev/null +++ b/plugins/java/model/META-INF/persistence.xml @@ -0,0 +1,35 @@ + + + + + org.eclipse.persistence.jpa.PersistenceProvider + + model.JavaAnnotation + model.JavaAstNode + model.JavaConstructor + model.JavaDocComment + model.JavaEntity + model.JavaEnum + model.JavaEnumConstant + model.JavaMethod + model.JavaImport + model.JavaInheritance + model.JavaInitializer + model.JavaMemberType + model.JavaRecord + model.JavaRelation + model.JavaTypedEntity + model.JavaVariable + + + + + + + \ No newline at end of file diff --git a/plugins/java/model/enums/AstType.java b/plugins/java/model/enums/AstType.java new file mode 100644 index 000000000..303d0b0bd --- /dev/null +++ b/plugins/java/model/enums/AstType.java @@ -0,0 +1,26 @@ +package model.enums; + +public enum AstType { + STATEMENT("Statement", "statement"), + DECLARATION("Declaration", "declaration"), + DEFINITION("Definition", "definition"), + USAGE("Usage", "usage"), + READ("Read", "read"), + WRITE("Write", "write"); + + private final String name; + private final String value; + + AstType(String name, String value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } +} diff --git a/plugins/java/model/enums/InitializerKind.java b/plugins/java/model/enums/InitializerKind.java new file mode 100644 index 000000000..32790c698 --- /dev/null +++ b/plugins/java/model/enums/InitializerKind.java @@ -0,0 +1,16 @@ +package model.enums; + +public enum InitializerKind { + INSTANCE("instance"), + STATIC("static"); + + private final String name; + + InitializerKind(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/plugins/java/model/enums/MemberTypeKind.java b/plugins/java/model/enums/MemberTypeKind.java new file mode 100644 index 000000000..3141b38c8 --- /dev/null +++ b/plugins/java/model/enums/MemberTypeKind.java @@ -0,0 +1,20 @@ +package model.enums; + +public enum MemberTypeKind { + TYPE("Type"), + CONSTRUCTOR("Constructor"), + FIELD("Field"), + METHOD("Method"), + ENUM("Enum"), + ENUM_CONSTANT("Enum constant"); + + private final String name; + + MemberTypeKind(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/plugins/java/model/enums/RelationKind.java b/plugins/java/model/enums/RelationKind.java new file mode 100644 index 000000000..7ceb76bc0 --- /dev/null +++ b/plugins/java/model/enums/RelationKind.java @@ -0,0 +1,6 @@ +package model.enums; + +public enum RelationKind { + OVERRIDE, + IMPLEMENT +} diff --git a/plugins/java/model/enums/SymbolType.java b/plugins/java/model/enums/SymbolType.java new file mode 100644 index 000000000..a82250ec3 --- /dev/null +++ b/plugins/java/model/enums/SymbolType.java @@ -0,0 +1,28 @@ +package model.enums; + +public enum SymbolType { + VARIABLE("Variable", "variable"), + CONSTRUCTOR("Constructor", "constructor"), + METHOD("Method", "method"), + ENUM("Enum", "enum"), + ENUM_CONSTANT("Enum constant", "enum-constant"), + TYPE("Type", "type"), + INITIALIZER("Initializer", "initializer"), + FILE("File", "file"); + + private final String name; + private final String value; + + SymbolType(String name, String value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } +} diff --git a/plugins/java/model/enums/Visibility.java b/plugins/java/model/enums/Visibility.java new file mode 100644 index 000000000..589c1dd6e --- /dev/null +++ b/plugins/java/model/enums/Visibility.java @@ -0,0 +1,24 @@ +package model.enums; + +public enum Visibility { + PRIVATE("private", 0), + PACKAGE_PRIVATE("package-private", 1), + PROTECTED("protected", 2), + PUBLIC("public", 3); + + private final String name; + private final int value; + + Visibility(String name, int value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public int getValue() { + return value; + } +} diff --git a/plugins/java/parser/CMakeLists.txt b/plugins/java/parser/CMakeLists.txt new file mode 100644 index 000000000..2b947403a --- /dev/null +++ b/plugins/java/parser/CMakeLists.txt @@ -0,0 +1,70 @@ +set(CMAKE_JAVA_INCLUDE_PATH + ${PROJECT_SOURCE_DIR}/lib/java/* + ${PLUGIN_DIR}/lib/java/* + ${CMAKE_CURRENT_BINARY_DIR}/gen-java/ + ${PROJECT_BINARY_DIR}/service/project/gen-java) + +include_directories( + include + ${CMAKE_CURRENT_BINARY_DIR}/gen-cpp + ${PROJECT_SOURCE_DIR}/model/include + ${PROJECT_SOURCE_DIR}/util/include + ${PROJECT_SOURCE_DIR}/parser/include + ${CMAKE_BINARY_DIR}/model/include + ${PROJECT_BINARY_DIR}/service/project/gen-cpp) + +include_directories(SYSTEM + ${THRIFT_LIBTHRIFT_INCLUDE_DIRS}) + +# Generate thrift files +add_custom_command( + OUTPUT + ${CMAKE_CURRENT_BINARY_DIR}/gen-cpp/javaparser_constants.cpp + ${CMAKE_CURRENT_BINARY_DIR}/gen-cpp/javaparser_types.cpp + ${CMAKE_CURRENT_BINARY_DIR}/gen-cpp/JavaParserService.cpp + ${CMAKE_CURRENT_BINARY_DIR}/gen-cpp + ${CMAKE_CURRENT_BINARY_DIR}/gen-java + COMMAND + ${THRIFT_EXECUTABLE} --gen cpp --gen java + -o ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/javaparser.thrift + DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/javaparser.thrift + COMMENT + "Generating Thrift for javaparser.thrift") + +# Create cpp static library from thrift files +add_library(javaparserthrift STATIC + ${CMAKE_CURRENT_BINARY_DIR}/gen-cpp/javaparser_constants.cpp + ${CMAKE_CURRENT_BINARY_DIR}/gen-cpp/javaparser_types.cpp + ${CMAKE_CURRENT_BINARY_DIR}/gen-cpp/JavaParserService.cpp) + +target_compile_options(javaparserthrift PUBLIC -fPIC) + +add_dependencies(javaparserthrift commonthrift projectthrift) + +# Create java library from thrift files +add_jar(javaparserthriftjava + ${CMAKE_CURRENT_BINARY_DIR}/gen-java/cc/parser/java/CompileCommand.java + ${CMAKE_CURRENT_BINARY_DIR}/gen-java/cc/parser/java/JavaParserService.java + OUTPUT_NAME javaparserthrift) + +add_dependencies(javaparserthriftjava javaparserthrift) + +# Java parser +add_subdirectory(srcjava) + +add_library(javaparser SHARED + src/javaparser.cpp) + +target_link_libraries(javaparser + model + javaparserthrift + commonthrift + projectthrift + ${THRIFT_LIBTHRIFT_LIBRARIES}) + +target_compile_options(javaparser PUBLIC -Wno-unknown-pragmas) + +install(TARGETS javaparser DESTINATION ${INSTALL_PARSER_DIR}) +install_jar(javaparserthriftjava "${INSTALL_JAVA_LIB_DIR}") diff --git a/plugins/java/parser/include/javaparser/javaparser.h b/plugins/java/parser/include/javaparser/javaparser.h new file mode 100644 index 000000000..35d753b40 --- /dev/null +++ b/plugins/java/parser/include/javaparser/javaparser.h @@ -0,0 +1,261 @@ +#ifndef CC_PARSER_JAVAPARSER_H +#define CC_PARSER_JAVAPARSER_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +namespace cc +{ +namespace parser +{ +namespace java +{ + +namespace core = cc::service::core; +namespace ba = boost::algorithm; +namespace fs = boost::filesystem; +namespace pr = boost::process; +namespace pt = boost::property_tree; +namespace chrono = std::chrono; +using TransportException = apache::thrift::transport::TTransportException; + +class TimeoutException : public std::runtime_error { +public: + using std::runtime_error::runtime_error; +}; + +class JavaParserServiceHandler : public JavaParserServiceIf { +public: + JavaParserServiceHandler() { + } + + void parseFile( + ParseResult& return_, + const CompileCommand& compileCommand_, long fileId_, + const std::string& fileCounterStr_) override + { + _service -> parseFile(return_, compileCommand_, fileId_, fileCounterStr_); + } + + void decompileClass( + std::string& return_, const std::string& path_) override { + _service -> decompileClass(return_, path_); + } + + /** + * Creates the client interface. + */ + void getClientInterface(int timeout_in_ms, int worker_num) + { + using Transport = apache::thrift::transport::TTransport; + using BufferedTransport = apache::thrift::transport::TBufferedTransport; + using Socket = apache::thrift::transport::TSocket; + using Protocol = apache::thrift::protocol::TBinaryProtocol; + + std::string host = "localhost"; + int port = 9090; + + std::shared_ptr + socket(new Socket(host, port)); + std::shared_ptr + transport(new BufferedTransport(socket)); + std::shared_ptr + protocol(new Protocol(transport)); + + // Redirect Thrift output into std::stringstream + apache::thrift::GlobalOutput.setOutputFunction( + [](const char* x) {thrift_ss << x;}); + + chrono::steady_clock::time_point begin = chrono::steady_clock::now(); + + while (!transport->isOpen()) { + try { + transport->open(); + + if (!server_started) { + LOG(info) << "[javaparser] Java server started!"; + server_started = true; + } + + LOG(info) << "[javaparser] Worker (" << + worker_num << ") connected to the Java server!"; + } catch (TransportException& ex) { + chrono::steady_clock::time_point current = chrono::steady_clock::now(); + float elapsed_time = + chrono::duration_cast(current - begin).count(); + + if (elapsed_time > timeout_in_ms) { + LOG(error) << "Connection timeout, could not reach Java server on" + << host << ":" << port; + apache::thrift::GlobalOutput.setOutputFunction( + apache::thrift::TOutput::errorTimeWrapper); + throw ex; + } + } + } + + apache::thrift::GlobalOutput.setOutputFunction( + apache::thrift::TOutput::errorTimeWrapper); + + _service.reset(new JavaParserServiceClient(protocol)); + } + + void reserve() { + reserved = true; + } + + void setFree() { + reserved = false; + } + + bool isFree() { + return !reserved; + } + + +private: + /** + * Service interface for IPC communication. + */ + std::unique_ptr _service; + + /** + * Handler's state + */ + bool reserved = false; + + /** + * Server's state. + */ + static bool server_started; + + /** + * Object to store Thrift messages during connecting to the Java server + */ + static std::stringstream thrift_ss; +}; + +class JavaParser : public AbstractParser { +public: + JavaParser(ParserContext& ctx_); + + virtual ~JavaParser(); + + virtual bool parse() override; + +private: + /** + * A single build command's cc::util::JobQueueThreadPool job. + */ + struct ParseJob + { + /** + * The build command itself. + * This is given to a free worker in JavaParser::_javaServiceHandlers. + */ + const CompileCommand command; + + /** + * The # of the build command in the compilation command database. + */ + std::size_t index; + + ParseJob(const CompileCommand command, std::size_t index) + : command(command), index(index) + {} + + ParseJob(const ParseJob&) = default; + }; + + /** + * A single Java class file's cc::util::JobQueueThreadPool job. + */ + struct DecompileJob + { + /** + * The path to the Java class file. + * This is given to a free worker in JavaParser::_javaServiceHandlers. + */ + const std::string path; + + DecompileJob(const std::string& path) : path(path) + {} + + DecompileJob(const DecompileJob&) = default; + }; + + fs::path _java_path; + fs::path _unzip_path; + pr::child _c; + std::function make_parse_pool; + int _numCompileCommands; + int _threadNum; + std::vector> _javaServiceHandlers; + + bool acceptCompileCommands(const std::string& path_); + + bool acceptJar(const std::string& path_); + + bool acceptClass(const std::string& path_); + + bool rejectInnerClass(const std::string& path_); + + void startAndConnectToJavaProcess(); + + void initializeWorkers(); + + std::shared_ptr& findFreeWorker(int timeout_in_ms_); + + CompileCommand getCompileCommandFromJson( + const pt::ptree::value_type& command_tree_); + + CompileCommand getCompileCommandForDecompiledFile( + const std::string& filePath_, const std::string& classpath_, + const std::string& sourcepath_); + + std::string getClasspathFromMetaInf( + const fs::path& root_, const fs::path& originalDir_); + + model::BuildActionPtr addBuildAction(const CompileCommand& compile_command_); + + void addCompileCommand( + const CmdArgs& cmd_args_, + model::BuildActionPtr buildAction_, + model::File::ParseStatus& parseStatus_); + + model::File::ParseStatus addBuildLogs( + const std::vector& buildLogs_, + const std::string& file_); + + bool parseCompileCommands(const std::string& path_); + + bool parseJar(const std::string& path_); + + std::vector decompileJar(const std::string& path_); +}; + +} // java +} // parser +} // cc + +#endif // CC_PARSER_JAVAPARSER_H diff --git a/plugins/java/parser/javaparser.thrift b/plugins/java/parser/javaparser.thrift new file mode 100644 index 000000000..9cf8d7dde --- /dev/null +++ b/plugins/java/parser/javaparser.thrift @@ -0,0 +1,47 @@ +include "../../../service/project/project.thrift" + +namespace cpp cc.parser.java +namespace java cc.parser.java + +struct CompileCommand +{ + 1: string directory, + 2: string command, + 3: string file +} + +struct CmdArgs +{ + 1: string directory, + 2: list classpath, + 3: list sourcepath, + 4: string filepath, + 5: string filename, + 6: string bytecodeDir, + 7: list bytecodesPaths +} + +struct ParseResult +{ + 1: CmdArgs cmdArgs, + 2: list buildLogs, + 3: bool errorDueParsing +} + +exception JavaBeforeParseException +{ + 1: string message +} + +exception ClassDecompileException +{ + 1: string message +} + +service JavaParserService +{ + ParseResult parseFile( + 1: CompileCommand compileCommand, 2: i64 fileId, 3: string fileCounterStr) + throws (1: JavaBeforeParseException jbe), + string decompileClass(1: string path) throws (1: ClassDecompileException cde) +} diff --git a/plugins/java/parser/src/javaparser.cpp b/plugins/java/parser/src/javaparser.cpp new file mode 100644 index 000000000..5435f7c2a --- /dev/null +++ b/plugins/java/parser/src/javaparser.cpp @@ -0,0 +1,573 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +namespace cc +{ +namespace parser +{ +namespace java +{ + +// Initialize static members +std::stringstream JavaParserServiceHandler::thrift_ss; +bool JavaParserServiceHandler::server_started = false; + +JavaParser::JavaParser(ParserContext& ctx_) : AbstractParser(ctx_) { + _java_path = pr::search_path("java"); + _unzip_path = pr::search_path("unzip"); + _threadNum = _ctx.options["jobs"].as(); + + //--- Create a thread pool to process commands ---// + + make_parse_pool =[this](ParseJob& job_) + { + const CompileCommand command = job_.command; + std::shared_ptr serviceHandler; + + try { + serviceHandler = findFreeWorker(15000); + } catch (TimeoutException& ex) { + LOG(error) << + "Operation timeout, could not find free " + "Java worker to process " << command.file << "!"; + return; + } + + std::string file_counter_str = + "(" + std::to_string(job_.index) + "/" + + std::to_string(_numCompileCommands) + ")"; + + LOG(info) << + file_counter_str << " " << "Parsing " << command.file; + + model::FilePtr filePtr = _ctx.srcMgr.getFile(command.file); + filePtr -> type = "JAVA"; + model::BuildActionPtr buildAction = addBuildAction(command); + model::File::ParseStatus parseStatus = + model::File::PSFullyParsed; + ParseResult parseResult; + + //--- Run Java parser ---// + + try { + serviceHandler->parseFile( + parseResult, command, filePtr->id, file_counter_str); + parseStatus = + addBuildLogs(parseResult.buildLogs, command.file); + + if (parseResult.errorDueParsing) { + LOG(warning) << + file_counter_str << " " << "Parsing " << + command.file << " had one or more errors during parsing"; + + if (parseStatus == model::File::ParseStatus::PSFullyParsed) { + parseStatus = model::File::ParseStatus::PSPartiallyParsed; + } + } + + addCompileCommand( + parseResult.cmdArgs, buildAction, parseStatus); + + } catch (JavaBeforeParseException& ex) { + LOG(warning) << + file_counter_str << " " << "Parsing " << + command.file << " has been failed before the start"; + LOG(warning) << ex.message; + } + + serviceHandler->setFree(); + }; + + for (int i = 0; i < _threadNum; ++i) { + _javaServiceHandlers.push_back( + std::make_shared(JavaParserServiceHandler()) + ); + } +} + +bool JavaParser::acceptCompileCommands(const std::string& path_) { + std::string ext = fs::extension(path_); + return ext == ".json"; +} + +bool JavaParser::acceptJar(const std::string& path_) { + std::string ext = fs::extension(path_); + return ext == ".jar"; +} + +bool JavaParser::acceptClass(const std::string& path_) { + std::string ext = fs::extension(path_); + return ext == ".class"; +} + +bool JavaParser::rejectInnerClass(const std::string& path_) { + std::string name = fs::basename(path_); + return name.find('$') == std::string::npos; +} + +void JavaParser::startAndConnectToJavaProcess() { + std::vector _java_args{ + "-DrawDbContext=" + _ctx.options["database"].as(), + "-DthreadNum=" + std::to_string(_threadNum), + "-jar", + "../lib/java/javaparser.jar" + }; + + _c = pr::child(_java_path, _java_args, pr::std_out > stdout); + + initializeWorkers(); +} + +void JavaParser::initializeWorkers() { + int worker_num = 0; + for (auto &handler : _javaServiceHandlers) { + try { + handler->getClientInterface(25000, ++worker_num); + } catch (TransportException& ex) { + LOG(error) << + "[javaparser] Failed to start worker (" << worker_num << ")!"; + throw ex; + } + } +} + +std::shared_ptr& + JavaParser::findFreeWorker(int timeout_in_ms_) +{ + chrono::steady_clock::time_point begin = chrono::steady_clock::now(); + chrono::steady_clock::time_point current; + float elapsed_time = 0; + + while (elapsed_time < timeout_in_ms_) { + for (auto &handler: _javaServiceHandlers) { + if (handler->isFree()) { + handler->reserve(); + return handler; + } + } + + current = chrono::steady_clock::now(); + elapsed_time = + chrono::duration_cast(current - begin).count(); + } + + throw TimeoutException("Could not find free Java Worker"); +} + +CompileCommand JavaParser::getCompileCommandFromJson( + const pt::ptree::value_type& command_tree_) +{ + CompileCommand compile_command; + + compile_command.directory = + command_tree_.second.get("directory"); + compile_command.command = + command_tree_.second.get("command"); + compile_command.file = + command_tree_.second.get("file"); + + return compile_command; +} + +CompileCommand JavaParser::getCompileCommandForDecompiledFile( + const std::string& filePath_, const std::string& classpath_, + const std::string& sourcepath_) +{ + CompileCommand compile_command; + std::ostringstream javacCommand; + javacCommand << "javac" << " -sourcepath " << sourcepath_ << + " -classpath " << classpath_ << " " << filePath_; + + compile_command.directory = fs::path(filePath_).parent_path().string(); + compile_command.command = javacCommand.str(); + compile_command.file = filePath_; + + return compile_command; +} + +std::string JavaParser::getClasspathFromMetaInf( + const fs::path& root_, const fs::path& originalDir_) +{ + std::ostringstream rawClasspath; + std::ostringstream classpath; + std::vector classpathVec; + fs::path manifestPath = root_ / "META-INF" / "MANIFEST.MF"; + + classpath << root_.string(); + + if (!fs::exists(manifestPath)) { + return classpath.str(); + } + + fs::ifstream fileHandler(manifestPath); + std::string line; + bool cpFound = false; + + while ( + getline(fileHandler, line) && + !(cpFound && ba::contains(line, ":"))) + { + if ( + (!cpFound && ba::starts_with(line, "Class-Path:")) || + (cpFound && !ba::contains(line, ":"))) + { + line = line.substr(0, line.size() -1); + if (!cpFound) { + ba::replace_first(line, "Class-Path:", ""); + cpFound = true; + } + + rawClasspath << line.substr(1); + } + } + + ba::split_regex( + classpathVec, rawClasspath.str(), boost::regex("([ ]+)")); + + for (std::string& cp : classpathVec) { + if (!cp.empty()) { + classpath << ":" << (originalDir_ / cp).string(); + } + } + + return classpath.str(); +} + +model::BuildActionPtr JavaParser::addBuildAction( + const CompileCommand& compile_command_) +{ + util::OdbTransaction transaction(_ctx.db); + model::BuildActionPtr buildAction(new model::BuildAction); + std::string extension = fs::extension(compile_command_.file); + + buildAction -> command = compile_command_.command; + buildAction -> type + = extension == ".class" + ? model::BuildAction::Link + : model::BuildAction::Compile; + + transaction([&, this] { _ctx.db->persist(buildAction); }); + + return buildAction; +} + +void JavaParser::addCompileCommand( + const CmdArgs& cmd_args_, + model::BuildActionPtr buildAction_, + model::File::ParseStatus& parseStatus_) +{ + util::OdbTransaction transaction(_ctx.db); + std::vector targets; + + model::BuildSource buildSource; + buildSource.file = _ctx.srcMgr.getFile(cmd_args_.filepath); + buildSource.file->parseStatus = parseStatus_; + _ctx.srcMgr.updateFile(*buildSource.file); + buildSource.action = buildAction_; + + for (const std::string& target : cmd_args_.bytecodesPaths) { + model::BuildTarget buildTarget; + buildTarget.file = _ctx.srcMgr.getFile(target); + buildTarget.action = buildAction_; + if (buildTarget.file->type != model::File::BINARY_TYPE) { + buildTarget.file->type = model::File::BINARY_TYPE; + _ctx.srcMgr.updateFile(*buildTarget.file); + } + + targets.push_back(std::move(buildTarget)); + } + + _ctx.srcMgr.persistFiles(); + + transaction([&, this] { + _ctx.db->persist(buildSource); + for (model::BuildTarget buildTarget : targets) + _ctx.db->persist(buildTarget); + }); +} + +model::File::ParseStatus JavaParser::addBuildLogs( + const std::vector& buildLogs_, const std::string& file_) +{ + util::OdbTransaction transaction(_ctx.db); + std::vector logs; + model::File::ParseStatus parseStatus = + model::File::ParseStatus::PSFullyParsed; + + for (const core::BuildLog& log : buildLogs_) { + model::BuildLog buildLog; + + buildLog.location.file = _ctx.srcMgr.getFile(file_); + buildLog.log.message = log.message; + buildLog.location.range.start.line = log.range.startpos.line; + buildLog.location.range.start.column = log.range.startpos.column; + buildLog.location.range.end.line = log.range.startpos.line; + buildLog.location.range.end.column = log.range.endpos.column; + + switch (log.messageType) { + case core::MessageType::FatalError: + buildLog.log.type = model::BuildLogMessage::FatalError; + + if (parseStatus > model::File::ParseStatus::PSNone) { + parseStatus = model::File::ParseStatus::PSNone; + } + break; + case core::MessageType::Error: + buildLog.log.type = model::BuildLogMessage::Error; + + if (parseStatus > model::File::ParseStatus::PSPartiallyParsed) { + parseStatus = model::File::ParseStatus::PSPartiallyParsed; + } + break; + case core::MessageType::Warning: + buildLog.log.type = model::BuildLogMessage::Warning; + break; + case core::MessageType::Note: + buildLog.log.type = model::BuildLogMessage::Note; + break; + default: // Just to suppress warning of uncovered enum constants + break; + } + + logs.push_back(std::move(buildLog)); + } + + transaction([&, this] { + for (model::BuildLog buildLog : logs) + _ctx.db->persist(buildLog); + }); + + return parseStatus; +} + +bool JavaParser::parse() { + bool success = true; + + for (const std::string& path + : _ctx.options["input"].as>()) { + if (acceptCompileCommands(path)) { + bool parseSuccess = parseCompileCommands(path); + + if (success) { + success = parseSuccess; + } + } else if (acceptJar(path)) { + bool parseSuccess = parseJar(path); + + if (success) { + success = parseSuccess; + } + } + } + + return success; +} + +bool JavaParser::parseCompileCommands(const std::string& path_) { + pt::ptree _pt; + pt::read_json(path_, _pt); + pt::ptree _pt_filtered; + pr::ipstream is; + std::size_t file_index = 0; + + // Filter compile commands tree to contain only Java-related files + std::copy_if ( + _pt.begin(), + _pt.end(), + std::back_inserter(_pt_filtered), + [](pt::ptree::value_type& command_tree_) + { + auto ext = + fs::extension(command_tree_.second.get("file")); + return ext == ".java"; + }); + + _numCompileCommands = _pt_filtered.size(); + + //--- Start Java Thrift server and connect to is via workers ---// + + //--- Process Java files ---// + + LOG(info) << "JavaParser parse path: " << path_; + + if (_numCompileCommands == 0) { + LOG(info) << "Java-related compile commands not found in " << path_; + return true; + } + + if (!_c.running()) { + startAndConnectToJavaProcess(); + } + + std::unique_ptr> parsePool = + util::make_thread_pool(_threadNum, make_parse_pool); + + for (pt::ptree::value_type &command_tree_: _pt_filtered) { + CompileCommand command = + getCompileCommandFromJson(command_tree_); + + ParseJob job(command, ++file_index); + + //--- Push the job ---// + + parsePool->enqueue(job); + } + + // Block execution until every job is finished. + parsePool->wait(); + + return true; +} + +bool JavaParser::parseJar(const std::string& path_) { + //--- Start Java Thrift server and connect to is via workers ---// + + if (!_c.running()) { + startAndConnectToJavaProcess(); + } + + std::vector commands = decompileJar(path_); + _numCompileCommands = commands.size(); + std::size_t file_index = 0; + + std::unique_ptr> parsePool = + util::make_thread_pool(_threadNum, make_parse_pool); + + for (const CompileCommand& command : commands) { + ParseJob job(command, ++file_index); + + //--- Push the job ---// + + parsePool->enqueue(job); + } + + // Block execution until every job is finished. + parsePool->wait(); + + return true; +} + +std::vector JavaParser::decompileJar(const std::string& path_) { + fs::path jarPath(path_); + fs::path workspace(_ctx.options["workspace"].as()); + fs::path project(_ctx.options["name"].as()); + std::string decompiled = fs::basename(jarPath); + fs::path decompiled_root = workspace / project / decompiled; + std::vector commands; + + pr::system( + _unzip_path, "-o", jarPath, "-d", decompiled_root + ); + + std::string classpath = + getClasspathFromMetaInf( + decompiled_root, jarPath.parent_path() + ); + + //--- Create a thread pool to decompile bytecodes ---// + + std::unique_ptr< + util::JobQueueThreadPool> decompilePool = + util::make_thread_pool( + _threadNum, + [this, &commands, &classpath, &decompiled_root] + (DecompileJob& job_) + { + const std::string bytecodePath = job_.path; + std::shared_ptr serviceHandler; + + try { + serviceHandler = findFreeWorker(15000); + } catch (TimeoutException& ex) { + LOG(error) << + "Operation timeout, could not find free " + "Java worker to decompile " << bytecodePath << "!"; + return; + } + + LOG(info) << "Decompiling " << bytecodePath; + + std::string javaFilePath; + + try { + serviceHandler -> decompileClass(javaFilePath, bytecodePath); + commands.push_back( + getCompileCommandForDecompiledFile( + javaFilePath, classpath, decompiled_root.string()) + ); + } catch (ClassDecompileException& ex) { + LOG(warning) << "Decompiling " << bytecodePath << " has been failed"; + LOG(warning) << ex.message; + } + + serviceHandler->setFree(); + }); + + //-- Decompile bytecodes --// + + for ( + fs::directory_entry& entry : + fs::recursive_directory_iterator(decompiled_root)) + { + const std::string& bytecodePath = entry.path().string(); + + if (acceptClass(bytecodePath) && rejectInnerClass(bytecodePath)) { + DecompileJob job(bytecodePath); + + //--- Push the job ---// + + decompilePool->enqueue(job); + } + } + + // Block execution until every job is finished. + decompilePool->wait(); + + return commands; +} + +JavaParser::~JavaParser() {} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +extern "C" +{ +boost::program_options::options_description getOptions() { + boost::program_options::options_description description("Java Plugin"); + + /* + description.add_options() + ("java-arg", po::value()->default_value("Java arg"), + "This argument will be used by the Java parser."); + */ + + return description; +} + +std::shared_ptr make(ParserContext& ctx_) { + return std::make_shared(ctx_); +} +} +#pragma clang diagnostic pop + +} // java +} // parser +} // cc diff --git a/plugins/java/parser/srcjava/ArgParser.java b/plugins/java/parser/srcjava/ArgParser.java new file mode 100644 index 000000000..91785b168 --- /dev/null +++ b/plugins/java/parser/srcjava/ArgParser.java @@ -0,0 +1,201 @@ +package parser.srcjava; + +import cc.parser.java.CompileCommand; +import parser.srcjava.enums.ValidCommands; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static logger.Logger.LOGGER; + +public class ArgParser { + private static final String JREPathStr = System.getProperty("java.home"); + private final String fileCounterStr; + private final String directory; + private final ArrayList classpath; + private final ArrayList sourcepath; + private final String filepath; + private final String filename; + private String encoding; + private String bytecodePath; + private List bytecodesPaths; + + public ArgParser(CompileCommand compileCommand, String fileCounterStr) { + this.fileCounterStr = fileCounterStr; + this.directory = compileCommand.getDirectory(); + this.classpath = new ArrayList<>(); + this.sourcepath = new ArrayList<>(); + this.filepath = compileCommand.getFile(); + this.filename = Paths.get(this.filepath).getFileName().toString(); + this.encoding = "utf8"; + this.bytecodePath = + this.filepath.substring(0, this.filepath.lastIndexOf('/')); + + parseCommand(compileCommand.getCommand()); + } + + private void parseCommand(String fullCommand) { + this.classpath.add(JREPathStr); + List rawArgs = Arrays.asList(fullCommand.split(" ")); + Iterator argIt = rawArgs.iterator(); + String command = argIt.next(); + + if (Arrays.stream(ValidCommands.values()) + .map(ValidCommands::getName) + .noneMatch(command::equals)) { + throw new IllegalArgumentException("Unknown command."); + } + + if (command.equals(ValidCommands.JAVAC.getName())) { + parseJavacCommand(argIt); + } + /* + UNDER CONSTRUCTION + else if (command.equals(ValidCommands.MVN.getName())) { + } etc... + */ + } + + private void parseJavacCommand( + Iterator argIt) + throws IllegalArgumentException + { + int filePathCounter = 0; + boolean bytecodeDirFound = false; + + while (argIt.hasNext()) { + String nextArg = argIt.next(); + if (nextArg.startsWith("-")) { + if (!argIt.hasNext()) { + throw new IllegalArgumentException("Command has invalid syntax."); + } + String flagName = nextArg.substring(1); + if (flagName.equals("cp") || flagName.equals("classpath")) { + classpath.addAll(Arrays.asList(argIt.next().split(":"))); + } else if (flagName.equals("sourcepath")) { + sourcepath.addAll(Arrays.asList(argIt.next().split(":"))); + } else if (flagName.equals("d")) { + if (!bytecodeDirFound) { + bytecodeDirFound = true; + bytecodePath = argIt.next(); + } else { + throw new IllegalArgumentException("Command has invalid syntax"); + } + } else if (flagName.equals("encoding")) { + encoding = argIt.next(); + } else { + argIt.next(); + } + } else if (nextArg.equals(filepath)) { + filePathCounter += 1; + } else { + throw new IllegalArgumentException("Command has invalid syntax"); + } + } + if (filePathCounter > 1) { + throw new IllegalArgumentException("Command has invalid syntax"); + } + + if (bytecodeDirFound && sourcepath.size() > 0) { + for (String sp : sourcepath) { + if (filepath.startsWith(sp)) { + Path localBytecodePath = + Paths.get(bytecodePath, filepath.substring(sp.length())); + setBytecodesPaths(localBytecodePath); + } + break; + } + } else { + setBytecodesPaths(Paths.get(bytecodePath)); + } + } + + private void setBytecodesPaths(Path localBytecodePath) { + try (Stream walk = Files.walk(localBytecodePath, 1)) { + bytecodesPaths = walk + .filter(this::bytecodeBelongsToFile) + .map(Path::toString) + .collect(Collectors.toList()); + } catch (IOException e) { + LOGGER.log( + Level.SEVERE, + fileCounterStr + + " Class files could not be found where they should be" + + " according to the compile command" + ); + } + } + + private boolean bytecodeBelongsToFile(Path path) { + String cleanFileName = filename.substring(0, filename.lastIndexOf('.')); + String actualFilenameStr = path.getFileName().toString(); + + return Files.isRegularFile(path) && + (actualFilenameStr.startsWith(cleanFileName) || + actualFilenameStr.startsWith(cleanFileName + '$')) && + actualFilenameStr.endsWith(".class"); + } + + public static String getJREPathStr() { + return JREPathStr; + } + + public String getDirectory() { + return directory; + } + + public ArrayList getClasspath() { + return classpath; + } + + public ArrayList getSourcepath() { + return sourcepath; + } + + public String getFilepath() { + return filepath; + } + + public String getFilename() { + return filename; + } + + public String getEncoding() { + return encoding; + } + + public String getBytecodePath() { + return bytecodePath; + } + + public List getBytecodesPaths() { + return bytecodesPaths; + } + + public String[] getCommandLineArguments() { + ArrayList commandLineArgs = new ArrayList<>(); + + if (!classpath.isEmpty()) { + commandLineArgs.add("--classpath"); + commandLineArgs.addAll(classpath); + } + + if (!sourcepath.isEmpty()) { + commandLineArgs.add("--sourcepath"); + commandLineArgs.addAll(sourcepath); + } + + commandLineArgs.add(filepath); + + return (String[]) commandLineArgs.toArray(); + } +} diff --git a/plugins/java/parser/srcjava/AstVisitor.java b/plugins/java/parser/srcjava/AstVisitor.java new file mode 100644 index 000000000..6d001218a --- /dev/null +++ b/plugins/java/parser/srcjava/AstVisitor.java @@ -0,0 +1,335 @@ +package parser.srcjava; + +import org.eclipse.jdt.core.dom.*; + +import javax.persistence.EntityManager; + +import java.util.logging.Level; + +import static logger.Logger.LOGGER; + +public class AstVisitor extends ASTVisitor { + private final String fileCounterStr; + private final CompilationUnit cu; + private final PersistManager pm; + private boolean errorDueParsing; + + public AstVisitor( + CompilationUnit cu, EntityManager em, long fileId, String fileCounterStr) + { + this.fileCounterStr = fileCounterStr; + this.cu = cu; + this.pm = new PersistManager(cu, em, fileId); + this.errorDueParsing = false; + } + + @Override + public boolean visit(CatchClause node) { + try { + pm.persistLocalVarDeclaration(node.getException()); + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(ClassInstanceCreation node) { + try { + pm.persistConstructorUsage(node, node.resolveConstructorBinding()); + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(EnhancedForStatement node) { + try { + pm.persistLocalVarDeclaration(node.getParameter()); + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(EnumDeclaration node) { + try { + pm.persistEnumDeclaration(node); + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(ExpressionMethodReference node) { + try { + pm.persistMethodUsage(node, node.resolveMethodBinding(), node.getName()); + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(FieldAccess node) { + try { + pm.persistVariableUsage(node, node.resolveFieldBinding()); + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(FieldDeclaration node) { + try { + pm.persistFieldDeclaration(node); + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(Initializer node) { + try { + pm.persistInitializer(node); + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(LambdaExpression node) { + try { + pm.persistLambdaExpression(node); + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(MethodDeclaration node) { + try { + if (node.isConstructor()) { + pm.persistConstructorDeclaration(node); + } else { + pm.persistMethodDeclaration(node); + } + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(MethodInvocation node) { + try { + pm.persistMethodUsage(node, node.resolveMethodBinding(), node.getName()); + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(QualifiedName node) { + try { + IBinding nodeBinding = node.resolveBinding(); + ASTNode parent = node.getParent(); + boolean isDeclaration = + parent instanceof EnumConstantDeclaration || + parent instanceof SingleVariableDeclaration || + parent instanceof VariableDeclarationFragment; + boolean isFieldAccess = + parent instanceof FieldAccess || + parent instanceof SuperFieldAccess; + + if (nodeBinding != null && nodeBinding.getKind() == IBinding.VARIABLE && + !isDeclaration && !isFieldAccess) + { + IVariableBinding variableBinding = + (IVariableBinding) node.resolveBinding(); + + if (variableBinding.isEnumConstant()) { + pm.persistEnumConstantUsage(node, variableBinding); + } else { + pm.persistVariableUsage(node, variableBinding); + } + } + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(SimpleName node) { + try { + IBinding nodeBinding = node.resolveBinding(); + ASTNode parent = node.getParent(); + boolean isDeclaration = + parent instanceof EnumConstantDeclaration || + parent instanceof SingleVariableDeclaration || + parent instanceof VariableDeclarationFragment; + boolean isFieldAccess = + parent instanceof FieldAccess || + parent instanceof SuperFieldAccess; + boolean isPartOfQualifiedName = + parent instanceof QualifiedName; + + if (nodeBinding != null && nodeBinding.getKind() == IBinding.VARIABLE && + !isDeclaration && !isFieldAccess && !isPartOfQualifiedName) + { + IVariableBinding variableBinding = + (IVariableBinding) node.resolveBinding(); + + if (variableBinding.isEnumConstant()) { + pm.persistEnumConstantUsage(node, variableBinding); + } else { + pm.persistVariableUsage(node, variableBinding); + } + } + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(SimpleType node) { + try { + pm.persistTypeUsage(node); + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(SuperConstructorInvocation node) { + try { + pm.persistConstructorUsage(node, node.resolveConstructorBinding()); + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(SuperFieldAccess node) { + try { + pm.persistVariableUsage(node, node.resolveFieldBinding()); + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(SuperMethodInvocation node) { + try { + pm.persistMethodUsage(node, node.resolveMethodBinding(), node.getName()); + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(SuperMethodReference node) { + try { + pm.persistMethodUsage(node, node.resolveMethodBinding(), node.getName()); + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(TypeDeclaration node) { + try { + pm.persistTypeDeclaration(node); + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + @Override + public boolean visit(VariableDeclarationFragment node) { + try { + IVariableBinding nodeBinding = node.resolveBinding(); + ASTNode parent = node.getParent(); + + if (!nodeBinding.isField() && !(parent instanceof LambdaExpression)) { + pm.persistLocalVarDeclaration(node); + } + } catch (Exception ex) { + printErrorDuringParsing(node); + errorDueParsing = true; + } + + return super.visit(node); + } + + private void printErrorDuringParsing(ASTNode node) { + int startPosition = node.getStartPosition(); + int startLine = cu.getLineNumber(startPosition); + int startColumn = cu.getColumnNumber(startPosition); + + LOGGER.log( + Level.WARNING, + fileCounterStr + + " Parsing " + node.getClass().getSimpleName() + " node at " + + startLine + ":" + startColumn + " has been failed" + ); + } + + public boolean isErrorDueParsing() { + return errorDueParsing; + } +} diff --git a/plugins/java/parser/srcjava/CMakeLists.txt b/plugins/java/parser/srcjava/CMakeLists.txt new file mode 100644 index 000000000..8da862ea5 --- /dev/null +++ b/plugins/java/parser/srcjava/CMakeLists.txt @@ -0,0 +1,25 @@ +set(CMAKE_JAVA_INCLUDE_PATH + ${PROJECT_SOURCE_DIR}/lib/java/* + ${PLUGIN_DIR}/lib/java/*) + +set(CMAKE_JAVA_COMPILE_FLAGS + ${CMAKE_JAVA_COMPILE_FLAGS} + -sourcepath ${CMAKE_CURRENT_SOURCE_DIR}:${PLUGIN_DIR}) + +add_jar(javaparserjava + SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/enums/ValidCommands.java + ${CMAKE_CURRENT_SOURCE_DIR}/ArgParser.java + ${CMAKE_CURRENT_SOURCE_DIR}/AstVisitor.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaParser.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaParserServer.java + ${CMAKE_CURRENT_SOURCE_DIR}/QueryManager.java + ${CMAKE_CURRENT_SOURCE_DIR}/PersistManager.java + ${CMAKE_CURRENT_SOURCE_DIR}/PositionInfo.java + ${CMAKE_CURRENT_SOURCE_DIR}/RelationCollector.java + ${CMAKE_CURRENT_SOURCE_DIR}/Utils.java + MANIFEST ${CMAKE_CURRENT_SOURCE_DIR}/META-INF/MANIFEST.MF + INCLUDE_JARS javalogger javamodel javaparserthriftjava + OUTPUT_NAME javaparser) + +install_jar(javaparserjava "${INSTALL_JAVA_LIB_DIR}") \ No newline at end of file diff --git a/plugins/java/parser/srcjava/JavaParser.java b/plugins/java/parser/srcjava/JavaParser.java new file mode 100644 index 000000000..b34ec7bdd --- /dev/null +++ b/plugins/java/parser/srcjava/JavaParser.java @@ -0,0 +1,108 @@ +package parser.srcjava; + +import cc.parser.java.*; +import com.strobel.decompiler.Decompiler; +import com.strobel.decompiler.DecompilerSettings; +import com.strobel.decompiler.PlainTextOutput; +import model.EMFactory; +import org.apache.commons.io.FileUtils; +import org.apache.thrift.TException; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTParser; +import org.eclipse.jdt.core.dom.CompilationUnit; + +import javax.persistence.EntityManager; +import java.io.*; +import java.util.*; + +import static parser.srcjava.Utils.*; + +public class JavaParser implements JavaParserService.Iface { + private final Hashtable javaCoreOptions; + private final ThreadLocal em; + private final ThreadLocal parser; + + { + javaCoreOptions = getJavaCoreOptions(); + EMFactory emf = new EMFactory(System.getProperty("rawDbContext"), true); + em = ThreadLocal.withInitial(emf::createEntityManager); + parser = ThreadLocal.withInitial(() -> ASTParser.newParser(AST.JLS_Latest)); + parser.get().setKind(ASTParser.K_COMPILATION_UNIT); + } + + @Override + public ParseResult parseFile( + CompileCommand compileCommand, long fileId, + String fileCounterStr) throws TException + { + ArgParser argParser = new ArgParser(compileCommand, fileCounterStr); + String filePath = argParser.getFilepath(); + + try { + File file = new File(filePath); + String fileStr = FileUtils.readFileToString(file, "UTF-8"); + + String[] classpathEntries = + argParser.getClasspath().toArray(new String[0]); + String[] sourcepathEntries = + argParser.getSourcepath().toArray(new String[0]); + String[] encodings = new String[sourcepathEntries.length]; + Arrays.fill(encodings, argParser.getEncoding()); + + parser.get().setResolveBindings(true); + parser.get().setBindingsRecovery(true); + parser.get().setCompilerOptions(javaCoreOptions); + parser.get().setUnitName(argParser.getFilename()); + parser.get().setEnvironment( + classpathEntries, sourcepathEntries, + encodings, true + ); + parser.get().setSource(fileStr.toCharArray()); + + CompilationUnit cu = (CompilationUnit) parser.get().createAST(null); + + AstVisitor visitor = new AstVisitor(cu, em.get(), fileId, fileCounterStr); + cu.accept(visitor); + + return getParseResult( + cu, argParser, fileCounterStr, visitor.isErrorDueParsing()); + + } catch (IOException e) { + JavaBeforeParseException ex = new JavaBeforeParseException(); + ex.message = e.getMessage(); + throw ex; + } + } + + @Override + public String decompileClass(String path) throws TException { + String javaFilePath = + String.join( + ".", path.substring(0, path.lastIndexOf('.')), + "java" + ); + + try ( + FileOutputStream stream = + new FileOutputStream(javaFilePath); + OutputStreamWriter writer = new OutputStreamWriter(stream)) + { + DecompilerSettings settings = new DecompilerSettings(); + + settings.setIncludeLineNumbersInBytecode(false); + settings.setIncludeErrorDiagnostics(false); + settings.setForceExplicitImports(true); + + synchronized (this) { + Decompiler.decompile(path, new PlainTextOutput(writer), settings); + } + } + catch (Exception e) { + ClassDecompileException ex = new ClassDecompileException(); + ex.message = e.getMessage(); + throw ex; + } + + return javaFilePath; + } +} diff --git a/plugins/java/parser/srcjava/JavaParserServer.java b/plugins/java/parser/srcjava/JavaParserServer.java new file mode 100644 index 000000000..3f3718844 --- /dev/null +++ b/plugins/java/parser/srcjava/JavaParserServer.java @@ -0,0 +1,51 @@ +package parser.srcjava; + +import org.apache.log4j.BasicConfigurator; +import org.apache.thrift.server.TThreadPoolServer; +import org.apache.thrift.transport.TServerSocket; +import org.apache.thrift.transport.TServerTransport; + +import java.util.logging.Level; + +import static logger.Logger.LOGGER; +import cc.parser.java.JavaParserService; + +public class JavaParserServer { + public static JavaParser javaParser; + public static JavaParserService.Processor processor; + + public static void main(String [] args) { + BasicConfigurator.configure(); + + try { + javaParser = new JavaParser(); + processor = new JavaParserService.Processor<>(javaParser); + + Runnable threadPool = () -> threadPool(processor); + new Thread(threadPool).start(); + } catch (Exception e) { + LOGGER.log( + Level.SEVERE, "[javaparser] Java server starting failed!"); + } + } + + public static void threadPool(JavaParserService.Processor processor) { + try { + TServerTransport serverTransport = new TServerSocket(9090); + TThreadPoolServer.Args a = + new TThreadPoolServer.Args(serverTransport).processor(processor); + int threadNum = Integer.parseInt(System.getProperty("threadNum")); + + a.minWorkerThreads(1); + a.maxWorkerThreads(threadNum); + + TThreadPoolServer server = new TThreadPoolServer(a); + + server.serve(); + } catch (Exception e) { + e.printStackTrace(); + LOGGER.log( + Level.SEVERE, "[javaparser] Java server starting failed!"); + } + } +} diff --git a/plugins/java/parser/srcjava/META-INF/MANIFEST.MF b/plugins/java/parser/srcjava/META-INF/MANIFEST.MF new file mode 100644 index 000000000..e8cefe5b3 --- /dev/null +++ b/plugins/java/parser/srcjava/META-INF/MANIFEST.MF @@ -0,0 +1,17 @@ +Manifest-Version: 1.0 +Class-Path: commons-io-2.8.0.jar + libthrift-0.13.0.jar + org.eclipse.core.contenttype_3.7.600.v20200124-1609.jar + org.eclipse.core.jobs_3.10.600.v20191122-2104.jar + org.eclipse.core.resources_3.13.600.v20191122-2104.jar + org.eclipse.core.runtime_3.17.100.v20200203-0917.jar + org.eclipse.equinox.common_3.10.600.v20191004-1420.jar + org.eclipse.equinox.preferences_3.7.500.v20190815-1535.jar + org.eclipse.jdt.core_3.24.0.v20201123-0742.jar + org.eclipse.osgi_3.15.0.v20190830-1434.jar + procyon-compilertools-0.5.36.jar + procyon-core-0.5.36.jar + javalogger.jar + javamodel.jar + javaparserthrift.jar +Main-Class: parser.srcjava.JavaParserServer diff --git a/plugins/java/parser/srcjava/PersistManager.java b/plugins/java/parser/srcjava/PersistManager.java new file mode 100644 index 000000000..6d36318e9 --- /dev/null +++ b/plugins/java/parser/srcjava/PersistManager.java @@ -0,0 +1,778 @@ +package parser.srcjava; + +import model.*; +import model.enums.*; +import org.eclipse.jdt.core.dom.*; + +import javax.persistence.EntityManager; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.function.Consumer; + +import static parser.srcjava.Utils.*; + +public class PersistManager { + private final CompilationUnit cu; + private final EntityManager em; + private final QueryManager qm; + private final long fileId; + + public PersistManager(CompilationUnit cu, EntityManager em, long fileId) { + this.cu = cu; + this.em = em; + this.qm = new QueryManager(em); + this.fileId = fileId; + } + + public void persistLocalVarDeclaration( + VariableDeclaration node) + { + JavaVariable javaVariable = new JavaVariable(); + IVariableBinding nodeBinding = node.resolveBinding(); + SimpleName simpleName = node.getName(); + String qualifiedName = simpleName.getFullyQualifiedName(); + String qualifiedType = getQualifiedTypeName(nodeBinding.getType()); + int modifiers = nodeBinding.getModifiers(); + int typeHash = qualifiedType.hashCode(); + String entityHashStr = ""; + String declaringNodeHashStr = ""; + IMethodBinding methodDeclBinding = nodeBinding.getDeclaringMethod(); + + if (methodDeclBinding == null) { + ASTNode declaringNode = findDeclaringNode(node); + ASTNode declaringNodeParent = declaringNode.getParent(); + + if ( + declaringNode instanceof Initializer && + declaringNodeParent instanceof AbstractTypeDeclaration) + { + String declaringClassName = + getQualifiedTypeName( + ((AbstractTypeDeclaration) declaringNodeParent).resolveBinding()); + + declaringNodeHashStr = + getInitializerHashStr( + declaringClassName, declaringNode.getStartPosition()); + } + + } else { + ITypeBinding classBinding = methodDeclBinding.getDeclaringClass(); + declaringNodeHashStr = getMethodHashStr( + node, classBinding, methodDeclBinding); + } + + entityHashStr = getVariableHashStr( + declaringNodeHashStr, qualifiedType, qualifiedName); + + int declaringNodeEntityHash = declaringNodeHashStr.hashCode(); + int entityHash = entityHashStr.hashCode(); + + JavaAstNode javaAstNode = persistJavaAstNodeRow( + node, SymbolType.VARIABLE, + node.getInitializer() == null ? AstType.DECLARATION : AstType.DEFINITION, + entityHash, entityHash + ); + + JavaAstNode javaAstNodeDef = + qm.queryParentAstNode(javaAstNode, declaringNodeEntityHash); + + setJavaTypedEntityFields(javaVariable, modifiers, typeHash, qualifiedType); + + if (methodDeclBinding == null) { + JavaInitializer javaInitializer = + qm.queryJavaInitializer(javaAstNodeDef.getId()); + + javaInitializer.addJavaInitVarLocal(javaVariable); + + } else if (methodDeclBinding.isConstructor()) { + JavaConstructor javaConstructor = + qm.queryJavaConstructor(javaAstNodeDef.getId()); + + javaConstructor.addJavaConVarLocal(javaVariable); + + } else { + JavaMethod javaMethod = qm.queryJavaMethod(javaAstNodeDef.getId()); + + javaMethod.addJavaMetVarLocal(javaVariable); + } + + setJavaEntityFields( + javaVariable, javaAstNode.getId(), entityHash, + simpleName.toString(), qualifiedName + ); + + persistRow(javaVariable); + } + + public void persistConstructorUsage( + ASTNode node, IMethodBinding constructorBinding) + { + JavaConstructor javaConstructor = new JavaConstructor(); + ITypeBinding classBinding = constructorBinding.getDeclaringClass(); + String name = constructorBinding.getName(); + String qualifiedName = getQualifiedTypeName(classBinding); + String entityHashStr = getMethodHashStr(classBinding, constructorBinding); + int modifiers = constructorBinding.getModifiers(); + int classHash = qualifiedName.hashCode(); + int entityHash = entityHashStr.hashCode(); + + JavaAstNode javaAstNode = persistJavaAstNodeRow( + node, SymbolType.CONSTRUCTOR, AstType.USAGE, entityHash, entityHash); + + persistJavaMemberType( + classHash, classHash, MemberTypeKind.CONSTRUCTOR, modifiers, javaAstNode); + + setJavaEntityFields( + javaConstructor, javaAstNode.getId(), + entityHash, name, qualifiedName + ); + + persistRow(javaConstructor); + } + + public void persistEnumDeclaration(EnumDeclaration node) { + JavaEnum javaEnum = new JavaEnum(); + ITypeBinding enumBinding = node.resolveBinding(); + List superInterfaceTypes = node.superInterfaceTypes(); + List enumConstants = node.enumConstants(); + SimpleName simpleName = node.getName(); + String declaringClassNameTypeParams = getQualifiedTypeName(enumBinding); + int modifiers = node.getModifiers(); + int entityHash = enumBinding.getQualifiedName().hashCode(); + int mainTypeHash; + + // Persist Enum constants + for (int i = 0; i < enumConstants.size(); i++) { + persistEnumConstantDeclaration( + javaEnum, (EnumConstantDeclaration) enumConstants.get(i), i); + } + + if (node.isMemberTypeDeclaration()) { + mainTypeHash = getMainTypeHashForInnerType(node); + } else { + mainTypeHash = entityHash; + } + + JavaAstNode javaAstNode = persistJavaAstNodeRow( + node, SymbolType.ENUM, AstType.DEFINITION, entityHash, entityHash); + + persistJavaMemberType( + mainTypeHash, entityHash, MemberTypeKind.ENUM, modifiers, javaAstNode); + + setJavaEntityFields( + javaEnum, javaAstNode.getId(), entityHash, + simpleName.toString(), declaringClassNameTypeParams + ); + + persistInterfaceImplementations(superInterfaceTypes, entityHash); + + persistRow(javaEnum); + } + + public void persistEnumConstantDeclaration( + JavaEnum javaEnum, EnumConstantDeclaration node, int index) + { + JavaEnumConstant enumConstant = new JavaEnumConstant(); + ITypeBinding declaringClass = + node.resolveConstructorBinding().getDeclaringClass(); + SimpleName simpleName = node.getName(); + String qualifiedName = simpleName.getFullyQualifiedName(); + String declaringClassName = getQualifiedTypeName(declaringClass); + String entityHashStr = getEnumConstantHashStr( + declaringClassName, qualifiedName); + int modifiers = declaringClass.getModifiers(); + int entityHash = entityHashStr.hashCode(); + + enumConstant.setValue(index); + javaEnum.addJavaEnumConstant(enumConstant); + + JavaAstNode javaAstNode = persistJavaAstNodeRow( + node, SymbolType.ENUM_CONSTANT, + AstType.DEFINITION, entityHash, entityHash); + + // Set JavaMemberType fields + persistJavaMemberType( + entityHash, entityHash, + MemberTypeKind.ENUM_CONSTANT, modifiers, javaAstNode + ); + + setJavaEntityFields( + enumConstant, javaAstNode.getId(), + entityHash, simpleName.toString(), qualifiedName + ); + } + + public void persistMethodUsage( + Expression node, IMethodBinding methodBinding, SimpleName simpleName) + { + JavaMethod javaMethod = new JavaMethod(); + ITypeBinding classBinding = methodBinding.getDeclaringClass(); + String qualifiedName = simpleName.getFullyQualifiedName(); + String qualifiedType = getQualifiedTypeName(methodBinding.getReturnType()); + String declaringClassName = getQualifiedTypeName(classBinding); + String entityHashStr = getMethodHashStr(classBinding, methodBinding); + int modifiers = methodBinding.getModifiers(); + int classHash = declaringClassName.hashCode(); + int entityHash = entityHashStr.hashCode(); + int typeHash = qualifiedType.hashCode(); + + setJavaTypedEntityFields(javaMethod, modifiers, typeHash, qualifiedType); + + JavaAstNode javaAstNode = persistJavaAstNodeRow( + node, SymbolType.METHOD, AstType.USAGE, entityHash, entityHash); + + // Set JavaMemberType fields + persistJavaMemberType( + classHash, typeHash, MemberTypeKind.METHOD, modifiers, javaAstNode); + + setJavaEntityFields( + javaMethod, javaAstNode.getId(), entityHash, + simpleName.toString(), qualifiedName + ); + + persistRow(javaMethod); + } + + public void persistVariableUsage( + Expression node, IVariableBinding variableBinding) + { + JavaVariable javaVariable = new JavaVariable(); + ASTNode parent = node.getParent(); + String qualifiedType = getQualifiedTypeName(variableBinding.getType()); + String name = variableBinding.getName(); + String entityHashStr = ""; + String declaringClassName = ""; + AstType astType; + + if (variableBinding.isField()) { + if (!variableBinding.getKey().equals(".length)I")) { + ITypeBinding classBinding = variableBinding.getDeclaringClass(); + declaringClassName = getQualifiedTypeName(classBinding); + } + entityHashStr = getFieldHashStr(declaringClassName, qualifiedType, name); + + } else { + IMethodBinding declaringMethodBinding = + variableBinding.getVariableDeclaration().getDeclaringMethod(); + String declaringNodeHashStr = ""; + + if (declaringMethodBinding == null) { + ASTNode declaringNode = findDeclaringNode(node); + ASTNode declaringNodeParent = declaringNode.getParent(); + + if ( + declaringNode instanceof Initializer && + declaringNodeParent instanceof AbstractTypeDeclaration) + { + declaringClassName = + getQualifiedTypeName( + ((AbstractTypeDeclaration) declaringNodeParent).resolveBinding()); + + declaringNodeHashStr = + getInitializerHashStr( + declaringClassName, declaringNode.getStartPosition()); + } + } else { + ITypeBinding classBinding = declaringMethodBinding.getDeclaringClass(); + declaringClassName = getQualifiedTypeName(classBinding); + declaringNodeHashStr = getMethodHashStr( + node, classBinding, declaringMethodBinding); + } + + entityHashStr = getVariableHashStr( + declaringNodeHashStr, qualifiedType, name); + } + + int modifiers = variableBinding.getModifiers(); + int classHash = declaringClassName.hashCode(); + int entityHash = entityHashStr.hashCode(); + int typeHash = qualifiedType.hashCode(); + + setJavaTypedEntityFields(javaVariable, modifiers, typeHash, qualifiedType); + + if (parent instanceof Assignment) { + Assignment assignment = (Assignment) parent; + Expression leftHandSide = assignment.getLeftHandSide(); + + astType = + node.getStartPosition() == leftHandSide.getStartPosition() ? + AstType.WRITE : + AstType.READ; + } else if (parent instanceof MethodInvocation) { + MethodInvocation methodInvocation = (MethodInvocation) parent; + astType = + node.getStartPosition() == methodInvocation.getStartPosition() ? + AstType.WRITE : + AstType.READ; + } else if ( + parent instanceof PrefixExpression || + parent instanceof PostfixExpression) + { + astType = AstType.WRITE; + } else { + astType = AstType.READ; + } + + JavaAstNode javaAstNode = persistJavaAstNodeRow( + node, SymbolType.VARIABLE, astType, entityHash, entityHash); + + if (variableBinding.isField()) { + persistJavaMemberType( + classHash, typeHash, MemberTypeKind.FIELD, modifiers, javaAstNode); + } + + setJavaEntityFields( + javaVariable, javaAstNode.getId(), entityHash, name, name); + + persistRow(javaVariable); + } + + public void persistEnumConstantUsage( + Name node, IVariableBinding variableBinding) + { + JavaEnumConstant javaEnumConstant = new JavaEnumConstant(); + String qualifiedType = getQualifiedTypeName(variableBinding.getType()); + String name = variableBinding.getName(); + String entityHashStr = getEnumConstantHashStr(qualifiedType, name); + int modifiers = variableBinding.getModifiers(); + int classHash = qualifiedType.hashCode(); + int entityHash = entityHashStr.hashCode(); + + javaEnumConstant.setValue(variableBinding.getVariableId()); + + JavaAstNode javaAstNode = persistJavaAstNodeRow( + node, SymbolType.ENUM_CONSTANT, AstType.USAGE, entityHash, entityHash + ); + + persistJavaMemberType( + classHash, classHash, + MemberTypeKind.ENUM_CONSTANT, modifiers, javaAstNode + ); + + setJavaEntityFields( + javaEnumConstant, javaAstNode.getId(), entityHash, name, name); + + persistRow(javaEnumConstant); + } + + public void persistFieldDeclaration(FieldDeclaration node) { + String declaringClassName = ""; + ASTNode parent = node.getParent(); + + if (parent instanceof AbstractTypeDeclaration) { + declaringClassName = + getQualifiedTypeName( + ((AbstractTypeDeclaration) parent).resolveBinding() + ); + } + + String qualifiedType = + getQualifiedTypeName(node.getType().resolveBinding()); + + for (Object varDeclFragObj : node.fragments()) { + persistFieldDeclarationFragment( + (VariableDeclarationFragment) varDeclFragObj, + declaringClassName, qualifiedType); + } + } + + private void persistFieldDeclarationFragment( + VariableDeclarationFragment node, + String declaringClassName, String qualifiedType) + { + JavaVariable javaVariable = new JavaVariable(); + IVariableBinding nodeBinding = node.resolveBinding(); + SimpleName simpleName = node.getName(); + String qualifiedName = simpleName.getFullyQualifiedName(); + String entityHashStr = getFieldHashStr( + declaringClassName, qualifiedType, qualifiedName); + int modifiers = nodeBinding.getModifiers(); + int entityHash = entityHashStr.hashCode(); + int classHash = declaringClassName.hashCode(); + int typeHash = qualifiedType.hashCode(); + + setJavaTypedEntityFields(javaVariable, modifiers, typeHash, qualifiedType); + + JavaAstNode javaAstNode = persistJavaAstNodeRow( + node, SymbolType.VARIABLE, + node.getInitializer() == null ? AstType.DECLARATION : AstType.DEFINITION, + entityHash, entityHash + ); + + persistJavaMemberType( + classHash, typeHash, MemberTypeKind.FIELD, modifiers, javaAstNode); + + setJavaEntityFields( + javaVariable, javaAstNode.getId(), + entityHash, simpleName.toString(), qualifiedName + ); + + persistRow(javaVariable); + } + + public void persistLambdaExpression(LambdaExpression node) { + JavaMethod javaMethod = new JavaMethod(); + IMethodBinding methodBinding = node.resolveMethodBinding(); + ITypeBinding classBinding = methodBinding.getDeclaringClass(); + String declaringClassName = getQualifiedTypeName(classBinding); + String qualifiedType = getQualifiedTypeName(methodBinding.getReturnType()); + String entityHashStr = getMethodHashStr(node, classBinding, methodBinding); + int modifiers = methodBinding.getModifiers(); + int entityHash = entityHashStr.hashCode(); + int classHash = declaringClassName.hashCode(); + int typeHash = qualifiedType.hashCode(); + + setJavaTypedEntityFields(javaMethod, modifiers, typeHash, qualifiedType); + + // Persist method's parameters + for (Object varDeclObj : node.parameters()) { + persistParameterDeclaration( + (VariableDeclarationFragment) varDeclObj, + entityHashStr, javaMethod::addJavaMetVarParam + ); + } + + JavaAstNode javaAstNode = persistJavaAstNodeRow( + node, SymbolType.METHOD, AstType.DEFINITION, entityHash, entityHash); + + persistJavaMemberType( + classHash, typeHash, MemberTypeKind.METHOD, modifiers, javaAstNode); + + setJavaEntityFields(javaMethod, javaAstNode.getId(), entityHash, "", ""); + + persistRow(javaMethod); + } + + public void persistConstructorDeclaration(MethodDeclaration node) { + JavaConstructor javaConstructor = new JavaConstructor(); + IMethodBinding methodBinding = node.resolveBinding(); + ITypeBinding classBinding = methodBinding.getDeclaringClass(); + String name = node.getName().toString(); + String declaringClassName = getQualifiedTypeName(classBinding); + String entityHashStr = getMethodHashStr(classBinding, methodBinding); + int modifiers = node.getModifiers(); + int entityHash = entityHashStr.hashCode(); + int classHash = declaringClassName.hashCode(); + + // Persist constructor's parameters + for (Object varDeclObj : node.parameters()) { + persistParameterDeclaration( + (SingleVariableDeclaration) varDeclObj, + entityHashStr, javaConstructor::addJavaConVarParam + ); + } + + JavaAstNode javaAstNode = persistJavaAstNodeRow( + node, SymbolType.CONSTRUCTOR, AstType.DEFINITION, entityHash, entityHash); + + persistJavaMemberType( + classHash, classHash, MemberTypeKind.CONSTRUCTOR, modifiers, javaAstNode); + + setJavaEntityFields( + javaConstructor, javaAstNode.getId(), entityHash, + name, declaringClassName + ); + + persistRow(javaConstructor); + } + + public void persistMethodDeclaration(MethodDeclaration node) { + JavaMethod javaMethod = new JavaMethod(); + IMethodBinding methodBinding = node.resolveBinding(); + ITypeBinding classBinding = methodBinding.getDeclaringClass(); + String declaringClassName = getQualifiedTypeName(classBinding); + SimpleName simpleName = node.getName(); + String name = simpleName.toString(); + String qualifiedName = simpleName.getFullyQualifiedName(); + String qualifiedType = + getQualifiedTypeName(node.getReturnType2().resolveBinding()); + AstType astType = + node.getBody() == null ? AstType.DECLARATION : AstType.DEFINITION; + String entityHashStr = getMethodHashStr(classBinding, methodBinding); + int modifiers = node.getModifiers(); + int entityHash = entityHashStr.hashCode(); + int classHash = declaringClassName.hashCode(); + int typeHash = qualifiedType.hashCode(); + + setJavaTypedEntityFields(javaMethod, modifiers, typeHash, qualifiedType); + + // Persist method's parameters + for (Object varDeclObj : node.parameters()) { + persistParameterDeclaration( + (SingleVariableDeclaration) varDeclObj, + entityHashStr, javaMethod::addJavaMetVarParam + ); + } + + JavaAstNode javaAstNode = persistJavaAstNodeRow( + node, SymbolType.METHOD, astType, entityHash, entityHash); + + JavaMemberType javaMemberType = persistJavaMemberType( + classHash, typeHash, MemberTypeKind.METHOD, modifiers, javaAstNode); + + persistMethodRelations( + classBinding.getSuperclass(), classBinding.getInterfaces(), + methodBinding, name, javaMemberType.getVisibility(), entityHash + ); + + setJavaEntityFields( + javaMethod, javaAstNode.getId(), entityHash, name, qualifiedName); + + persistRow(javaMethod); + } + + public void persistTypeDeclaration(TypeDeclaration node) { + JavaRecord javaRecord = new JavaRecord(); + ITypeBinding typeBinding = node.resolveBinding(); + Type superclassType = node.getSuperclassType(); + List superInterfaceTypes = node.superInterfaceTypes(); + SimpleName simpleName = node.getName(); + String qualifiedNameTypeParams = getQualifiedTypeName(typeBinding); + int modifiers = node.getModifiers(); + int entityHash = typeBinding.getQualifiedName().hashCode(); + int mainTypeHash; + + if (node.isMemberTypeDeclaration()) { + mainTypeHash = getMainTypeHashForInnerType(node); + } else { + mainTypeHash = entityHash; + } + + setJavaRecordFields(javaRecord, modifiers); + + JavaAstNode javaAstNode = + persistJavaAstNodeRow( + node, SymbolType.TYPE, AstType.DEFINITION, entityHash, entityHash); + + persistJavaMemberType( + mainTypeHash, entityHash, MemberTypeKind.TYPE, modifiers, javaAstNode); + + setJavaEntityFields( + javaRecord, javaAstNode.getId(), entityHash, + simpleName.toString(), qualifiedNameTypeParams + ); + + persistClassExtensions(superclassType, entityHash); + persistInterfaceImplementations(superInterfaceTypes, entityHash); + + persistRow(javaRecord); + } + + public void persistTypeUsage(SimpleType node) { + JavaRecord javaRecord = new JavaRecord(); + ITypeBinding typeBinding = node.resolveBinding(); + String name = typeBinding.getName(); + String qualifiedNameTypeParams = getQualifiedTypeName(typeBinding); + boolean isEnum = typeBinding.isEnum(); + int modifiers = typeBinding.getModifiers(); + int entityHash = qualifiedNameTypeParams.hashCode(); + int defEntityHash = + typeBinding.getQualifiedName().split("<", 2)[0].hashCode(); + + setJavaRecordFields(javaRecord, modifiers); + + JavaAstNode javaAstNode = + persistJavaAstNodeRow( + node, isEnum ? SymbolType.ENUM : SymbolType.TYPE, + AstType.USAGE, entityHash, defEntityHash + ); + + persistJavaMemberType( + entityHash, entityHash, + isEnum ? MemberTypeKind.ENUM : MemberTypeKind.TYPE, + modifiers, javaAstNode + ); + + setJavaEntityFields( + javaRecord, javaAstNode.getId(), entityHash, name, qualifiedNameTypeParams); + + persistRow(javaRecord); + } + + public void persistInitializer(Initializer node) { + JavaInitializer javaInitializer = new JavaInitializer(); + ASTNode parent = node.getParent(); + String declaringClassName = ""; + + if (parent instanceof AbstractTypeDeclaration) { + declaringClassName = + getQualifiedTypeName( + ((AbstractTypeDeclaration) parent).resolveBinding() + ); + } + + String entityHashStr = + getInitializerHashStr(declaringClassName, node.getStartPosition()); + int entityHash = entityHashStr.hashCode(); + + setJavaInitializerFields( + javaInitializer, node.getModifiers(), declaringClassName.hashCode()); + + JavaAstNode javaAstNode = persistJavaAstNodeRow( + node, SymbolType.INITIALIZER, AstType.DEFINITION, entityHash, entityHash); + + setJavaEntityFields(javaInitializer, javaAstNode.getId(), entityHash); + + persistRow(javaInitializer); + } + + public void persistParameterDeclaration( + VariableDeclaration node, String methodHashStr, + Consumer connectParent) + { + JavaVariable javaVariable = new JavaVariable(); + IVariableBinding nodeBinding = node.resolveBinding(); + SimpleName simpleName = node.getName(); + String qualifiedType = getQualifiedTypeName(nodeBinding.getType()); + String qualifiedName = simpleName.getFullyQualifiedName(); + String entityHashStr = getVariableHashStr( + methodHashStr, qualifiedType, qualifiedName); + int modifiers = nodeBinding.getModifiers(); + int entityHash = entityHashStr.hashCode(); + int typeHash = qualifiedType.hashCode(); + + setJavaTypedEntityFields(javaVariable, modifiers, typeHash, qualifiedType); + + JavaAstNode javaAstNode = persistJavaAstNodeRow( + node, SymbolType.VARIABLE, AstType.DECLARATION, entityHash, entityHash); + + setJavaEntityFields( + javaVariable, javaAstNode.getId(), + entityHash, simpleName.toString(), qualifiedName + ); + + connectParent.accept(javaVariable); + } + + public void persistClassExtensions( + Type superclassType, int entityHash) + { + if (superclassType != null) { + String qualifiedSuperClassName = + getQualifiedTypeName(superclassType.resolveBinding()); + int superClassHash = qualifiedSuperClassName.hashCode(); + + persistJavaInheritance(superClassHash, entityHash); + } + } + + public void persistInterfaceImplementations( + List superInterfaceTypes, int entityHash) + { + superInterfaceTypes.forEach(i -> { + Type aInterface = (Type) i; + String qualifiedSuperInterfaceName = + getQualifiedTypeName(aInterface.resolveBinding()); + int superInterfaceHash = qualifiedSuperInterfaceName.hashCode(); + + persistJavaInheritance(superInterfaceHash, entityHash); + }); + } + + public JavaInheritance persistJavaInheritance( + int baseEntityHash, int derivedEntityHash) + { + JavaInheritance javaInheritance = new JavaInheritance(); + + setJavaInheritanceFields( + javaInheritance, baseEntityHash, derivedEntityHash); + + persistRow(javaInheritance); + + return javaInheritance; + } + + public void persistMethodRelations( + ITypeBinding superclassBinding, ITypeBinding[] superInterfaceBindings, + IMethodBinding methodBinding, String methodName, + Visibility methodVisibility, int methodEntityHash) + { + RelationCollector relationCollector = + new RelationCollector( + methodBinding, methodName, + methodVisibility, methodEntityHash + ); + + List javaRelations = + relationCollector.collectBaseMethods( + superclassBinding, superInterfaceBindings); + + javaRelations.forEach(this::persistRow); + } + + public JavaMemberType persistJavaMemberType( + int typeHash, int memberTypeHash, MemberTypeKind memberTypeKind, + int modifiers, JavaAstNode javaAstNode) + { + JavaMemberType javaMemberType = new JavaMemberType(); + + setJavaMemberTypeFields( + javaMemberType, typeHash, memberTypeHash, + memberTypeKind, modifiers, javaAstNode + ); + + persistRow(javaMemberType); + + return javaMemberType; + } + + public void persistJavaDoc(Javadoc node, int entityHash) { + JavaDocComment javaDocComment = new JavaDocComment(); + String commentString = node.toString(); + + setJavaDocCommentFields(javaDocComment, commentString, entityHash); + + persistRow(javaDocComment); + } + + public JavaAstNode persistJavaAstNodeRow( + T node, SymbolType symbolType, AstType astType, + int entityHash, int defEntityHash) + { + JavaAstNode javaAstNode = new JavaAstNode(); + PositionInfo positionInfo; + String astValue; + + try { + Method getJavadocMethod = + node.getClass().getMethod("getJavadoc", (Class[]) null); + Javadoc javadoc = + (Javadoc) getJavadocMethod.invoke(node, (Object[]) null); + + persistJavaDoc(javadoc, entityHash); + + positionInfo = new PositionInfo(this.cu, node, javadoc); + int javadocLen = javadoc.toString().length(); + astValue = node.toString().substring(javadocLen); + + } catch (NoSuchMethodException | NullPointerException e) { + positionInfo = new PositionInfo(this.cu, node); + astValue = node.toString(); + + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(); + } + + setJavaAstNodeFields( + javaAstNode, astValue, positionInfo, fileId, + entityHash, defEntityHash, symbolType, astType, true + ); + + persistRow(javaAstNode); + + return javaAstNode; + } + + public void persistRow(Object jpaObject) { + try { + em.getTransaction().begin(); + em.persist(jpaObject); + em.getTransaction().commit(); + } catch (Exception ex) { + em.getTransaction().rollback(); + throw ex; + } + + } +} diff --git a/plugins/java/parser/srcjava/PositionInfo.java b/plugins/java/parser/srcjava/PositionInfo.java new file mode 100644 index 000000000..8e1ee5334 --- /dev/null +++ b/plugins/java/parser/srcjava/PositionInfo.java @@ -0,0 +1,111 @@ +package parser.srcjava; + +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.Javadoc; + +public class PositionInfo { + private final int start; + private final int end; + private final int startLine; + private final int startColumn; + private final int endLine; + private final int endColumn; + + public PositionInfo(CompilationUnit cu, int startPosition, int endPosition) { + this.start = startPosition + 1; + this.end = endPosition + 2; + this.startLine = cu.getLineNumber(start); + this.startColumn = cu.getColumnNumber(start); + + int endLine = cu.getLineNumber(end); + int endColumn = cu.getColumnNumber(end); + + /* + End position checking, and trying with a two less value. + This will work in most cases, if the current element is a class + declaration and the newline character is missing from it's end. + */ + this.endLine = + cu.getLineNumber(this.end) == -1 ? + cu.getLineNumber(this.end - 2) : + endLine; + this.endColumn = + cu.getColumnNumber(this.end) == -1 ? + cu.getColumnNumber(this.end - 2) : + endColumn; + } + + public PositionInfo(CompilationUnit cu, ASTNode node) { + int startPosition = node.getStartPosition(); + this.start = startPosition + 1; + this.end = startPosition + node.getLength() + 1; + this.startLine = cu.getLineNumber(start); + this.startColumn = cu.getColumnNumber(start); + + int endLine = cu.getLineNumber(end); + int endColumn = cu.getColumnNumber(end); + + /* + End position checking, and trying with a two less value. + This will work in most cases, if the current element is a class + declaration and the newline character is missing from it's end. + */ + this.endLine = + cu.getLineNumber(this.end) == -1 ? + cu.getLineNumber(this.end - 2) : + endLine; + this.endColumn = + cu.getColumnNumber(this.end) == -1 ? + cu.getColumnNumber(this.end - 2) : + endColumn; + } + + public PositionInfo(CompilationUnit cu, ASTNode node, Javadoc javadoc) { + this.start = javadoc.getStartPosition() + javadoc.getLength() + 2; + this.end = node.getStartPosition() + node.getLength() + 1; + this.startLine = cu.getLineNumber(start); + this.startColumn = cu.getColumnNumber(start); + + int endLine = cu.getLineNumber(end); + int endColumn = cu.getColumnNumber(end); + + /* + End position checking, and trying with a two less value. + This will work in most cases, if the current element is a class + declaration and the newline character is missing from it's end. + */ + this.endLine = + cu.getLineNumber(this.end) == -1 ? + cu.getLineNumber(this.end - 2) : + endLine; + this.endColumn = + cu.getColumnNumber(this.end) == -1 ? + cu.getColumnNumber(this.end - 2) : + endColumn; + } + + public int getStart() { + return start; + } + + public int getEnd() { + return end; + } + + public int getStartLine() { + return startLine; + } + + public int getStartColumn() { + return startColumn; + } + + public int getEndLine() { + return endLine; + } + + public int getEndColumn() { + return endColumn; + } +} \ No newline at end of file diff --git a/plugins/java/parser/srcjava/QueryManager.java b/plugins/java/parser/srcjava/QueryManager.java new file mode 100644 index 000000000..14d1749c6 --- /dev/null +++ b/plugins/java/parser/srcjava/QueryManager.java @@ -0,0 +1,106 @@ +package parser.srcjava; + +import model.*; +import model.enums.AstType; + +import javax.persistence.EntityManager; +import javax.persistence.criteria.*; + +public class QueryManager { + private final EntityManager em; + private final CriteriaBuilder cb; + + public QueryManager(EntityManager em) { + this.em = em; + this.cb = em.getCriteriaBuilder(); + } + + public JavaAstNode queryParentAstNode( + JavaAstNode child, int childEntityHash) + { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + + long childLocationFile = child.getLocation_file(); + long childStartLine = child.getLocation_range_start_line(); + long childStartColumn = child.getLocation_range_start_column(); + Path locationFile = + root.get("location_file"); + Path entityHash = + root.get("entityHash"); + Path astType = + root.get("astType"); + Path startLine = + root.get("location_range_start_line"); + Path endLine = + root.get("location_range_end_line"); + Path startColumn = + root.get("location_range_start_column"); + Path endColumn = + root.get("location_range_end_column"); + + Predicate sameFile = cb.equal(locationFile, childLocationFile); + Predicate sameEntityHash = cb.equal(entityHash, childEntityHash); + Predicate definition = cb.equal(astType, AstType.DEFINITION); + Predicate startPosLessEqualPos = + cb.or( + cb.and( + cb.equal(startLine, childStartLine), + cb.le(startColumn, childStartColumn) + ), + cb.lt(startLine, childStartLine) + ); + Predicate posLessThanEndPos = + cb.or( + cb.and( + cb.equal(endLine, childStartLine), + cb.gt(endColumn, childStartColumn) + ), + cb.gt(endLine, childStartLine) + ); + + cr + .select(root) + .where( + cb.and( + sameFile, sameEntityHash, definition, + startPosLessEqualPos, posLessThanEndPos + ) + ); + + return em.createQuery(cr).getSingleResult(); + } + + public E queryJavaEntityByAstNodeId( + CriteriaQuery cr, + Root root, + long astNodeId) + { + cr + .select(root) + .where(cb.equal(root.get("astNodeId"), astNodeId)); + + return em.createQuery(cr).getSingleResult(); + } + + public JavaInitializer queryJavaInitializer(long javaAstNodeId) { + CriteriaQuery cr = cb.createQuery(JavaInitializer.class); + Root root = cr.from(JavaInitializer.class); + + return queryJavaEntityByAstNodeId(cr, root, javaAstNodeId); + } + + public JavaConstructor queryJavaConstructor(long javaAstNodeId) { + CriteriaQuery cr = cb.createQuery(JavaConstructor.class); + Root root = cr.from(JavaConstructor.class); + + return queryJavaEntityByAstNodeId(cr, root, javaAstNodeId); + } + + public JavaMethod queryJavaMethod(long javaAstNodeId) { + CriteriaQuery cr = cb.createQuery(JavaMethod.class); + Root root = cr.from(JavaMethod.class); + + return queryJavaEntityByAstNodeId(cr, root, javaAstNodeId); + } +} diff --git a/plugins/java/parser/srcjava/RelationCollector.java b/plugins/java/parser/srcjava/RelationCollector.java new file mode 100644 index 000000000..c72363c98 --- /dev/null +++ b/plugins/java/parser/srcjava/RelationCollector.java @@ -0,0 +1,122 @@ +package parser.srcjava; + +import model.JavaRelation; +import model.enums.RelationKind; +import model.enums.Visibility; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import static parser.srcjava.Utils.*; + +public class RelationCollector { + private final IMethodBinding methodBinding; + private final String name; + private final Visibility visibility; + private final int entityHash; + + public RelationCollector( + IMethodBinding methodBinding, String name, + Visibility visibility, int entityHash) + { + this.methodBinding = methodBinding; + this.name = name; + this.visibility = visibility; + this.entityHash = entityHash; + } + + public List collectBaseMethods( + ITypeBinding superclassBinding, ITypeBinding[] superInterfaceBindings) + { + List javaRelations = new ArrayList<>(); + JavaRelation javaExtension = collectExtension(superclassBinding); + JavaRelation javaImplementation = + collectImplementation(superInterfaceBindings); + + if (javaExtension != null) { + javaRelations.add(javaExtension); + } + + if (javaImplementation != null) { + javaRelations.add(javaImplementation); + } + + return javaRelations; + } + + public JavaRelation collectExtension(ITypeBinding superclassBinding) { + if (superclassBinding == null) { + return null; + } + + JavaRelation javaRelation = + getRelation(superclassBinding, RelationKind.OVERRIDE); + + if (javaRelation != null) { + return javaRelation; + } + + return collectExtension(superclassBinding.getSuperclass()); + } + + public JavaRelation collectImplementation( + ITypeBinding[] superInterfaceBindings) + { + for (ITypeBinding superInterfaceBinding : superInterfaceBindings) { + JavaRelation javaRelation = + getRelation(superInterfaceBinding, RelationKind.IMPLEMENT); + + if (javaRelation != null) { + return javaRelation; + } + + return collectImplementation(superInterfaceBinding.getInterfaces()); + } + + return null; + } + + private JavaRelation getRelation( + ITypeBinding superclassBinding, RelationKind relationKind) + { + IMethodBinding[] declaredMethods = superclassBinding.getDeclaredMethods(); + Optional superMethodBinding = + Arrays.stream(declaredMethods) + .filter(this::isOverriddenMethod) + .findFirst(); + + if (superMethodBinding.isPresent()) { + JavaRelation javaRelation = new JavaRelation(); + String superMethodEntityHashStr = + getMethodHashStr(superclassBinding, methodBinding); + int superMethodEntityHash = superMethodEntityHashStr.hashCode(); + + setJavaRelationFields( + javaRelation, superMethodEntityHash, + entityHash, relationKind + ); + + return javaRelation; + } + + return null; + } + + private boolean isOverriddenMethod(IMethodBinding superMethodBinding) + { + Visibility mVisibility = getVisibility(superMethodBinding.getModifiers()); + String mName = superMethodBinding.getName(); + + return mName.equals(name) && + mVisibility.getValue() <= + visibility.getValue() && + Arrays.equals( + getParameterTypeNames(superMethodBinding.getParameterTypes()), + getParameterTypeNames(methodBinding.getParameterTypes()) + ); + } +} diff --git a/plugins/java/parser/srcjava/Utils.java b/plugins/java/parser/srcjava/Utils.java new file mode 100644 index 000000000..562472d8f --- /dev/null +++ b/plugins/java/parser/srcjava/Utils.java @@ -0,0 +1,366 @@ +package parser.srcjava; + +import cc.parser.java.CmdArgs; +import cc.parser.java.ParseResult; +import cc.service.core.BuildLog; +import cc.service.core.MessageType; +import cc.service.core.Position; +import cc.service.core.Range; +import model.*; +import model.enums.*; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.compiler.IProblem; +import org.eclipse.jdt.core.dom.*; + +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; +import java.util.logging.Level; +import java.util.stream.Collectors; + +import static logger.Logger.LOGGER; + +public abstract class Utils { + public static String getJavaVersion() { + String javaCoreVersion = System.getProperty("java.version"); + int dot1 = javaCoreVersion.indexOf("."); + + if (javaCoreVersion.startsWith("1.")) { + int dot2 = javaCoreVersion.indexOf(".", dot1 + 1); + return javaCoreVersion.substring(0, dot2); + } + + return javaCoreVersion.substring(0, dot1); + } + + public static Hashtable getJavaCoreOptions() { + Hashtable options = JavaCore.getOptions(); + JavaCore.setComplianceOptions(getJavaVersion(), options); + + return options; + } + + public static ParseResult getParseResult( + CompilationUnit cu, ArgParser argParser, + String fileCounterStr, boolean errorDueParsing) + { + ParseResult parseResult = new ParseResult(); + CmdArgs cmdArgs = getCmdArgs(argParser); + List buildLogs = Arrays.stream( + cu.getProblems()) + .map(p -> getBuildLog(cu, p, fileCounterStr)) + .collect(Collectors.toList()); + + parseResult.cmdArgs = cmdArgs; + parseResult.buildLogs = buildLogs; + parseResult.errorDueParsing = errorDueParsing; + + return parseResult; + } + + public static CmdArgs getCmdArgs(ArgParser argParser) { + CmdArgs cmdArgs = new CmdArgs(); + cmdArgs.directory = argParser.getDirectory(); + cmdArgs.classpath = argParser.getClasspath(); + cmdArgs.sourcepath = argParser.getSourcepath(); + cmdArgs.filepath = argParser.getFilepath(); + cmdArgs.filename = argParser.getFilename(); + cmdArgs.bytecodeDir = argParser.getBytecodePath(); + cmdArgs.bytecodesPaths = argParser.getBytecodesPaths(); + + return cmdArgs; + } + + public static BuildLog getBuildLog( + CompilationUnit cu, IProblem problem, String fileCounterStr) + { + MessageType messageType; + PositionInfo positionInfo = + new PositionInfo(cu, problem.getSourceStart(), problem.getSourceEnd()); + Position startPos = + new Position( + positionInfo.getStartLine(), positionInfo.getStartColumn()); + Position endPos = + new Position( + positionInfo.getEndLine(), positionInfo.getEndColumn()); + String message = problem.getMessage(); + + if (problem.isError()) { + messageType = MessageType.Error; + + LOGGER.log( + Level.SEVERE, String.join( + " ", fileCounterStr, + positionInfo.getStartLine() + ":" + positionInfo.getStartColumn(), + message + ) + ); + } else if (problem.isWarning()) { + messageType = MessageType.Warning; + + LOGGER.log( + Level.WARNING, String.join( + " ", fileCounterStr, + positionInfo.getStartLine() + ":" + positionInfo.getStartColumn(), + message + ) + ); + } else { + messageType = MessageType.Note; + + LOGGER.log( + Level.INFO, String.join( + " ", fileCounterStr, + positionInfo.getStartLine() + ":" + positionInfo.getStartColumn(), + message + ) + ); + } + + BuildLog buildLog = new BuildLog(); + buildLog.message = message; + buildLog.messageType = messageType; + buildLog.range = new Range(startPos, endPos); + + return buildLog; + } + + public static Visibility getVisibility(int modifiers) { + if (Flags.isPublic(modifiers)) { + return Visibility.PUBLIC; + } else if (Flags.isProtected(modifiers)) { + return Visibility.PROTECTED; + } else if (Flags.isPrivate(modifiers)) { + return Visibility.PRIVATE; + } + + return Visibility.PACKAGE_PRIVATE; + } + + public static String[] getParameterTypeNames(ITypeBinding[] parameters) { + String[] parameterTypeNames = new String[parameters.length]; + + for (int i = 0; i < parameters.length; i++) { + parameterTypeNames[i] = parameters[i].getQualifiedName(); + } + + return parameterTypeNames; + } + + public static String getParameterTypeNamesStr(ITypeBinding[] parameters) { + return Arrays.toString(getParameterTypeNames(parameters)); + } + + public static void setJavaRelationFields( + JavaRelation javaRelation, int leftEntityHash, + int rightEntityHash, RelationKind kind) + { + javaRelation.setLhs(leftEntityHash); + javaRelation.setRhs(rightEntityHash); + javaRelation.setKind(kind); + } + + public static void setJavaDocCommentFields( + JavaDocComment javaDocComment, String commentString, long entityHash) + { + javaDocComment.setContent(commentString); + javaDocComment.setContentHash(commentString.hashCode()); + javaDocComment.setEntityHash(entityHash); + } + + public static void setJavaRecordFields(JavaRecord javaRecord, int modifiers) { + javaRecord.setAbstract(Flags.isAbstract(modifiers)); + javaRecord.setFinal(Flags.isFinal(modifiers)); + javaRecord.setStatic(Flags.isStatic(modifiers)); + } + + public static void setJavaTypedEntityFields( + T javaTypedEntity, int modifiers, long typeHash, String qualifiedType) + { + javaTypedEntity.setFinal(Flags.isFinal(modifiers)); + javaTypedEntity.setStatic(Flags.isStatic(modifiers)); + javaTypedEntity.setTypeHash(typeHash); + javaTypedEntity.setQualifiedType(qualifiedType); + } + + public static void setJavaEntityFields( + T javaEntity, long astNodeId, long entityHash) + { + javaEntity.setAstNodeId(astNodeId); + javaEntity.setEntityHash(entityHash); + } + + public static void setJavaEntityFields( + T javaEntity, long astNodeId, + long entityHash, String name, String qualifiedName) + { + javaEntity.setAstNodeId(astNodeId); + javaEntity.setEntityHash(entityHash); + javaEntity.setName(name); + javaEntity.setQualifiedName(qualifiedName); + } + + public static void setJavaInheritanceFields( + JavaInheritance javaInheritance, int baseEntityHash, int derivedEntityHash) + { + javaInheritance.setBase(baseEntityHash); + javaInheritance.setDerived(derivedEntityHash); + } + + public static void setJavaInitializerFields( + JavaInitializer javaInitializer, int modifiers, int typeHash) + { + javaInitializer.setKind( + Flags.isStatic(modifiers) ? + InitializerKind.STATIC : + InitializerKind.INSTANCE + ); + javaInitializer.setTypeHash(typeHash); + } + + public static void setJavaMemberTypeFields( + JavaMemberType javaMemberType, int typeHash, int memberTypeHash, + MemberTypeKind memberTypeKind, int modifiers, JavaAstNode javaAstNode) + { + javaMemberType.setTypeHash(typeHash); + javaMemberType.setMemberTypeHash(memberTypeHash); + javaMemberType.setKind(memberTypeKind); + javaMemberType.setVisibility(getVisibility(modifiers)); + javaMemberType.setMemberAstNode(javaAstNode); + } + + public static void setJavaAstNodeFields( + JavaAstNode javaAstNode, String astValue, PositionInfo positionInfo, + long fileId, int entityHash, int defEntityHash, SymbolType symbolType, + AstType astType, boolean visibleInSourceCode) + { + javaAstNode.setAstValue(astValue); + javaAstNode.setLocation_range_start_line(positionInfo.getStartLine()); + javaAstNode.setLocation_range_start_column(positionInfo.getStartColumn()); + javaAstNode.setLocation_range_end_line(positionInfo.getEndLine()); + javaAstNode.setLocation_range_end_column(positionInfo.getEndColumn()); + javaAstNode.setLocation_file(fileId); + javaAstNode.setEntityHash(entityHash); + javaAstNode.setDefEntityHash(defEntityHash); + javaAstNode.setSymbolType(symbolType); + javaAstNode.setAstType(astType); + javaAstNode.setVisibleInSourceCode(visibleInSourceCode); + } + + public static String getInitializerHashStr( + String className, int startPosition) + { + return String.join(" ", className, String.valueOf(startPosition)); + } + + public static String getMethodHashStr( + ITypeBinding declaringTypeBinding, IMethodBinding methodBinding) + { + String className = getQualifiedTypeName(declaringTypeBinding); + String type = methodBinding.getReturnType().getQualifiedName(); + String name = methodBinding.getName(); + String parameters = + getParameterTypeNamesStr(methodBinding.getParameterTypes()); + + return String.join(" ", className, type, name, parameters); + } + + public static String getMethodHashStr( + ASTNode node, ITypeBinding declaringTypeBinding, + IMethodBinding methodBinding) + { + ASTNode declaringNode = findDeclaringNode(node); + String className = getQualifiedTypeName(declaringTypeBinding); + String type = methodBinding.getReturnType().getQualifiedName(); + String name = methodBinding.getName(); + String parameters = + getParameterTypeNamesStr(methodBinding.getParameterTypes()); + + if (declaringNode instanceof LambdaExpression) { + return String.join( + " ", String.valueOf(declaringNode.getStartPosition()), + className, type, name, parameters + ); + } + + return String.join(" ", className, type, name, parameters); + } + + public static String getEnumConstantHashStr( + String enumName, String name) + { + return String.join(" ", enumName, name); + } + + public static String getFieldHashStr( + String className, String type, String name) + { + return String.join(" ", className, type, name); + } + + public static String getVariableHashStr( + String methodHashStr, String type, String name) + { + return String.join(" ", methodHashStr, type, name); + } + + public static int getMainTypeHashForInnerType(ASTNode node) { + ASTNode mainType = node.getParent(); + + if (!(mainType instanceof AbstractTypeDeclaration)) { + // TODO: Anonymous class declarations + return 0; + } + + String mainTypeQualifiedName = + ((AbstractTypeDeclaration) mainType).resolveBinding() + .getQualifiedName().split("<", 2)[0]; + + return mainTypeQualifiedName.hashCode(); + } + + public static String typeParametersArrayToString( + ITypeBinding[] typeParametersArray) + { + if (typeParametersArray.length == 0) { + return ""; + } + + StringBuilder sb = new StringBuilder("<"); + + for (int i =0; i < typeParametersArray.length; ++i) { + sb.append(typeParametersArray[i].getQualifiedName()); + + if (!(i == typeParametersArray.length - 1)) { + sb.append(","); + } + } + + sb.append(">"); + + return sb.toString(); + } + + public static String getQualifiedTypeName(ITypeBinding typeBinding) { + return typeBinding.getQualifiedName() + + typeParametersArrayToString(typeBinding.getTypeParameters()); + } + + public static ASTNode findDeclaringNode(ASTNode node) { + while (node != null) { + if ( + node instanceof LambdaExpression || + node instanceof MethodDeclaration || + node instanceof AbstractTypeDeclaration || + node instanceof Initializer) + { + return node; + } + + node = node.getParent(); + } + + return null; + } +} diff --git a/plugins/java/parser/srcjava/enums/ValidCommands.java b/plugins/java/parser/srcjava/enums/ValidCommands.java new file mode 100644 index 000000000..9c6e27f2d --- /dev/null +++ b/plugins/java/parser/srcjava/enums/ValidCommands.java @@ -0,0 +1,21 @@ +package parser.srcjava.enums; + +public enum ValidCommands { + JAVAC("javac"); + /* + FUTURE PLANS: + MVN("mvn"); + etc... + */ + + private final String name; + + ValidCommands(String name) { + this.name = name; + } + + // Getter + public String getName() { + return name; + } +} diff --git a/plugins/java/service/CMakeLists.txt b/plugins/java/service/CMakeLists.txt new file mode 100644 index 000000000..ce4375c3c --- /dev/null +++ b/plugins/java/service/CMakeLists.txt @@ -0,0 +1,72 @@ +include_directories( + include + ${PROJECT_SOURCE_DIR}/util/include + ${PROJECT_SOURCE_DIR}/webserver/include + ${CMAKE_CURRENT_BINARY_DIR}/gen-cpp + ${PROJECT_SOURCE_DIR}/model/include + ${PROJECT_BINARY_DIR}/service/language/gen-cpp + ${PROJECT_BINARY_DIR}/service/project/gen-cpp + ${PROJECT_SOURCE_DIR}/service/project/include + ${PLUGIN_DIR}/model/include) + +include_directories(SYSTEM + ${THRIFT_LIBTHRIFT_INCLUDE_DIRS}) + +# Generate thrift files +add_custom_command( + OUTPUT + ${CMAKE_CURRENT_BINARY_DIR}/gen-cpp/JavaService.cpp + ${CMAKE_CURRENT_BINARY_DIR}/gen-cpp + ${CMAKE_CURRENT_BINARY_DIR}/gen-java + COMMAND + ${THRIFT_EXECUTABLE} --gen cpp --gen java + -o ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/javaservice.thrift + DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/javaservice.thrift + COMMENT + "Generating Thrift for javaservice.thrift") + +# Create cpp static library from thrift files +add_library(javaservicethrift STATIC + ${CMAKE_CURRENT_BINARY_DIR}/gen-cpp/JavaService.cpp) + +target_compile_options(javaservicethrift PUBLIC -fPIC) + +add_dependencies(javaservicethrift languagethrift commonthrift projectthrift) + +set(CMAKE_JAVA_INCLUDE_PATH + ${PROJECT_SOURCE_DIR}/lib/java/* + ${CMAKE_CURRENT_BINARY_DIR}/gen-java/ + ${PROJECT_BINARY_DIR}/service/language/gen-java + ${PROJECT_BINARY_DIR}/service/project/gen-java/) + +# Create java library from thrift files +add_jar(javaservicethriftjava + ${CMAKE_CURRENT_BINARY_DIR}/gen-java/cc/service/java/JavaService.java + OUTPUT_NAME javaservicethrift) + +add_dependencies(javaservicethriftjava javaservicethrift) + +# Src java +add_subdirectory(srcjava) + +add_library(javaservice SHARED + src/javaservice.cpp + src/plugin.cpp) + +target_compile_options(javaservice PUBLIC -Wno-unknown-pragmas) + +target_link_libraries(javaservice + util + model + mongoose + javaservicethrift + projectservice + projectthrift + languagethrift + gvc + ${THRIFT_LIBTHRIFT_LIBRARIES}) + +install(TARGETS javaservice DESTINATION ${INSTALL_SERVICE_DIR}) +install_jar(javaservicethriftjava ${INSTALL_JAVA_LIB_DIR}) diff --git a/plugins/java/service/include/service/javaservice.h b/plugins/java/service/include/service/javaservice.h new file mode 100644 index 000000000..8ade4b981 --- /dev/null +++ b/plugins/java/service/include/service/javaservice.h @@ -0,0 +1,344 @@ +#ifndef CC_SERVICE_JAVA_JAVASERVICE_H +#define CC_SERVICE_JAVA_JAVASERVICE_H + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +namespace cc +{ +namespace service +{ +namespace java +{ + +namespace language = cc::service::language; +namespace core = cc::service::core; + +using TransportException = apache::thrift::transport::TTransportException; + +class JavaQueryHandler : public JavaServiceIf +{ +public: + JavaQueryHandler() { + } + + /** + * Creates the client interface. + */ + void getClientInterface(int timeoutInMs_) + { + using Transport = apache::thrift::transport::TTransport; + using BufferedTransport = apache::thrift::transport::TBufferedTransport; + using Socket = apache::thrift::transport::TSocket; + using Protocol = apache::thrift::protocol::TBinaryProtocol; + namespace chrono = std::chrono; + + std::string host = "localhost"; + int port = 9090; + + std::shared_ptr + socket(new Socket(host, port)); + std::shared_ptr + transport(new BufferedTransport(socket)); + std::shared_ptr + protocol(new Protocol(transport)); + + // Redirect Thrift output into std::stringstream + apache::thrift::GlobalOutput.setOutputFunction( + [](const char* x) {thrift_ss << x;}); + + chrono::steady_clock::time_point begin = chrono::steady_clock::now(); + + while (!transport->isOpen()) { + try { + transport->open(); + } catch (TransportException& ex) { + chrono::steady_clock::time_point current = chrono::steady_clock::now(); + float elapsed_time = + chrono::duration_cast(current - begin).count(); + + if (elapsed_time > timeoutInMs_) { + LOG(debug) << "Connection timeout, could not reach Java server on" + << host << ":" << port; + apache::thrift::GlobalOutput.setOutputFunction( + apache::thrift::TOutput::errorTimeWrapper); + throw ex; + } + } + } + + apache::thrift::GlobalOutput.setOutputFunction( + apache::thrift::TOutput::errorTimeWrapper); + + LOG(info) << "[javaparser] Java server started!"; + + _service.reset(new JavaServiceClient(protocol)); + } + + void getAstNodeInfo( + language::AstNodeInfo& return_, + const core::AstNodeId& astNodeId_) override + { + _service -> getAstNodeInfo(return_, astNodeId_); + } + + void getAstNodeInfoByPosition( + language::AstNodeInfo& return_, + const core::FilePosition& fpos_) override + { + _service -> getAstNodeInfoByPosition(return_, fpos_); + } + + void getFileRange( + core::FileRange& return_, + const core::AstNodeId& astNodeId_) override + { + _service -> getFileRange(return_, astNodeId_); + } + + void getProperties( + std::map& return_, + const core::AstNodeId& astNodeId_) override + { + _service -> getProperties(return_, astNodeId_); + } + + void getDocumentation( + std::string& return_, + const core::AstNodeId& astNodeId_) override + { + _service -> getDocumentation(return_, astNodeId_); + } + + void getReferenceTypes( + std::map& return_, + const core::AstNodeId& astNodeId_) override + { + _service -> getReferenceTypes(return_, astNodeId_); + } + + std::int32_t getReferenceCount( + const core::AstNodeId& astNodeId_, + const std::int32_t referenceId_) override + { + return _service -> getReferenceCount(astNodeId_, referenceId_); + } + + void getReferences( + std::vector& return_, + const core::AstNodeId& astNodeId_, + const std::int32_t referenceId_, + const std::vector& tags_) override + { + _service -> getReferences(return_, astNodeId_, referenceId_, tags_); + } + + void getFileReferenceTypes( + std::map& return_) override + { + _service -> getFileReferenceTypes(return_); + } + + std::int32_t getFileReferenceCount( + const core::FileId& fileId_, + const std::int32_t referenceId_) override + { + return _service -> getFileReferenceCount(fileId_, referenceId_); + } + + void getFileReferences( + std::vector& return_, + const core::FileId& fileId_, + const std::int32_t referenceId_) override + { + _service -> getFileReferences(return_, fileId_, referenceId_); + } + + void getDiagramTypes( + std::map& return_, + const core::AstNodeId& astNodeId_ + ) override + { + _service -> getDiagramTypes(return_, astNodeId_); + } + + void getDiagram( + std::string& return_, + const core::AstNodeId& astNodeId_, + const std::int32_t diagramId_) override + { + _service -> getDiagram(return_, astNodeId_, diagramId_); + } + + void getSyntaxHighlight( + std::vector& return_, + const core::FileRange& range_, + const std::vector& content_) override + { + _service -> getSyntaxHighlight(return_, range_, content_); + } + +private: + /** + * Service interface for IPC communication. + */ + std::unique_ptr _service; + + /** + * Object to store Thrift messages during connecting to the Java server + */ + static std::stringstream thrift_ss; +}; + +} // java + +namespace language +{ + +namespace fs = boost::filesystem; +namespace pr = boost::process; +namespace pt = boost::property_tree; +using TransportException = apache::thrift::transport::TTransportException; + +class JavaServiceHandler : virtual public LanguageServiceIf +{ +public: + + JavaServiceHandler( + std::shared_ptr db_, + std::shared_ptr datadir_, + const cc::webserver::ServerContext& context_); + + std::string getRawDbContext(); + + void getFileTypes(std::vector& return_) override; + + void getAstNodeInfo( + AstNodeInfo& return_, + const core::AstNodeId& astNodeId_) override; + + void getAstNodeInfoByPosition( + AstNodeInfo& return_, + const core::FilePosition& fpos_) override; + + void getSourceText( + std::string& return_, + const core::AstNodeId& astNodeId_) override; + + void getDocumentation( + std::string& return_, + const core::AstNodeId& astNodeId_) override; + + void getProperties( + std::map& return_, + const core::AstNodeId& astNodeId_) override; + + void getDiagramTypes( + std::map& return_, + const core::AstNodeId& astNodeId_) override; + + void getDiagram( + std::string& return_, + const core::AstNodeId& astNodeId_, + const std::int32_t diagramId_) override; + + void getDiagramLegend( + std::string& return_, + const std::int32_t diagramId_) override; + + void getFileDiagramTypes( + std::map& return_, + const core::FileId& fileId_) override; + + void getFileDiagram( + std::string& return_, + const core::FileId& fileId_, + const int32_t diagramId_) override; + + void getFileDiagramLegend( + std::string& return_, + const std::int32_t diagramId_) override; + + void getReferenceTypes( + std::map& return_, + const core::AstNodeId& astNodeId) override; + + std::int32_t getReferenceCount( + const core::AstNodeId& astNodeId_, + const std::int32_t referenceId_) override; + + void getReferences( + std::vector& return_, + const core::AstNodeId& astNodeId_, + const std::int32_t referenceId_, + const std::vector& tags_) override; + + void getReferencesInFile( + std::vector& return_, + const core::AstNodeId& astNodeId_, + const std::int32_t referenceId_, + const core::FileId& fileId_, + const std::vector& tags_) override; + + void getReferencesPage( + std::vector& return_, + const core::AstNodeId& astNodeId_, + const std::int32_t referenceId_, + const std::int32_t pageSize_, + const std::int32_t pageNo_) override; + + void getFileReferenceTypes( + std::map& return_, + const core::FileId& fileId_) override; + + std::int32_t getFileReferenceCount( + const core::FileId& fileId_, + const std::int32_t referenceId_) override; + + void getFileReferences( + std::vector& return_, + const core::FileId& fileId_, + const std::int32_t referenceId_) override; + + void getSyntaxHighlight( + std::vector& return_, + const core::FileRange& range_) override; + +private: + std::shared_ptr _db; + util::OdbTransaction _transaction; + + std::shared_ptr _datadir; + const cc::webserver::ServerContext& _context; + + fs::path _java_path; + pr::child c; + cc::service::java::JavaQueryHandler javaQueryHandler; +}; + +} // language +} // service +} // cc + +#endif // CC_SERVICE_JAVA_JAVASERVICE_H diff --git a/plugins/java/service/javaservice.thrift b/plugins/java/service/javaservice.thrift new file mode 100644 index 000000000..e2fc5e260 --- /dev/null +++ b/plugins/java/service/javaservice.thrift @@ -0,0 +1,58 @@ +include "../../../service/language/language.thrift" +include "../../../service/project/common.thrift" + +namespace cpp cc.service.java +namespace java cc.service.java + +service JavaService +{ + common.FileRange getFileRange(1:common.AstNodeId astNodeId) + throws (1:common.InvalidId ex) + + language.AstNodeInfo getAstNodeInfo(1:common.AstNodeId astNodeId) + throws (1:common.InvalidId ex) + + language.AstNodeInfo getAstNodeInfoByPosition(1:common.FilePosition fpos) + throws (1:common.InvalidPos ex) + + map getProperties(1:common.AstNodeId astNodeIds) + throws (1:common.InvalidId ex) + + string getDocumentation(1:common.AstNodeId astNodeId) + throws (1:common.InvalidId ex) + + map getReferenceTypes(1:common.AstNodeId astNodeId) + throws (1:common.InvalidId ex) + + i32 getReferenceCount( + 1:common.AstNodeId astNodeId, + 2:i32 referenceId) + + list getReferences( + 1:common.AstNodeId astNodeId, + 2:i32 referenceId + 3:list tags) + throws (1:common.InvalidId ex) + + map getFileReferenceTypes() + throws (1:common.InvalidId ex) + + i32 getFileReferenceCount( + 1:common.FileId fileId, + 2:i32 referenceId) + + list getFileReferences( + 1:common.FileId fileId, + 2:i32 referenceId) + throws (1:common.InvalidId ex) + + map getDiagramTypes(1:common.AstNodeId astNodeId) + throws (1:common.InvalidId ex) + + string getDiagram(1:common.AstNodeId astNodeId, 2:i32 diagramId) + throws (1:common.InvalidId exId, 2:common.Timeout exLong) + + list getSyntaxHighlight( + 1:common.FileRange range, + 2:list content) +} \ No newline at end of file diff --git a/plugins/java/service/src/javaservice.cpp b/plugins/java/service/src/javaservice.cpp new file mode 100644 index 000000000..86724bb47 --- /dev/null +++ b/plugins/java/service/src/javaservice.cpp @@ -0,0 +1,263 @@ +#include +#include + +#include +#include + +#include + +namespace +{ + +namespace model = cc::model; + +typedef odb::query FileQuery; + +} + +namespace cc +{ +namespace service +{ +namespace language +{ + +JavaServiceHandler::JavaServiceHandler( + std::shared_ptr db_, + std::shared_ptr datadir_, + const cc::webserver::ServerContext& context_) + : _db(db_), + _transaction(db_), + _datadir(datadir_), + _context(context_) +{ + _java_path = pr::search_path("java"); + std::string raw_db_context = getRawDbContext(); + + std::vector _java_args{ + "-DrawDbContext=" + raw_db_context, + "-jar", + "../lib/java/javaservice.jar" + }; + c = pr::child(_java_path, _java_args, pr::std_out > stdout); + + try { + javaQueryHandler.getClientInterface(25000); + } catch (TransportException& ex) { + LOG(error) << "[javaservice] Starting service failed!"; + } +} + +std::string JavaServiceHandler::getRawDbContext() { + pt::ptree _pt; + pt::read_json(*_datadir + "/project_info.json", _pt); + + return _pt.get("database"); +} + +void JavaServiceHandler::getFileTypes(std::vector& return_) +{ + return_.push_back("JAVA"); + return_.push_back("Dir"); +} + +void JavaServiceHandler::getAstNodeInfo( + AstNodeInfo& return_, + const core::AstNodeId& astNodeId_) +{ + javaQueryHandler.getAstNodeInfo(return_, astNodeId_); +} + +void JavaServiceHandler::getAstNodeInfoByPosition( + AstNodeInfo& return_, + const core::FilePosition& fpos_) +{ + javaQueryHandler.getAstNodeInfoByPosition(return_, fpos_); +} + +void JavaServiceHandler::getSourceText( + std::string& return_, + const core::AstNodeId& astNodeId_) +{ + core::FileRange fileRange; + + javaQueryHandler.getFileRange(fileRange, astNodeId_); + + return_ = _transaction([&, this](){ + model::FilePtr file = _db->query_one( + FileQuery::id == std::stoull(fileRange.file)); + + if (!file) { + return std::string(); + } + + return cc::util::textRange( + file->content.load()->content, + fileRange.range.startpos.line, + fileRange.range.startpos.column, + fileRange.range.endpos.line, + fileRange.range.endpos.column); + }); +} + +void JavaServiceHandler::getProperties( + std::map& return_, + const core::AstNodeId& astNodeId_) +{ + javaQueryHandler.getProperties(return_, astNodeId_); +} + +void JavaServiceHandler::getDocumentation( + std::string& return_, + const core::AstNodeId& astNodeId_) +{ + javaQueryHandler.getDocumentation(return_, astNodeId_); +} + +void JavaServiceHandler::getDiagramTypes( + std::map& return_, + const core::AstNodeId& astNodeId_) +{ + javaQueryHandler.getDiagramTypes(return_, astNodeId_); +} + +void JavaServiceHandler::getDiagram( + std::string& return_, + const core::AstNodeId& astNodeId_, + const std::int32_t diagramId_) +{ + javaQueryHandler.getDiagram(return_, astNodeId_, diagramId_); +} + +void JavaServiceHandler::getDiagramLegend( + std::string& return_, + const std::int32_t diagramId_) +{ + LOG(info) << "getDiagramLegend"; +} + +void JavaServiceHandler::getFileDiagramTypes( + std::map& return_, + const core::FileId& fileId_) +{ + LOG(info) << "getFileDiagramTypes"; +} + +void JavaServiceHandler::getFileDiagram( + std::string& return_, + const core::FileId& fileId_, + const int32_t diagramId_) +{ + LOG(info) << "getFileDiagram"; +} + +void JavaServiceHandler::getFileDiagramLegend( + std::string& return_, + const std::int32_t diagramId_) +{ + LOG(info) << "getFileDiagramLegend"; +} + +void JavaServiceHandler::getReferenceTypes( + std::map& return_, + const core::AstNodeId& astNodeId_) +{ + javaQueryHandler.getReferenceTypes(return_, astNodeId_); +} + +std::int32_t JavaServiceHandler::getReferenceCount( + const core::AstNodeId& astNodeId_, + const std::int32_t referenceId_) +{ + return javaQueryHandler.getReferenceCount(astNodeId_, referenceId_); +} + +void JavaServiceHandler::getReferences( + std::vector& return_, + const core::AstNodeId& astNodeId_, + const std::int32_t referenceId_, + const std::vector& tags_) +{ + javaQueryHandler.getReferences(return_, astNodeId_, referenceId_, tags_); +} + +void JavaServiceHandler::getReferencesInFile( + std::vector& /* return_ */, + const core::AstNodeId& /* astNodeId_ */, + const std::int32_t /* referenceId_ */, + const core::FileId& /* fileId_ */, + const std::vector& /* tags_ */) +{ + // TODO +} + +void JavaServiceHandler::getReferencesPage( + std::vector& /* return_ */, + const core::AstNodeId& /* astNodeId_ */, + const std::int32_t /* referenceId_ */, + const std::int32_t /* pageSize_ */, + const std::int32_t /* pageNo_ */) +{ + // TODO +} + +void JavaServiceHandler::getFileReferenceTypes( + std::map& return_, + const core::FileId& /* fileId_*/) +{ + javaQueryHandler.getFileReferenceTypes(return_); +} + +std::int32_t JavaServiceHandler::getFileReferenceCount( + const core::FileId& fileId_, + const std::int32_t referenceId_) +{ + return javaQueryHandler.getFileReferenceCount(fileId_, referenceId_); +} + +void JavaServiceHandler::getFileReferences( + std::vector& return_, + const core::FileId& fileId_, + const std::int32_t referenceId_) +{ + javaQueryHandler.getFileReferences(return_, fileId_, referenceId_); +} + +void JavaServiceHandler::getSyntaxHighlight( + std::vector& return_, + const core::FileRange& range_) +{ + /* + std::vector content; + + _transaction([&, this]() { + + //--- Load the file content and break it into lines ---// + + model::FilePtr file = _db->query_one( + FileQuery::id == std::stoull(range_.file)); + + if (!file || !file->content.load()) + return; + + std::istringstream s(file->content->content); + std::string line; + while (std::getline(s, line)) + content.push_back(line); + }); + + javaQueryHandler.getSyntaxHighlight(return_, range_, content); + */ +} + +} // language + +namespace java +{ + +// Initialize static member +std::stringstream JavaQueryHandler::thrift_ss; + +} // java +} // service +} // cc diff --git a/plugins/java/service/src/plugin.cpp b/plugins/java/service/src/plugin.cpp new file mode 100644 index 000000000..3a5687f30 --- /dev/null +++ b/plugins/java/service/src/plugin.cpp @@ -0,0 +1,26 @@ +#include + +#include + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +extern "C" +{ + boost::program_options::options_description getOptions() + { + boost::program_options::options_description description("Java Plugin"); + return description; + } + + void registerPlugin( + const cc::webserver::ServerContext& context_, + cc::webserver::PluginHandler* pluginHandler_) + { + cc::webserver::registerPluginSimple( + context_, + pluginHandler_, + CODECOMPASS_LANGUAGE_SERVICE_FACTORY_WITH_CFG(Java), + "JavaService"); + } +} +#pragma clang diagnostic pop diff --git a/plugins/java/service/srcjava/CMakeLists.txt b/plugins/java/service/srcjava/CMakeLists.txt new file mode 100644 index 000000000..665797d13 --- /dev/null +++ b/plugins/java/service/srcjava/CMakeLists.txt @@ -0,0 +1,20 @@ +set(CMAKE_JAVA_INCLUDE_PATH + ${PROJECT_SOURCE_DIR}/lib/java/* + ${PLUGIN_DIR}/lib/java/*) + +set(CMAKE_JAVA_COMPILE_FLAGS + ${CMAKE_JAVA_COMPILE_FLAGS} + -sourcepath ${CMAKE_CURRENT_SOURCE_DIR}:${PLUGIN_DIR}) + +add_jar(javaservicejava + ${CMAKE_CURRENT_SOURCE_DIR}/enums/DiagramType.java + ${CMAKE_CURRENT_SOURCE_DIR}/enums/FileReferenceType.java + ${CMAKE_CURRENT_SOURCE_DIR}/enums/ReferenceType.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaQueryFactory.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaQueryHandler.java + ${CMAKE_CURRENT_SOURCE_DIR}/JavaQueryServer.java + MANIFEST ${CMAKE_CURRENT_SOURCE_DIR}/META-INF/MANIFEST.MF + OUTPUT_NAME javaservice + INCLUDE_JARS javalogger javamodel corethriftjava javaservicethriftjava) + +install_jar(javaservicejava "${INSTALL_JAVA_LIB_DIR}") diff --git a/plugins/java/service/srcjava/JavaQueryFactory.java b/plugins/java/service/srcjava/JavaQueryFactory.java new file mode 100644 index 000000000..b2835f96a --- /dev/null +++ b/plugins/java/service/srcjava/JavaQueryFactory.java @@ -0,0 +1,1174 @@ +package service.srcjava; + +import cc.service.core.FilePosition; +import cc.service.core.FileRange; +import model.*; +import model.enums.AstType; +import model.enums.MemberTypeKind; +import model.enums.RelationKind; +import model.enums.SymbolType; + +import javax.persistence.EntityManager; +import javax.persistence.NoResultException; +import javax.persistence.criteria.*; +import java.util.*; +import java.util.logging.Level; +import java.util.stream.Collectors; + +import static logger.Logger.LOGGER; + +public abstract class JavaQueryFactory { + private static final EntityManager em; + private static final CriteriaBuilder cb; + + static { + EMFactory emf = new EMFactory(System.getProperty("rawDbContext"), false); + em = emf.createEntityManager(); + cb = em.getCriteriaBuilder(); + } + + public static List queryJavaAstNodeByPosition( + FilePosition fpos) + { + long fileId = Long.parseUnsignedLong(fpos.file); + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + + int line = fpos.pos.line; + int column = fpos.pos.column; + Path locationFile = + root.get("location_file"); + Path startLine = + root.get("location_range_start_line"); + Path endLine = + root.get("location_range_end_line"); + Path startColumn = + root.get("location_range_start_column"); + Path endColumn = + root.get("location_range_end_column"); + + Predicate sameFile = cb.equal(locationFile, fileId); + Predicate startPosLessEqualPos = + cb.or( + cb.and(cb.equal(startLine, line), cb.le(startColumn, column)), + cb.lt(startLine, line) + ); + Predicate posLessThanEndPos = + cb.or( + cb.and(cb.equal(endLine, line), cb.gt(endColumn, column)), + cb.gt(endLine, line) + ); + + cr + .select(root) + .where(cb.and(sameFile, startPosLessEqualPos, posLessThanEndPos)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaAstNodesByFileRange( + FileRange fileRange) + { + long fileId = Long.parseUnsignedLong(fileRange.file); + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + + long rStartLine = fileRange.range.startpos.line; + long rEndLine = fileRange.range.endpos.line; + + Path locationFile = + root.get("location_file"); + Path startLine = + root.get("location_range_start_line"); + Path endLine = + root.get("location_range_end_line"); + Path visibility = root.get("visibleInSourceCode"); + + Predicate sameFile = cb.equal(locationFile, fileId); + Predicate startLineGreaterEqualLine = cb.ge(startLine, rStartLine); + Predicate endLineLessThanLine = cb.lt(endLine, rEndLine); + Predicate endLineNotEqualInitial = cb.notEqual(endLine, (long) -1); + Predicate visibleInSourceCode = cb.isTrue(visibility); + + cr + .select(root) + .where( + cb.and( + sameFile, startLineGreaterEqualLine, endLineLessThanLine, + endLineNotEqualInitial, visibleInSourceCode + ) + ); + + return em.createQuery(cr).getResultList(); + } + + public static JavaAstNode queryJavaAstNode(long javaAstNodeId) { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + + cr + .select(root) + .where(cb.equal(root.get("id"), javaAstNodeId)); + + return em.createQuery(cr).getSingleResult(); + } + + public static List queryJavaAstNodes(JavaAstNode javaAstNode) { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + + cr + .select(root) + .where(cb.equal(root.get("entityHash"), javaAstNode.getEntityHash())); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaAstNodes(long entityHash) { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + + cr + .select(root) + .where(cb.equal(root.get("entityHash"), entityHash)); + + return em.createQuery(cr).getResultList(); + } + + public static > + List queryJavaAstNodes(T javaEntities) { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + + cr + .select(root) + .where( + root.get("id") + .in(javaEntities.stream() + .map(JavaEntity::getAstNodeId) + .collect(Collectors.toList()) + ) + ); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaAstNodes( + JavaAstNode javaAstNode, CriteriaQuery cr, + Root root, Predicate customPredicate) + { + Predicate entityHashPredicate = cb.equal( + root.get("entityHash"), javaAstNode.getEntityHash() + ); + + cr.select(root).where(cb.and(entityHashPredicate, customPredicate)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaAstNodes( + long entityHash, CriteriaQuery cr, + Root root, Predicate customPredicate) + { + Predicate entityHashPredicate = cb.equal( + root.get("entityHash"), entityHash + ); + + cr.select(root).where(cb.and(entityHashPredicate, customPredicate)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaAstNodesFromDef( + JavaAstNode javaAstNode, CriteriaQuery cr, + Root root, Predicate customPredicate) + { + Predicate entityHashPredicate = cb.equal( + root.get("defEntityHash"), javaAstNode.getDefEntityHash() + ); + + cr.select(root).where(cb.and(entityHashPredicate, customPredicate)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaAstNodesInFile(long fileId) { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + + cr + .select(root) + .where(cb.equal(root.get("location_file"), fileId)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaAstNodesInFile( + long fileId, CriteriaQuery cr, + Root root, Predicate customPredicate) + { + Predicate entityHashPredicate = cb.equal( + root.get("location_file"), fileId + ); + + cr.select(root).where(cb.and(entityHashPredicate, customPredicate)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaImportNodesInFile(long fileId) { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + + Predicate predicate = cb.equal(root.get("symbolType"), SymbolType.FILE); + + return queryJavaAstNodesInFile(fileId, cr, root, predicate); + } + + public static List queryJavaTypeNodesInFile(long fileId) { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + + Predicate predicate = + cb.and( + cb.equal(root.get("symbolType"), SymbolType.TYPE), + cb.equal(root.get("astType"), AstType.DEFINITION) + ); + + return queryJavaAstNodesInFile(fileId, cr, root, predicate); + } + + public static List queryJavaConstructorNodesInFile(long fileId) { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + + Predicate predicate = + cb.and( + cb.equal(root.get("symbolType"), SymbolType.CONSTRUCTOR), + cb.equal(root.get("astType"), AstType.DEFINITION) + ); + + return queryJavaAstNodesInFile(fileId, cr, root, predicate); + } + + public static List queryJavaMethodNodesInFile(long fileId) { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + + Predicate predicate = + cb.and( + cb.equal(root.get("symbolType"), SymbolType.METHOD), + cb.or( + cb.equal(root.get("astType"), AstType.DEFINITION), + cb.equal(root.get("astType"), AstType.DECLARATION) + ) + ); + + return queryJavaAstNodesInFile(fileId, cr, root, predicate); + } + + public static List queryJavaMemberTypeDefinitionNodes( + JavaAstNode javaAstNode, boolean ignoreSameHashes, + MemberTypeKind... memberTypeKind) + { + List javaMemberTypes = + queryJavaMemberTypes(javaAstNode, ignoreSameHashes, memberTypeKind); + + return javaMemberTypes.stream() + .map(JavaMemberType::getMemberAstNode) + .filter( + m -> + m.getAstType() == AstType.DECLARATION || + m.getAstType() == AstType.DEFINITION) + .collect(Collectors.toList()); + } + + public static List queryDefinitionNodes(JavaAstNode javaAstNode) + { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + Predicate predicate = + cb.equal(root.get("astType"), AstType.DEFINITION); + + return queryJavaAstNodes( + javaAstNode.getDefEntityHash(), cr, root, predicate); + } + + public static List queryDefinitionNodes(long entityHash) + { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + Predicate predicate = + cb.equal(root.get("astType"), AstType.DEFINITION); + + return queryJavaAstNodes(entityHash, cr, root, predicate); + } + + public static List queryDeclOrDefNodes(JavaAstNode javaAstNode) + { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + Predicate predicate = + cb.or( + cb.equal(root.get("astType"), AstType.DECLARATION), + cb.equal(root.get("astType"), AstType.DEFINITION) + ); + + return queryJavaAstNodes(javaAstNode, cr, root, predicate); + } + + public static List queryDeclOrDefNodes(long entityHash) + { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + Predicate predicate = + cb.or( + cb.equal(root.get("astType"), AstType.DECLARATION), + cb.equal(root.get("astType"), AstType.DEFINITION) + ); + + return queryJavaAstNodes(entityHash, cr, root, predicate); + } + + public static List queryJavaMemberTypes( + JavaAstNode recordJavaAstNode) + { + CriteriaQuery cr = cb.createQuery(JavaMemberType.class); + Root root = cr.from(JavaMemberType.class); + + cr + .select(root) + .where( + cb.equal(root.get("entityHash"), recordJavaAstNode.getEntityHash()) + ); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaMemberTypes( + JavaAstNode recordJavaAstNode, CriteriaQuery cr, + Root root, Predicate customPredicate) + { + Predicate entityHashPredicate = + cb.equal(root.get("typeHash"), recordJavaAstNode.getEntityHash()); + + cr.select(root).where(cb.and(entityHashPredicate, customPredicate)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaMemberTypes( + JavaAstNode javaAstNode, JavaAstNode definition, + MemberTypeKind memberTypeKind) + { + CriteriaQuery cr = cb.createQuery(JavaMemberType.class); + Root root = cr.from(JavaMemberType.class); + + Path memberAstNodeId = root.get("memberAstNode").get("id"); + Path kind = root.get("kind"); + + Predicate idEqualNodeOrDefId = + cb.or( + cb.equal(memberAstNodeId, definition.getId()), + cb.equal(memberAstNodeId, javaAstNode.getId()) + ); + Predicate kindEqualParameter = cb.equal(kind, memberTypeKind); + + cr + .select(root) + .where(cb.and(idEqualNodeOrDefId, kindEqualParameter)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryVisibleDeclarationNodes( + JavaAstNode javaAstNode) + { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + Predicate predicate = + cb.and( + cb.equal(root.get("astType"), AstType.DECLARATION), + cb.isTrue(root.get("visibleInSourceCode")) + ); + + return queryJavaAstNodes( + javaAstNode.getDefEntityHash(), cr, root, predicate); + } + + public static List queryUsageNodes(JavaAstNode javaAstNode) { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + Predicate predicate = + cb.or( + cb.equal(root.get("astType"), AstType.USAGE), + cb.equal(root.get("astType"), AstType.READ), + cb.equal(root.get("astType"), AstType.WRITE) + ); + + if (javaAstNode.getAstType() == AstType.DEFINITION || + javaAstNode.getAstType() == AstType.DECLARATION) + { + return queryJavaAstNodesFromDef(javaAstNode, cr, root, predicate); + } + + return queryJavaAstNodes(javaAstNode, cr, root, predicate); + } + + public static List queryReadNodes(JavaAstNode javaAstNode) { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + Predicate predicate = cb.equal(root.get("astType"), AstType.READ); + + return queryJavaAstNodes(javaAstNode, cr, root, predicate); + } + + public static List queryWriteNodes(JavaAstNode javaAstNode) { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + Predicate predicate = cb.equal(root.get("astType"), AstType.WRITE); + + return queryJavaAstNodes(javaAstNode, cr, root, predicate); + } + + public static List queryCalleeNodes(JavaAstNode javaAstNode) { + List calls = queryCallNodes(javaAstNode); + + return calls.stream() + .flatMap(c -> queryDefinitionNodes(c).stream()) + .collect(Collectors.toList()); + } + + public static List queryCallerNodes(JavaAstNode javaAstNode) { + List usages = queryUsageNodes(javaAstNode); + + return usages.stream() + .map(JavaQueryFactory::queryCallerNode) + .collect(Collectors.toList()); + } + + public static JavaAstNode queryCallerNode(JavaAstNode usage) { + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + + Path astType = root.get("astType"); + Path symbolType = root.get("symbolType"); + Path locationFile = + root.get("location_file"); + Path startLine = + root.get("location_range_start_line"); + Path endLine = + root.get("location_range_end_line"); + Path startColumn = + root.get("location_range_start_column"); + Path endColumn = + root.get("location_range_end_column"); + + long uStartLine = usage.getLocation_range_start_line(); + long uEndLine = usage.getLocation_range_end_line(); + long uStartColumn = usage.getLocation_range_start_line(); + long uEndColumn = usage.getLocation_range_start_line(); + + Predicate definition = cb.equal(astType, AstType.DEFINITION); + Predicate methodOrConstructor = + cb.or( + cb.equal(symbolType, SymbolType.METHOD), + cb.equal(symbolType, SymbolType.CONSTRUCTOR) + ); + Predicate sameFile = cb.equal(locationFile, usage.getLocation_file()); + Predicate startPosLessEqualPos = + cb.or( + cb.and( + cb.equal(startLine, uStartLine), + cb.le(startColumn, uStartColumn) + ), + cb.lt(startLine, usage.getLocation_range_start_line()) + ); + Predicate posLessThanEndPos = + cb.or( + cb.and(cb.equal(endLine, uEndLine), cb.gt(endColumn, uEndColumn)), + cb.gt(endLine, uEndLine) + ); + + cr + .select(root) + .where( + cb.and( + definition, methodOrConstructor, sameFile, + startPosLessEqualPos, posLessThanEndPos + ) + ); + + List javaAstNodes = em.createQuery(cr).getResultList(); + + if (javaAstNodes.isEmpty()) { + LOGGER.log( + Level.WARNING, + "Database query result was not expected to be empty. " + + getCurrentPath() + ", line #" + getCurrentLineNumber() + ); + + return null; + } + + JavaAstNode minJavaAstNode = javaAstNodes.get(0); + + for (JavaAstNode javaAstNode : javaAstNodes) { + if (javaAstNode.isVisibleInSourceCode() && + javaAstNode.isRangeSmaller(minJavaAstNode)) { + minJavaAstNode = javaAstNode; + } + } + + return minJavaAstNode; + } + + public static List queryCallNodes(JavaAstNode javaAstNode) { + JavaAstNode definition; + + if (javaAstNode.getAstType() == AstType.DEFINITION) { + definition = javaAstNode; + } else { + List definitions = queryDefinitionNodes(javaAstNode); + + if (definitions.isEmpty()) { + return definitions; + } + + definition = definitions.get(0); + } + + CriteriaQuery cr = cb.createQuery(JavaAstNode.class); + Root root = cr.from(JavaAstNode.class); + + Path astType = root.get("astType"); + Path symbolType = root.get("symbolType"); + Path locationFile = + root.get("location_file"); + Path startLine = + root.get("location_range_start_line"); + Path endLine = + root.get("location_range_end_line"); + Path startColumn = + root.get("location_range_start_column"); + Path endColumn = + root.get("location_range_end_column"); + + long dFile = definition.getLocation_file(); + long dStartLine = definition.getLocation_range_start_line(); + long dEndLine = definition.getLocation_range_end_line(); + long dStartColumn = definition.getLocation_range_start_column(); + long dEndColumn = definition.getLocation_range_end_column(); + + Predicate usage = cb.equal(astType, AstType.USAGE); + Predicate methodOrConstructor = + cb.or( + cb.equal(symbolType, SymbolType.METHOD), + cb.equal(symbolType, SymbolType.CONSTRUCTOR) + ); + Predicate sameFile = cb.equal(locationFile, dFile); + Predicate startPosGreaterEqualPos = + cb.or( + cb.and( + cb.equal(startLine, dStartLine), + cb.ge(startColumn, dStartColumn) + ), + cb.gt(startLine, dStartLine) + ); + Predicate posGreaterEqualEndPos = + cb.or( + cb.and( + cb.equal(endLine, dEndLine), + cb.le(endColumn, dEndColumn) + ), + cb.lt(endLine, dEndLine) + ); + + cr + .select(root) + .where( + cb.and( + usage, methodOrConstructor, sameFile, + startPosGreaterEqualPos, posGreaterEqualEndPos + ) + ); + + return em.createQuery(cr).getResultList(); + } + + public static List queryParameterNodes(JavaAstNode javaAstNode) { + CriteriaQuery cr = cb.createQuery(JavaEntity.class); + Root root = cr.from(JavaEntity.class); + + cr + .select(root) + .where(cb.equal(root.get("astNodeId"), javaAstNode.getId())); + + try { + JavaEntity javaEntity = em.createQuery(cr).getSingleResult(); + + if (javaEntity instanceof JavaConstructor) { + return queryJavaAstNodes( + ((JavaConstructor) javaEntity).getJavaConVarParams()); + } else if (javaEntity instanceof JavaMethod) { + return queryJavaAstNodes( + ((JavaMethod) javaEntity).getJavaMetVarParams()); + } + } catch (NoResultException ex) { + LOGGER.log( + Level.WARNING, + "Database query result was not expected to be empty. " + + getCurrentPath() + ", line #" + getCurrentLineNumber() + ); + } + + return new ArrayList<>(); + } + + public static List queryLocalVarNodes(JavaAstNode javaAstNode) { + CriteriaQuery cr = cb.createQuery(JavaEntity.class); + Root root = cr.from(JavaEntity.class); + + cr + .select(root) + .where(cb.equal(root.get("astNodeId"), javaAstNode.getId())); + + try { + JavaEntity javaEntity = em.createQuery(cr).getSingleResult(); + + if (javaEntity instanceof JavaConstructor) { + return queryJavaAstNodes( + ((JavaConstructor) javaEntity).getJavaConVarLocals()); + } else if (javaEntity instanceof JavaMethod) { + return queryJavaAstNodes( + ((JavaMethod) javaEntity).getJavaMetVarLocals()); + } else if (javaEntity instanceof JavaInitializer) { + return queryJavaAstNodes( + ((JavaInitializer) javaEntity).getJavaInitVarLocals()); + } + } catch (NoResultException ex) { + LOGGER.log( + Level.WARNING, + "Database query result was not expected to be empty. " + + getCurrentPath() + ", line #" + getCurrentLineNumber() + ); + } + + return new ArrayList<>(); + } + + public static List queryReturnTypeNodes(JavaAstNode javaAstNode) + { + List javaMethods = queryJavaMethods(javaAstNode); + + if (!javaMethods.isEmpty()) { + JavaMethod javaMethod = javaMethods.get(0); + List javaRecords = queryJavaRecords(javaMethod); + + if (!javaRecords.isEmpty()) { + JavaRecord javaRecord = javaRecords.get(0); + + return queryDefinitionNodes(javaRecord.getEntityHash()); + } + } + + return new ArrayList<>(); + } + + public static List queryRelationNodes( + JavaAstNode javaAstNode, RelationKind relationKind, boolean reverse) + { + CriteriaQuery cr = cb.createQuery(JavaRelation.class); + Root root = cr.from(JavaRelation.class); + Predicate relationKindPredicate = cb.equal( + root.get("kind"), relationKind + ); + + List javaRelations = + queryJavaRelations(javaAstNode, reverse, cr, root, relationKindPredicate); + + return javaRelations.stream() + .flatMap( + r -> queryDeclOrDefNodes(reverse ? r.getRhs() : r.getLhs()).stream()) + .collect(Collectors.toList() + ); + } + + public static List queryTypeNodes(JavaAstNode javaAstNode) { + List javaVariables = queryJavaVariables(javaAstNode); + + if (!javaVariables.isEmpty()) { + JavaVariable javaVariable = javaVariables.get(0); + List javaRecords = queryJavaRecords(javaVariable); + + if (!javaRecords.isEmpty()) { + JavaRecord javaRecord = javaRecords.get(0); + + return queryDefinitionNodes(javaRecord.getEntityHash()); + } + } + + return new ArrayList<>(); + } + + public static List queryInheritFromNodes( + JavaAstNode javaAstNode) + { + List javaInheritances = + queryInheritancesDerived(javaAstNode); + + return javaInheritances.stream() + .flatMap(i -> queryDefinitionNodes(i.getBase()).stream()) + .collect(Collectors.toList()); + } + + public static List queryInheritedByNodes( + JavaAstNode javaAstNode) + { + List javaInheritances = + queryInheritancesBase(javaAstNode); + + return javaInheritances.stream() + .flatMap(i -> queryDefinitionNodes(i.getDerived()).stream()) + .collect(Collectors.toList()); + } + + public static List queryJavaInitializerNodes( + JavaAstNode javaAstNode) + { + CriteriaQuery cr = cb.createQuery(JavaInitializer.class); + Root root = cr.from(JavaInitializer.class); + + cr + .select(root) + .where( + cb.equal(root.get("typeHash"), javaAstNode.getEntityHash()) + ); + + List javaInitializers = em.createQuery(cr).getResultList(); + + return javaInitializers.stream() + .flatMap(i -> queryDefinitionNodes(i.getEntityHash()).stream()) + .collect(Collectors.toList()); + } + + public static List queryJavaEnumConstantNodes( + JavaAstNode javaAstNode) + { + List javaEnums = queryJavaEnums(javaAstNode); + + if (!javaEnums.isEmpty()) { + JavaEnum javaEnum = javaEnums.get(0); + Set javaEnumConstants = javaEnum.getJavaEnumConstants(); + + return javaEnumConstants.stream() + .map(c -> + queryJavaAstNode(c.getAstNodeId())).collect(Collectors.toList() + ); + } + + return new ArrayList<>(); + } + + public static List queryInheritancesDerived( + JavaAstNode javaAstNode) + { + CriteriaQuery cr = cb.createQuery(JavaInheritance.class); + Root root = cr.from(JavaInheritance.class); + + cr + .select(root) + .where(cb.equal(root.get("derived"), javaAstNode.getEntityHash())); + + return em.createQuery(cr).getResultList(); + } + + public static List queryInheritancesDerived( + JavaAstNode javaAstNode, CriteriaQuery cr, + Root root, Predicate customPredicate) + { + Predicate entityHashPredicate = cb.equal( + root.get("derived"), javaAstNode.getEntityHash() + ); + + cr.select(root).where(cb.and(entityHashPredicate, customPredicate)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryInheritancesBase( + JavaAstNode javaAstNode) + { + CriteriaQuery cr = cb.createQuery(JavaInheritance.class); + Root root = cr.from(JavaInheritance.class); + + cr + .select(root) + .where(cb.equal(root.get("base"), javaAstNode.getEntityHash())); + + return em.createQuery(cr).getResultList(); + } + + public static List queryInheritancesBase( + JavaAstNode javaAstNode, CriteriaQuery cr, + Root root, Predicate customPredicate) + { + Predicate entityHashPredicate = cb.equal( + root.get("base"), javaAstNode.getEntityHash() + ); + + cr.select(root).where(cb.and(entityHashPredicate, customPredicate)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaRecords( + JavaTypedEntity javaTypedEntity) + { + CriteriaQuery cr = cb.createQuery(JavaRecord.class); + Root root = cr.from(JavaRecord.class); + + cr + .select(root) + .where(cb.equal(root.get("entityHash"), javaTypedEntity.getTypeHash())); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaRecords(JavaAstNode javaAstNode) { + CriteriaQuery cr = cb.createQuery(JavaRecord.class); + Root root = cr.from(JavaRecord.class); + + cr + .select(root) + .where(cb.equal(root.get("entityHash"), javaAstNode.getEntityHash())); + + List javaRecords = em.createQuery(cr).getResultList(); + + if (javaRecords.isEmpty()) { + LOGGER.log( + Level.WARNING, + "Database query result was not expected to be empty. " + + getCurrentPath() + ", line #" + getCurrentLineNumber() + ); + } + + return javaRecords; + } + + public static List queryJavaRecords( + JavaAstNode javaAstNode, CriteriaQuery cr, + Root root, Predicate customPredicate) + { + Predicate entityHashPredicate = cb.equal( + root.get("entityHash"), javaAstNode.getEntityHash() + ); + + cr.select(root).where(cb.and(entityHashPredicate, customPredicate)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaEnums(JavaAstNode javaAstNode) { + CriteriaQuery cr = cb.createQuery(JavaEnum.class); + Root root = cr.from(JavaEnum.class); + + cr + .select(root) + .where(cb.equal(root.get("entityHash"), javaAstNode.getEntityHash())); + + List javaEnums = em.createQuery(cr).getResultList(); + + if (javaEnums.isEmpty()) { + LOGGER.log( + Level.WARNING, + "Database query result was not expected to be empty. " + + getCurrentPath() + ", line #" + getCurrentLineNumber() + ); + } + + return javaEnums; + } + + public static List queryJavaEnums( + JavaAstNode javaAstNode, CriteriaQuery cr, + Root root, Predicate customPredicate) + { + Predicate entityHashPredicate = cb.equal( + root.get("entityHash"), javaAstNode.getEntityHash() + ); + + cr.select(root).where(cb.and(entityHashPredicate, customPredicate)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaRelations( + JavaAstNode javaAstNode, boolean reverse) + { + CriteriaQuery cr = cb.createQuery(JavaRelation.class); + Root root = cr.from(JavaRelation.class); + + Path entityHash = reverse ? root.get("lhs") : root.get("rhs"); + + cr + .select(root) + .where(cb.equal(entityHash, javaAstNode.getEntityHash())); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaRelations( + JavaAstNode javaAstNode, boolean reverse, + CriteriaQuery cr, + Root root, Predicate customPredicate) + { + Path entityHash = reverse ? root.get("lhs") : root.get("rhs"); + + Predicate entityHashPredicate = cb.equal( + entityHash, javaAstNode.getEntityHash() + ); + + cr.select(root).where(cb.and(entityHashPredicate, customPredicate)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaMethods(JavaAstNode javaAstNode) { + CriteriaQuery cr = cb.createQuery(JavaMethod.class); + Root root = cr.from(JavaMethod.class); + + cr + .select(root) + .where(cb.equal(root.get("entityHash"), javaAstNode.getEntityHash())); + + List javaMethods = em.createQuery(cr).getResultList(); + + if (javaMethods.isEmpty()) { + LOGGER.log( + Level.WARNING, + "Database query result was not expected to be empty. " + + getCurrentPath() + ", line #" + getCurrentLineNumber() + ); + } + + return javaMethods; + } + + public static List queryJavaMethods( + JavaAstNode javaAstNode, CriteriaQuery cr, + Root root, Predicate customPredicate) + { + Predicate entityHashPredicate = cb.equal( + root.get("entityHash"), javaAstNode.getEntityHash() + ); + + cr.select(root).where(cb.and(entityHashPredicate, customPredicate)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaVariables(JavaAstNode javaAstNode) { + CriteriaQuery cr = cb.createQuery(JavaVariable.class); + Root root = cr.from(JavaVariable.class); + + cr + .select(root) + .where(cb.equal(root.get("entityHash"), javaAstNode.getEntityHash())); + + List javaVariables = em.createQuery(cr).getResultList(); + + if (javaVariables.isEmpty()) { + LOGGER.log( + Level.WARNING, + "Database query result was not expected to be empty. " + + getCurrentPath() + ", line #" + getCurrentLineNumber() + ); + } + + return javaVariables; + } + + public static List queryJavaVariables( + JavaAstNode javaAstNode, CriteriaQuery cr, + Root root, Predicate customPredicate) + { + Predicate entityHashPredicate = cb.equal( + root.get("entityHash"), javaAstNode.getEntityHash() + ); + + cr.select(root).where(cb.and(entityHashPredicate, customPredicate)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaConstructors( + JavaAstNode javaAstNode) + { + CriteriaQuery cr = + cb.createQuery(JavaConstructor.class); + Root root = cr.from(JavaConstructor.class); + + cr + .select(root) + .where(cb.equal(root.get("entityHash"), javaAstNode.getEntityHash())); + + List javaConstructors = em.createQuery(cr).getResultList(); + + if (javaConstructors.isEmpty()) { + LOGGER.log( + Level.WARNING, + "Database query result was not expected to be empty. " + + getCurrentPath() + ", line #" + getCurrentLineNumber() + ); + } + + return javaConstructors; + } + + public static List queryJavaConstructors( + JavaAstNode javaAstNode, CriteriaQuery cr, + Root root, Predicate customPredicate) + { + Predicate entityHashPredicate = cb.equal( + root.get("entityHash"), javaAstNode.getEntityHash() + ); + + cr.select(root).where(cb.and(entityHashPredicate, customPredicate)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaEnumConstants( + JavaAstNode javaAstNode) + { + CriteriaQuery cr = + cb.createQuery(JavaEnumConstant.class); + Root root = cr.from(JavaEnumConstant.class); + + cr + .select(root) + .where(cb.equal(root.get("entityHash"), javaAstNode.getEntityHash())); + + List javaEnumConstants = + em.createQuery(cr).getResultList(); + + if (javaEnumConstants.isEmpty()) { + LOGGER.log( + Level.WARNING, + "Database query result was not expected to be empty. " + + getCurrentPath() + ", line #" + getCurrentLineNumber() + ); + } + + return javaEnumConstants; + } + + public static List queryJavaEnumConstants( + JavaAstNode javaAstNode, CriteriaQuery cr, + Root root, Predicate customPredicate) + { + Predicate entityHashPredicate = cb.equal( + root.get("entityHash"), javaAstNode.getEntityHash() + ); + + cr.select(root).where(cb.and(entityHashPredicate, customPredicate)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaInitializers( + JavaAstNode javaAstNode) + { + CriteriaQuery cr = + cb.createQuery(JavaInitializer.class); + Root root = cr.from(JavaInitializer.class); + + cr + .select(root) + .where(cb.equal(root.get("entityHash"), javaAstNode.getEntityHash())); + + List javaInitializers = + em.createQuery(cr).getResultList(); + + if (javaInitializers.isEmpty()) { + LOGGER.log( + Level.WARNING, + "Database query result was not expected to be empty. " + + getCurrentPath() + ", line #" + getCurrentLineNumber() + ); + } + + return javaInitializers; + } + + public static List queryJavaInitializers( + JavaAstNode javaAstNode, CriteriaQuery cr, + Root root, Predicate customPredicate) + { + Predicate entityHashPredicate = cb.equal( + root.get("entityHash"), javaAstNode.getEntityHash() + ); + + cr.select(root).where(cb.and(entityHashPredicate, customPredicate)); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaMemberTypes( + JavaAstNode recordJavaAstNode, boolean ignoreSameHash, + MemberTypeKind... memberTypeKinds) + { + CriteriaQuery cr = cb.createQuery(JavaMemberType.class); + Root root = cr.from(JavaMemberType.class); + Predicate predicate; + + if (ignoreSameHash) { + predicate = + cb.and( + root.get("kind").in((Object[]) memberTypeKinds), + cb.notEqual(root.get("memberTypeHash"), root.get("typeHash")) + ); + } else { + predicate = root.get("kind").in((Object[]) memberTypeKinds); + } + + return queryJavaMemberTypes(recordJavaAstNode, cr, root, predicate); + } + + public static List queryJavaDocComments( + JavaAstNode javaAstNode) + { + CriteriaQuery cr = + cb.createQuery(JavaDocComment.class); + Root root = cr.from(JavaDocComment.class); + + cr + .select(root) + .where(cb.equal(root.get("entityHash"), javaAstNode.getEntityHash())); + + return em.createQuery(cr).getResultList(); + } + + public static List queryJavaDocComments( + long entityHash) + { + CriteriaQuery cr = + cb.createQuery(JavaDocComment.class); + Root root = cr.from(JavaDocComment.class); + + cr + .select(root) + .where(cb.equal(root.get("entityHash"), entityHash)); + + return em.createQuery(cr).getResultList(); + } + + private static String getCurrentPath() { + return new Throwable().getStackTrace()[1].getFileName(); + } + + private static int getCurrentLineNumber() { + return new Throwable().getStackTrace()[1].getLineNumber(); + } +} diff --git a/plugins/java/service/srcjava/JavaQueryHandler.java b/plugins/java/service/srcjava/JavaQueryHandler.java new file mode 100644 index 000000000..34d466962 --- /dev/null +++ b/plugins/java/service/srcjava/JavaQueryHandler.java @@ -0,0 +1,815 @@ +package service.srcjava; + +import cc.service.core.*; +import cc.service.java.JavaService; +import cc.service.language.AstNodeInfo; +import cc.service.language.SyntaxHighlight; +import model.*; +import model.enums.MemberTypeKind; +import model.enums.RelationKind; +import org.apache.thrift.TException; +import service.srcjava.enums.DiagramType; +import service.srcjava.enums.FileReferenceType; +import service.srcjava.enums.ReferenceType; + +import java.util.*; +import java.util.stream.Collectors; + +import static service.srcjava.JavaQueryFactory.*; + +public class JavaQueryHandler implements JavaService.Iface { + @Override + public FileRange getFileRange(String javaAstNodeId) { + JavaAstNode javaAstNode = queryJavaAstNode(Long.parseLong(javaAstNodeId)); + + return getFileRange(javaAstNode); + } + + @Override + public AstNodeInfo getAstNodeInfo(String javaAstNodeId) { + JavaAstNode javaAstNode = queryJavaAstNode(Long.parseLong(javaAstNodeId)); + + return createAstNodeInfo(javaAstNode); + } + + @Override + public AstNodeInfo getAstNodeInfoByPosition(FilePosition fpos) + throws TException + { + List javaAstNodes = queryJavaAstNodeByPosition(fpos); + + if (javaAstNodes.isEmpty()) { + InvalidPos ex = new InvalidPos(); + ex.msg = "There are no any JavaAstNode at this position."; + ex.fpos = fpos; + + throw ex; + } + + JavaAstNode minJavaAstNode = javaAstNodes.get(0); + + for (JavaAstNode javaAstNode : javaAstNodes) { + if (javaAstNode.isVisibleInSourceCode() && + javaAstNode.isRangeSmaller(minJavaAstNode)) { + minJavaAstNode = javaAstNode; + } + } + + return createAstNodeInfo( + minJavaAstNode, getTags(Collections.singletonList(minJavaAstNode)) + ); + } + + @Override + public Map getProperties(String javaAstNodeId) { + Map properties = new HashMap<>(); + long javaAstNodeIdLong = Long.parseLong(javaAstNodeId); + JavaAstNode javaAstNode = queryJavaAstNode(javaAstNodeIdLong); + + switch (javaAstNode.getSymbolType()){ + case VARIABLE: { + List javaVariables = queryJavaVariables(javaAstNode); + + if (!javaVariables.isEmpty()) { + JavaVariable javaVariable = javaVariables.get(0); + + properties.put("Final variable", + Boolean.toString(javaVariable.isFinal())); + properties.put("Static variable", + Boolean.toString(javaVariable.isStatic())); + properties.put("Name", javaVariable.getName()); + properties.put("Qualified name", javaVariable.getQualifiedName()); + properties.put("Type", javaVariable.getQualifiedType()); + + return properties; + } + break; + } + case CONSTRUCTOR: { + List javaConstructors = + queryJavaConstructors(javaAstNode); + + if (!javaConstructors.isEmpty()) { + JavaConstructor javaConstructor = javaConstructors.get(0); + + properties.put("Name", javaConstructor.getName()); + properties.put("Qualified name", javaConstructor.getQualifiedName()); + + return properties; + } + break; + } + case METHOD: { + List javaMethods = queryJavaMethods(javaAstNode); + + if (!javaMethods.isEmpty()) { + JavaMethod javaMethod = javaMethods.get(0); + + properties.put("Final method", + Boolean.toString(javaMethod.isFinal())); + properties.put("Static method", + Boolean.toString(javaMethod.isStatic())); + properties.put("Name", javaMethod.getName()); + properties.put("Qualified name", javaMethod.getQualifiedName()); + properties.put("Type", javaMethod.getQualifiedType()); + + return properties; + } + break; + } + case TYPE: { + List javaRecords = queryJavaRecords(javaAstNode); + + if (!javaRecords.isEmpty()) { + JavaRecord javaRecord = javaRecords.get(0); + + properties.put("Abstract type", + Boolean.toString(javaRecord.isAbstract())); + properties.put("Final type", + Boolean.toString(javaRecord.isFinal())); + properties.put("Static type", + Boolean.toString(javaRecord.isStatic())); + properties.put("Name", javaRecord.getName()); + properties.put("Qualified name", javaRecord.getQualifiedName()); + + return properties; + } + break; + } + case ENUM: { + List javaEnums = queryJavaEnums(javaAstNode); + + if (!javaEnums.isEmpty()) { + JavaEnum javaEnum = javaEnums.get(0); + + properties.put("Name", javaEnum.getName()); + properties.put("Qualified name", javaEnum.getQualifiedName()); + } + break; + } + case ENUM_CONSTANT: { + List javaEnumConstants = + queryJavaEnumConstants(javaAstNode); + + if (!javaEnumConstants.isEmpty()) { + JavaEnumConstant javaEnumConstant = javaEnumConstants.get(0); + + properties.put("Name", javaEnumConstant.getName()); + properties.put("Qualified name", javaEnumConstant.getQualifiedName()); + properties.put( + "Value", Integer.toString(javaEnumConstant.getValue()) + ); + + return properties; + } + break; + } + case INITIALIZER: { + List javaInitializers = + queryJavaInitializers(javaAstNode); + + if (!javaInitializers.isEmpty()) { + JavaInitializer javaInitializer = javaInitializers.get(0); + + properties.put("Kind", javaInitializer.getKind().getName()); + } + break; + } + } + + return properties; + } + + @Override + public String getDocumentation(String javaAstNodeId) { + StringBuilder sb = new StringBuilder(); + + long javaAstNodeIdLong = Long.parseLong(javaAstNodeId); + JavaAstNode javaAstNode = queryJavaAstNode(javaAstNodeIdLong); + + List javaDocComments = queryJavaDocComments(javaAstNode); + + if (!javaDocComments.isEmpty()) { + sb + .append("
") + .append(javaDocComments.get(0).getContent() + .replace("\n", "
")) + .append("
"); + } + + switch (javaAstNode.getSymbolType()) { + case TYPE: + case ENUM: + //--- Data members ---// + + List constructors = + getReferences( + javaAstNodeId, ReferenceType.CONSTRUCTOR.ordinal(), + new ArrayList<>() + ); + List methods = + getReferences( + javaAstNodeId, ReferenceType.METHOD.ordinal(), new ArrayList<>() + ); + + + getDocumentationForAstNodeInfos(sb, constructors); + getDocumentationForAstNodeInfos(sb, methods); + break; + } + + return sb.toString(); + } + + private void getDocumentationForAstNodeInfos( + StringBuilder sb, List astNodeInfos) + { + astNodeInfos.forEach(nodeInfo -> { + sb + .append("
"); + + //--- Add tags ---/ + + nodeInfo.tags.forEach(tag -> { + if (tag.equals("public") || + tag.equals("private") || + tag.equals("protected")) + { + sb + .append(""); + } else { + sb + .append("") + .append(Character.toUpperCase(tag.charAt(0))) + .append(""); + } + }); + + sb + .append( + nodeInfo.astNodeValue + .replace("\n", "
")) + .append("
"); + + //--- Query documentation of members ---// + + List memberDocComments = + queryJavaDocComments(nodeInfo.entityHash); + + if (!memberDocComments.isEmpty()) + sb + .append( + memberDocComments.get(0).getContent() + .replace("\n", "
") + ); + + sb.append("
"); + }); + } + + @Override + public Map getReferenceTypes(String javaAstNodeId) { + long javaAstNodeIdLong = Long.parseLong(javaAstNodeId); + HashMap referenceTypes = new HashMap<>(); + JavaAstNode javaAstNode = queryJavaAstNode(javaAstNodeIdLong); + + referenceTypes.put("Definition", ReferenceType.DEFINITION.ordinal()); + referenceTypes.put("Declaration", ReferenceType.DECLARATION.ordinal()); + referenceTypes.put("Usage", ReferenceType.USAGE.ordinal()); + + switch (javaAstNode.getSymbolType()) { + case CONSTRUCTOR: + referenceTypes.put( + "This calls", ReferenceType.THIS_CALLS.ordinal()); + referenceTypes.put( + "Callee", ReferenceType.CALLEE.ordinal()); + referenceTypes.put( + "Caller", ReferenceType.CALLER.ordinal()); + referenceTypes.put( + "Parameters", ReferenceType.PARAMETER.ordinal()); + referenceTypes.put( + "Local variables", ReferenceType.LOCAL_VAR.ordinal()); + break; + case METHOD: + referenceTypes.put( + "This calls", ReferenceType.THIS_CALLS.ordinal()); + referenceTypes.put( + "Callee", ReferenceType.CALLEE.ordinal()); + referenceTypes.put( + "Caller", ReferenceType.CALLER.ordinal()); + referenceTypes.put( + "Parameters", ReferenceType.PARAMETER.ordinal()); + referenceTypes.put( + "Local variables", ReferenceType.LOCAL_VAR.ordinal()); + referenceTypes.put( + "Overrides", ReferenceType.OVERRIDE.ordinal()); + referenceTypes.put( + "Overridden by", ReferenceType.OVERRIDDEN_BY.ordinal()); + referenceTypes.put( + "Implements", ReferenceType.IMPLEMENT.ordinal()); + referenceTypes.put( + "Implemented by", ReferenceType.IMPLEMENTED_BY.ordinal()); + referenceTypes.put( + "Return type", ReferenceType.RETURN_TYPE.ordinal()); + break; + case VARIABLE: + referenceTypes.put( + "Reads", ReferenceType.READ.ordinal()); + referenceTypes.put( + "Writes", ReferenceType.WRITE.ordinal()); + referenceTypes.put( + "Type", ReferenceType.TYPE.ordinal()); + break; + case TYPE: + referenceTypes.put( + "Inherits from", ReferenceType.INHERIT_FROM.ordinal()); + referenceTypes.put( + "Inherited by", ReferenceType.INHERIT_BY.ordinal()); + referenceTypes.put( + "Initializer", ReferenceType.INITIALIZER.ordinal()); + referenceTypes.put( + "Inner type", ReferenceType.INNER_TYPE.ordinal()); + referenceTypes.put( + "Constructor", ReferenceType.CONSTRUCTOR.ordinal()); + referenceTypes.put( + "Data member", ReferenceType.DATA_MEMBER.ordinal()); + referenceTypes.put( + "Method", ReferenceType.METHOD.ordinal()); + break; + case ENUM: + referenceTypes.put( + "Inherits from", ReferenceType.INHERIT_FROM.ordinal()); + referenceTypes.put( + "Initializer", ReferenceType.INITIALIZER.ordinal()); + referenceTypes.put( + "Inner type", ReferenceType.INNER_TYPE.ordinal()); + referenceTypes.put( + "Enum constants", ReferenceType.ENUM_CONSTANTS.ordinal()); + referenceTypes.put( + "Constructor", ReferenceType.CONSTRUCTOR.ordinal()); + referenceTypes.put( + "Data member", ReferenceType.DATA_MEMBER.ordinal()); + referenceTypes.put( + "Method", ReferenceType.METHOD.ordinal()); + break; + case INITIALIZER: + referenceTypes.put( + "Local variables", ReferenceType.LOCAL_VAR.ordinal()); + } + + return referenceTypes; + } + + @Override + public int getReferenceCount(String javaAstNodeId, int referenceId) { + long javaAstNodeIdLong = Long.parseLong(javaAstNodeId); + JavaAstNode javaAstNode = queryJavaAstNode(javaAstNodeIdLong); + + switch (ReferenceType.values()[referenceId]) { + case DEFINITION: + return queryDefinitionNodes(javaAstNode).size(); + case DECLARATION: + return queryVisibleDeclarationNodes(javaAstNode).size(); + case USAGE: + return queryUsageNodes(javaAstNode).size(); + case THIS_CALLS: + return queryCallNodes(javaAstNode).size(); + case CALLEE: + return queryCalleeNodes(javaAstNode).size(); + case CALLER: + return queryCallerNodes(javaAstNode).size(); + case PARAMETER: + return queryParameterNodes(javaAstNode).size(); + case LOCAL_VAR: + return queryLocalVarNodes(javaAstNode).size(); + case RETURN_TYPE: + return queryReturnTypeNodes(javaAstNode).size(); + case OVERRIDE: + return queryRelationNodes( + javaAstNode, RelationKind.OVERRIDE, false).size(); + case OVERRIDDEN_BY: + return queryRelationNodes( + javaAstNode, RelationKind.OVERRIDE, true).size(); + case IMPLEMENT: + return queryRelationNodes( + javaAstNode, RelationKind.IMPLEMENT, false).size(); + case IMPLEMENTED_BY: + return queryRelationNodes( + javaAstNode, RelationKind.IMPLEMENT, true).size(); + case READ: + return queryReadNodes(javaAstNode).size(); + case WRITE: + return queryWriteNodes(javaAstNode).size(); + case TYPE: + return queryTypeNodes(javaAstNode).size(); + case INHERIT_FROM: + return queryInheritFromNodes(javaAstNode).size(); + case INHERIT_BY: + return queryInheritedByNodes(javaAstNode).size(); + case INNER_TYPE: + return queryJavaMemberTypeDefinitionNodes( + javaAstNode, true, + MemberTypeKind.TYPE, MemberTypeKind.ENUM + ).size(); + case INITIALIZER: + return queryJavaInitializerNodes(javaAstNode).size(); + case CONSTRUCTOR: + return queryJavaMemberTypeDefinitionNodes( + javaAstNode, false, MemberTypeKind.CONSTRUCTOR).size(); + case DATA_MEMBER: + return queryJavaMemberTypeDefinitionNodes( + javaAstNode, false, MemberTypeKind.FIELD).size(); + case METHOD: + return queryJavaMemberTypeDefinitionNodes( + javaAstNode, false, MemberTypeKind.METHOD).size(); + case ENUM_CONSTANTS: + return queryJavaEnumConstantNodes(javaAstNode).size(); + } + + return 0; + } + + @Override + public List getReferences( + String javaAstNodeId, int referenceId, List tags) + { + long javaAstNodeIdLong = Long.parseLong(javaAstNodeId); + JavaAstNode javaAstNode = queryJavaAstNode(javaAstNodeIdLong); + List javaAstNodes = new ArrayList<>(); + List javaAstNodeInfos; + + switch (ReferenceType.values()[referenceId]) { + case DEFINITION: + javaAstNodes = queryDefinitionNodes(javaAstNode); + break; + case DECLARATION: + javaAstNodes = queryVisibleDeclarationNodes(javaAstNode); + break; + case USAGE: + javaAstNodes = queryUsageNodes(javaAstNode); + break; + case THIS_CALLS: + javaAstNodes = queryCallNodes(javaAstNode); + break; + case CALLEE: + javaAstNodes = queryCalleeNodes(javaAstNode); + break; + case CALLER: + javaAstNodes = queryCallerNodes(javaAstNode); + break; + case PARAMETER: + javaAstNodes = queryParameterNodes(javaAstNode); + break; + case LOCAL_VAR: + javaAstNodes = queryLocalVarNodes(javaAstNode); + break; + case RETURN_TYPE: + javaAstNodes = queryReturnTypeNodes(javaAstNode); + break; + case OVERRIDE: + javaAstNodes = queryRelationNodes( + javaAstNode, RelationKind.OVERRIDE, false); + break; + case OVERRIDDEN_BY: + javaAstNodes = queryRelationNodes( + javaAstNode, RelationKind.OVERRIDE, true); + break; + case IMPLEMENT: + javaAstNodes = queryRelationNodes( + javaAstNode, RelationKind.IMPLEMENT, false); + break; + case IMPLEMENTED_BY: + javaAstNodes = queryRelationNodes( + javaAstNode, RelationKind.IMPLEMENT, true); + break; + case READ: + javaAstNodes = queryReadNodes(javaAstNode); + break; + case WRITE: + javaAstNodes = queryWriteNodes(javaAstNode); + break; + case TYPE: + javaAstNodes = queryTypeNodes(javaAstNode); + break; + case INHERIT_FROM: + javaAstNodes = queryInheritFromNodes(javaAstNode); + break; + case INHERIT_BY: + javaAstNodes = queryInheritedByNodes(javaAstNode); + break; + case INNER_TYPE: + javaAstNodes = + queryJavaMemberTypeDefinitionNodes( + javaAstNode, true, + MemberTypeKind.TYPE, MemberTypeKind.ENUM + ); + break; + case INITIALIZER: + javaAstNodes = queryJavaInitializerNodes(javaAstNode); + break; + case CONSTRUCTOR: + javaAstNodes = + queryJavaMemberTypeDefinitionNodes( + javaAstNode, false, MemberTypeKind.CONSTRUCTOR); + break; + case DATA_MEMBER: + javaAstNodes = + queryJavaMemberTypeDefinitionNodes( + javaAstNode, false, MemberTypeKind.FIELD); + break; + case METHOD: + javaAstNodes = + queryJavaMemberTypeDefinitionNodes( + javaAstNode, false, MemberTypeKind.METHOD); + break; + case ENUM_CONSTANTS: + javaAstNodes = queryJavaEnumConstantNodes(javaAstNode); + break; + } + + javaAstNodeInfos = createAstNodeInfos(javaAstNodes); + + return javaAstNodeInfos; + } + + @Override + public Map getFileReferenceTypes() { + Map fileReferenceTypes = new HashMap<>(); + + fileReferenceTypes.put("Import", FileReferenceType.IMPORTS.ordinal()); + fileReferenceTypes.put("Type", FileReferenceType.TYPES.ordinal()); + fileReferenceTypes.put("Constructor", + FileReferenceType.CONSTRUCTORS.ordinal()); + fileReferenceTypes.put("Method", FileReferenceType.METHODS.ordinal()); + + return fileReferenceTypes; + } + + @Override + public int getFileReferenceCount(String fileId, int referenceId) { + long fileIdLong = Long.parseUnsignedLong(fileId); + + switch (FileReferenceType.values()[referenceId]) { + case IMPORTS: + return queryJavaImportNodesInFile(fileIdLong).size(); + case TYPES: + return queryJavaTypeNodesInFile(fileIdLong).size(); + case CONSTRUCTORS: + return queryJavaConstructorNodesInFile(fileIdLong).size(); + case METHODS: + return queryJavaMethodNodesInFile(fileIdLong).size(); + } + + return 0; + } + + @Override + public List getFileReferences(String fileId, int referenceId) { + long fileIdLong = Long.parseUnsignedLong(fileId); + List javaAstNodes = new ArrayList<>(); + List javaAstNodeInfos; + + switch (FileReferenceType.values()[referenceId]) { + case IMPORTS: + javaAstNodes = queryJavaImportNodesInFile(fileIdLong); + break; + case TYPES: + javaAstNodes = queryJavaTypeNodesInFile(fileIdLong); + break; + case CONSTRUCTORS: + javaAstNodes = queryJavaConstructorNodesInFile(fileIdLong); + break; + case METHODS: + javaAstNodes = queryJavaMethodNodesInFile(fileIdLong); + break; + } + + javaAstNodeInfos = createAstNodeInfos(javaAstNodes); + + return javaAstNodeInfos; + } + + @Override + public Map getDiagramTypes(String javaAstNodeId) { + JavaAstNode javaAstNode = queryJavaAstNode(Long.parseLong(javaAstNodeId)); + HashMap diagramTypes = new HashMap<>(); + + switch (javaAstNode.getSymbolType()) + { + case CONSTRUCTOR: + case METHOD: + diagramTypes.put( + "Method call diagram", DiagramType.METHOD_CALL.ordinal()); + break; + case TYPE: + case ENUM: + diagramTypes.put( + "Detailed class diagram", DiagramType.DETAILED_CLASS.ordinal() + ); + diagramTypes.put( + "Class collaboration diagram", + DiagramType.CLASS_COLLABORATION.ordinal() + ); + break; + } + + return diagramTypes; + } + + @Override + public String getDiagram(String javaAstNodeId, int diagramId) { + JavaAstNode javaAstNode = queryJavaAstNode(Long.parseLong(javaAstNodeId)); + + switch (DiagramType.values()[diagramId]) + { + case METHOD_CALL: + break; + + case DETAILED_CLASS: + break; + + case CLASS_COLLABORATION: + break; + } + + return ""; + } + + @Override + public List getSyntaxHighlight( + FileRange fileRange, List content) + { + List syntaxHighlights = new ArrayList<>(); + /* + Pattern specialChars = Pattern.compile("[-\\[\\]{}()*+?.,\\^$|#\\s]"); + List javaAstNodes = queryJavaAstNodesByFileRange(fileRange); + + javaAstNodes.stream() + .filter(n -> !n.getAstValue().isEmpty()) + .forEach(n -> { + Matcher matcher = specialChars.matcher(n.getAstValue()); + String sanitizedAstValue = + matcher.replaceAll(matchResult -> "\\\\" + matchResult.group()); + String reg = "\\b" + sanitizedAstValue + "\\b"; + int startLine = (int) n.getLocation_range_start_line(); + int endLine = (int) n.getLocation_range_end_line(); + + for (int i = startLine - 1; i < endLine && i < content.size(); ++i) { + Pattern wordPattern = Pattern.compile(reg); + Matcher wordMatcher = wordPattern.matcher(content.get(i)); + + while (wordMatcher.find()) { + MatchResult matchResult = wordMatcher.toMatchResult(); + SyntaxHighlight syntax = new SyntaxHighlight(); + Range range = new Range(); + Position startPosition = new Position(); + Position endPosition = new Position(); + + startPosition.line = i + 1; + startPosition.column = matchResult.start() + 1; + endPosition.line = i + 1; + endPosition.column = matchResult.end() + 1; + + range.startpos = startPosition; + range.endpos = endPosition; + + syntax.range = range; + + String symbolClass = "cm-" + n.getSymbolType().getValue(); + + syntax.className = symbolClass + " " + + symbolClass + "-" + n.getAstType().getValue(); + + syntaxHighlights.add(syntax); + } + } + }); + */ + return syntaxHighlights; + } + + private List createAstNodeInfos(List javaAstNodes) { + Map> tags = getTags(javaAstNodes); + + return javaAstNodes.stream() + .map(p -> createAstNodeInfo(p, tags)) + .sorted((n1, n2) -> { + Integer line1 = n1.range.range.startpos.line; + Integer line2 = n2.range.range.startpos.line; + int lineComp = line1.compareTo(line2); + + if (lineComp != 0) { + return lineComp; + } + + Integer col1 = n1.range.range.startpos.column; + Integer col2 = n2.range.range.startpos.column; + return col1.compareTo(col2); + }) + .collect(Collectors.toList()); + } + + private AstNodeInfo createAstNodeInfo(JavaAstNode javaAstNode) { + AstNodeInfo astNodeInfo = new AstNodeInfo(); + FileRange fileRange = getFileRange(javaAstNode); + + astNodeInfo.id = String.valueOf(javaAstNode.getId()); + astNodeInfo.entityHash = javaAstNode.getEntityHash(); + astNodeInfo.astNodeType = javaAstNode.getAstType().getName(); + astNodeInfo.symbolType = javaAstNode.getSymbolType().getName(); + astNodeInfo.astNodeValue = javaAstNode.getAstValue(); + astNodeInfo.range = fileRange; + + return astNodeInfo; + } + + private AstNodeInfo createAstNodeInfo( + JavaAstNode javaAstNode, Map> tags) + { + AstNodeInfo astNodeInfo = createAstNodeInfo(javaAstNode); + + astNodeInfo.tags = tags.get(javaAstNode.getId()); + + return astNodeInfo; + } + + private Map> getTags(List javaAstNodes) { + Map> tags = new HashMap<>(); + + javaAstNodes.forEach(node -> { + List definitions = queryDefinitionNodes(node); + JavaAstNode definition = + definitions.isEmpty() ? node : definitions.get(0); + + switch (node.getSymbolType()) { + case TYPE: + putTags(node, definition, MemberTypeKind.TYPE, tags); + break; + case CONSTRUCTOR: + putTags(node, definition, MemberTypeKind.CONSTRUCTOR, tags); + break; + case VARIABLE: + putTags(node, definition, MemberTypeKind.FIELD, tags); + break; + case METHOD: + putTags(node, definition, MemberTypeKind.METHOD, tags); + break; + case ENUM: + putTags(node, definition, MemberTypeKind.ENUM, tags); + break; + case ENUM_CONSTANT: + putTags(node, definition, MemberTypeKind.ENUM_CONSTANT, tags); + break; + } + }); + + return tags; + } + + private void putTags( + JavaAstNode javaAstNode, JavaAstNode definition, + MemberTypeKind memberTypeKind, Map> tags) + { + List javaMemberTypes = + queryJavaMemberTypes(javaAstNode, definition, memberTypeKind); + + javaMemberTypes.forEach(m -> { + long nodeId = javaAstNode.getId(); + if (!tags.containsKey(nodeId)) { + tags.put( + nodeId, + new ArrayList<>( + Collections.singleton(m.getVisibility().getName()) + ) + ); + } else { + tags.get(nodeId).add(m.getVisibility().getName()); + } + }); + } + + private FileRange getFileRange(JavaAstNode javaAstNode) { + FileRange fileRange = new FileRange(); + Range range = new Range(); + Position startPosition = new Position( + (int) javaAstNode.getLocation_range_start_line(), + (int) javaAstNode.getLocation_range_start_column() + ); + Position endPosition = new Position( + (int) javaAstNode.getLocation_range_end_line(), + (int) javaAstNode.getLocation_range_end_column() + ); + + range.startpos = startPosition; + range.endpos = endPosition; + + fileRange.file = Long.toUnsignedString(javaAstNode.getLocation_file()); + fileRange.range = range; + + return fileRange; + } +} \ No newline at end of file diff --git a/plugins/java/service/srcjava/JavaQueryServer.java b/plugins/java/service/srcjava/JavaQueryServer.java new file mode 100644 index 000000000..d9e42754b --- /dev/null +++ b/plugins/java/service/srcjava/JavaQueryServer.java @@ -0,0 +1,46 @@ +package service.srcjava; + +import org.apache.log4j.BasicConfigurator; +import org.apache.thrift.server.TServer; +import org.apache.thrift.server.TServer.Args; +import org.apache.thrift.server.TSimpleServer; +import org.apache.thrift.transport.TServerSocket; +import org.apache.thrift.transport.TServerTransport; + +import java.util.logging.Level; + +import static logger.Logger.LOGGER; +import cc.service.java.JavaService; + +public class JavaQueryServer { + public static JavaQueryHandler javaQueryHandler; + public static JavaService.Processor processor; + + public static void main(String [] args) { + BasicConfigurator.configure(); + + try { + javaQueryHandler = new JavaQueryHandler(); + processor = new JavaService.Processor<>(javaQueryHandler); + + Runnable simple = () -> simple(processor); + new Thread(simple).start(); + } catch (Exception e) { + LOGGER.log( + Level.SEVERE, "Java server starting failed!"); + } + } + + public static void simple(JavaService.Processor processor) { + try { + TServerTransport serverTransport = new TServerSocket(9090); + TServer server = + new TSimpleServer(new Args(serverTransport).processor(processor)); + + server.serve(); + } catch (Exception e) { + LOGGER.log( + Level.SEVERE, "Java server starting failed!"); + } + } +} diff --git a/plugins/java/service/srcjava/META-INF/MANIFEST.MF b/plugins/java/service/srcjava/META-INF/MANIFEST.MF new file mode 100644 index 000000000..9a01c88aa --- /dev/null +++ b/plugins/java/service/srcjava/META-INF/MANIFEST.MF @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +Class-Path: libthrift-0.13.0.jar + javalogger.jar + javamodel.jar + javaservicethrift.jar +Main-Class: service.srcjava.JavaQueryServer diff --git a/plugins/java/service/srcjava/enums/DiagramType.java b/plugins/java/service/srcjava/enums/DiagramType.java new file mode 100644 index 000000000..cc5f2e6c2 --- /dev/null +++ b/plugins/java/service/srcjava/enums/DiagramType.java @@ -0,0 +1,43 @@ +package service.srcjava.enums; + +public enum DiagramType { + METHOD_CALL, /*!< In the method call diagram the nodes are methods and + the edges are the method calls between them. The diagram also displays + some dynamic information such as virtual method calls. */ + + DETAILED_CLASS, /*!< This is a classical UML class diagram for the selected + class and its direct children and parents. The nodes contain the methods + and member variables with their visibility. */ + + CLASS_OVERVIEW, /*!< This is a class diagram which contains all classes + which inherit from the current one, and all parents from which the + current one inherits. The methods and member variables are node included + in the nodes, but the type of the member variables are indicated as + aggregation relationship. */ + + CLASS_COLLABORATION, /*!< This returns a class collaboration diagram + which shows the individual class members and their inheritance + hierarchy. */ + + COMPONENT_USERS, /*!< Component users diagram for source file S shows which + source files depend on S through the interfaces S provides. */ + + EXTERNAL_DEPENDENCY, /*!< This diagram shows the module which directory + depends on. The "depends on" diagram on module A traverses the + subdirectories of module A and shows all directories that contain files + that any of the source files in A includes. */ + + EXTERNAL_USERS, /*!< This diagram shows directories (modules) that are + users of the queried module. */ + + INCLUDE_DEPENDENCY, /*!< This diagram shows of the `#include` file + dependencies. */ + + INTERFACE, /*!< Interface diagram shows the used and provided interfaces of + a source code file and shows linking information. */ + + SUBSYSTEM_DEPENDENCY, /*!< This diagram shows the directories relationship + between the subdirectories of the queried module. This diagram is useful + to understand the relationships of the subdirectories (submodules) + of a module. */ +} diff --git a/plugins/java/service/srcjava/enums/FileReferenceType.java b/plugins/java/service/srcjava/enums/FileReferenceType.java new file mode 100644 index 000000000..91806bb3c --- /dev/null +++ b/plugins/java/service/srcjava/enums/FileReferenceType.java @@ -0,0 +1,12 @@ +package service.srcjava.enums; + +public enum FileReferenceType { + IMPORTS, /*!< Included source files in the current source file after the + inclusion directive. */ + + TYPES, /*!< User defined data types such as classes, structs etc. */ + + CONSTRUCTORS, /*!< Methods in the current source file. */ + + METHODS /*!< Methods in the current source file. */ +} diff --git a/plugins/java/service/srcjava/enums/ReferenceType.java b/plugins/java/service/srcjava/enums/ReferenceType.java new file mode 100644 index 000000000..abb9c1fad --- /dev/null +++ b/plugins/java/service/srcjava/enums/ReferenceType.java @@ -0,0 +1,63 @@ +package service.srcjava.enums; + +public enum ReferenceType { + DEFINITION, /*!< By this option the definition(s) of the AST node can be + queried. However according to the "one definition rule" a named entity + can have only one definition, in a parsing several definitions might be + available. This is the case when the project is built for several targets + and in the different builds different definitions are defined for an + entity (e.g. because of an #ifdef section). */ + + DECLARATION, /*!< By this options the declaration(s) of the AST node can be + queried. */ + + USAGE, /*!< By this option the usages of the AST node can be queried, i.e. + the nodes of which the entity hash is identical to the queried one. */ + + THIS_CALLS, /*!< Get function calls in a function. WARNING: If the + definition of the AST node is not unique then it returns the callees of + one of them. */ + + CALLEE, /*!< Get called functions definitions. WARNING: If the definition of + the AST node is not unique then it returns the callees of one of them. */ + + CALLER, /*!< Get caller functions. */ + + PARAMETER, /*!< This option returns the parameters of a function. */ + + LOCAL_VAR, /*!< This option returns the local variables of a function. */ + + RETURN_TYPE, /*!< This option returns the return type of a function. */ + + OVERRIDE, /*!< This option returns the functions which the given function + overrides. */ + + OVERRIDDEN_BY, /*!< This option returns the overrides of a function. */ + + IMPLEMENT, /*!< This option returns the functions which the given function + implements. */ + + IMPLEMENTED_BY, /*!< This option returns the implementations of a function. */ + + READ, /*!< This option returns the places where a variable is read. */ + + WRITE, /*!< This option returns the places where a variable is written. */ + + TYPE, /*!< This option returns the type of a variable. */ + + INHERIT_FROM, /*!< Types from which the queried type inherits. */ + + INHERIT_BY, /*!< Types by which the queried type is inherited. */ + + DATA_MEMBER, /*!< Data members of a class. */ + + INNER_TYPE, /*!< Inner classes of a class. */ + + INITIALIZER, /*!< Initializers of a class. */ + + CONSTRUCTOR, /*!< Constructors of a class. */ + + METHOD, /*!< Members of a class. */ + + ENUM_CONSTANTS, /*!< Enum constants. */ +} diff --git a/plugins/java/test/CMakeLists.txt b/plugins/java/test/CMakeLists.txt new file mode 100644 index 000000000..d6369e70b --- /dev/null +++ b/plugins/java/test/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(dummytest + src/javaparsertest.cpp + src/javaservicetest.cpp) + +target_link_libraries(javatest ${GTEST_BOTH_LIBRARIES} pthread) + +# Add a test to the project to be run by ctest +add_test(allJavaTest javatest) \ No newline at end of file diff --git a/plugins/java/test/src/javaparsertest.cpp b/plugins/java/test/src/javaparsertest.cpp new file mode 100644 index 000000000..77e2fa03c --- /dev/null +++ b/plugins/java/test/src/javaparsertest.cpp @@ -0,0 +1,29 @@ +#include + +class JavaParserTest : public ::testing::Test +{ +protected: + /** + * Prepare the objects for each test + */ + virtual void SetUp() override + { + } + + /** + * Release any resources you allocated in SetUp() + */ + virtual void TearDown() override + { + } +}; + +TEST_F(JavaParserTest, simpleJavaParserTest) +{ + ASSERT_EQ(1,1); +} + +TEST_F(JavaParserTest, simpleJavaParserTest2) +{ + ASSERT_TRUE(true); +} diff --git a/plugins/java/test/src/javaservicetest.cpp b/plugins/java/test/src/javaservicetest.cpp new file mode 100644 index 000000000..acf21ab05 --- /dev/null +++ b/plugins/java/test/src/javaservicetest.cpp @@ -0,0 +1,24 @@ +#include + +class JavaServiceTest : public ::testing::Test +{ +protected: + /** + * Prepare the objects for each test + */ + virtual void SetUp() override + { + } + + /** + * Release any resources you allocated in SetUp() + */ + virtual void TearDown() override + { + } +}; + +TEST_F(JavaServiceTest, simpleJavaServiceTest) +{ + EXPECT_EQ(7,7); +} diff --git a/plugins/java/webgui/js/javaDiagram.js b/plugins/java/webgui/js/javaDiagram.js new file mode 100644 index 000000000..2f8aaa995 --- /dev/null +++ b/plugins/java/webgui/js/javaDiagram.js @@ -0,0 +1,99 @@ +require([ + 'dojo/topic', + 'dijit/Menu', + 'dijit/MenuItem', + 'dijit/PopupMenuItem', + 'codecompass/model', + 'codecompass/viewHandler'], +function (topic, Menu, MenuItem, PopupMenuItem, model, viewHandler) { + model.addService('javaservice', 'JavaService', LanguageServiceClient); + + var astDiagram = { + id : 'java-ast-diagram', + + getDiagram : function (diagramType, nodeId, callback) { + model.javaservice.getDiagram(nodeId, diagramType, callback); + }, + + getDiagramLegend : function (diagramType) { + return model.javaservice.getDiagramLegend(diagramType); + }, + + mouseOverInfo : function (diagramType, nodeId) { + var nodeInfo = model.javaservice.getAstNodeInfo(nodeId); + var range = nodeInfo.range.range; + + return { + fileId : nodeInfo.range.file, + selection : [ + range.startpos.line, + range.startpos.column, + range.endpos.line, + range.endpos.column + ] + }; + } + }; + + viewHandler.registerModule(astDiagram, { + type : viewHandler.moduleType.Diagram + }); + + var fileDiagramHandler = { + id : 'java-file-diagram-handler', + + getDiagram : function (diagramType, nodeId, callback) { + model.javaservice.getFileDiagram(nodeId, diagramType, callback); + }, + + getDiagramLegend : function (diagramType) { + return model.javaservice.getFileDiagramLegend(diagramType); + }, + + mouseOverInfo : function (diagramType, nodeId) { + return { + fileId : nodeId, + selection : [1,1,1,1] + }; + } + }; + + viewHandler.registerModule(fileDiagramHandler, { + type : viewHandler.moduleType.Diagram + }); + + var fileDiagrams = { + id : 'java-file-diagrams', + render : function (fileInfo) { + var submenu = new Menu(); + + var diagramTypes = model.javaservice.getFileDiagramTypes(fileInfo.id); + for (diagramType in diagramTypes) + submenu.addChild(new MenuItem({ + label : diagramType, + type : diagramType, + onClick : function () { + var that = this; + + topic.publish('codecompass/openFile', { fileId : fileInfo.id }); + + topic.publish('codecompass/openDiagram', { + handler : 'java-file-diagram-handler', + diagramType : diagramTypes[that.type], + node : fileInfo.id + }); + } + })); + + if (Object.keys(diagramTypes).length !== 0) + return new PopupMenuItem({ + label : 'Java Diagrams', + popup : submenu + }); + } + }; + + viewHandler.registerModule(fileDiagrams, { + type : viewHandler.moduleType.FileManagerContextMenu + }); +}); diff --git a/plugins/java/webgui/js/javaInfoTree.js b/plugins/java/webgui/js/javaInfoTree.js new file mode 100644 index 000000000..dffd30784 --- /dev/null +++ b/plugins/java/webgui/js/javaInfoTree.js @@ -0,0 +1,382 @@ +require([ + 'codecompass/model', + 'codecompass/viewHandler', + 'codecompass/util'], +function (model, viewHandler, util) { + + model.addService('javaservice', 'JavaService', LanguageServiceClient); + + function createTagLabels(tags) { + var label = ''; + + if (!tags) + return label; + + if (tags.indexOf('static') > -1) + label += 'S'; + if (tags.indexOf('constructor') > -1) + label += 'C'; + if (tags.indexOf('destructor') > -1) + label += 'D'; + if (tags.indexOf('implicit') > -1) + label += 'I'; + if (tags.indexOf('inherited') > -1) + label += 'I'; + if (tags.indexOf('virtual') > -1) + label += 'V'; + if (tags.indexOf('global') > -1) + label += 'G'; + + return label; + } + + function createReferenceCountLabel(label, count) { + return label + '(' + count + ')'; + } + + function createLabel(astNodeInfo) { + var labelClass = ''; + + if (astNodeInfo.tags.indexOf('implicit') > -1) + labelClass = 'label-implicit'; + + var labelValue = astNodeInfo.astNodeValue; + + // Create dom node for return type of a function and place it at the end of + // signature. + if (astNodeInfo.symbolType === 'Function') { + var init = labelValue.slice(0, labelValue.indexOf('(')); + var returnTypeEnd = init.lastIndexOf(' '); + + //--- Constructor, destructor doesn't have return type ---// + + if (returnTypeEnd !== -1) { + var funcSignature = init.slice(returnTypeEnd); + + labelValue = funcSignature + + ' : ' + + init.slice(0, returnTypeEnd) + + ""; + } + } + + var label = createTagLabels(astNodeInfo.tags) + + '' + + astNodeInfo.range.range.startpos.line + ':' + + astNodeInfo.range.range.startpos.column + ': ' + + labelValue + + ''; + + return label; + } + + function getCssClass(astNodeInfo) { + var tags = astNodeInfo.tags; + + return tags.indexOf('public') > -1 ? 'icon-visibility icon-public' : + tags.indexOf('private') > -1 ? 'icon-visibility icon-private' : + tags.indexOf('protected') > -1 ? 'icon-visibility icon-protected' : + tags.indexOf('package-private') > -1 ? + 'icon-visibility icon-package-private' : + null; + } + + function groupReferencesByVisibilities(references, parentNode, nodeInfo) { + var res = []; + var visibilities = ['public', 'private', 'protected', 'package-private']; + + visibilities.forEach(function (visibility) { + var nodes = references.filter(function (reference) { + return reference.tags.indexOf(visibility) > -1; + }); + + if (!nodes.length) + return; + + res.push({ + id : nodeInfo.id + visibility + parentNode.refType, + name : createReferenceCountLabel(visibility, nodes.length), + refType : parentNode.refType, + hasChildren : true, + cssClass : 'icon-visibility icon-' + visibility, + getChildren : function () { + var res = []; + + nodes.forEach(function (reference) { + res.push({ + id : visibility + reference.id, + name : createLabel(reference), + refType : parentNode.refType, + nodeInfo : reference, + hasChildren : false, + cssClass : getCssClass(reference) + }); + }); + + return res; + } + }); + }); + + return res; + } + + function loadReferenceNodes(parentNode, nodeInfo, refTypes) { + var res = []; + var fileGroupsId = []; + + var references = model.javaservice.getReferences( + nodeInfo.id, + parentNode.refType); + + if (parentNode.refType === refTypes['Constructor'] || + parentNode.refType === refTypes['Method'] || + parentNode.refType === refTypes['Data member'] || + parentNode.refType === refTypes['Inner type']) + return groupReferencesByVisibilities(references, parentNode, nodeInfo); + + references.forEach(function (reference) { + if (parentNode.refType === refTypes['Caller'] || + parentNode.refType === refTypes['Usage']) { + + //--- Group nodes by file name ---// + + var fileId = reference.range.file; + if (fileGroupsId[fileId]) + return; + + fileGroupsId[fileId] = parentNode.refType + fileId + reference.id; + + var referenceInFile = references.filter(function (reference) { + return reference.range.file === fileId; + }); + + var fileInfo = model.project.getFileInfo(fileId); + res.push({ + id : fileGroupsId[fileId], + name : createReferenceCountLabel( + fileInfo.name, referenceInFile.length), + refType : parentNode.refType, + hasChildren : true, + cssClass : util.getIconClass(fileInfo.path), + getChildren : function () { + var that = this; + var res = []; + + referenceInFile.forEach(function (reference) { + if (parentNode.refType === refTypes['Caller']) { + res.push({ + id : reference.id, + name : createLabel(reference), + nodeInfo : reference, + refType : parentNode.refType, + cssClass : 'icon icon-Method', + hasChildren : true, + getChildren : function () { + var res = []; + + //--- Recursive Node ---// + + var refCount = model.javaservice.getReferenceCount( + reference.id, parentNode.refType); + + if (refCount) + res.push({ + id : 'Caller-' + reference.id, + name : createReferenceCountLabel( + parentNode.name, refCount), + nodeInfo : reference, + refType : parentNode.refType, + cssClass : parentNode.cssClass, + hasChildren : true, + getChildren : parentNode.getChildren + }); + + //--- Call ---// + + var calls = model.javaservice.getReferences( + this.nodeInfo.id, + refTypes['This calls']); + + calls.forEach(function (call) { + if (call.entityHash === nodeInfo.entityHash) + res.push({ + name : createLabel(call), + refType : parentNode.refType, + nodeInfo : call, + hasChildren : false, + cssClass : getCssClass(call) + }); + }); + return res; + } + }); + } else if (parentNode.refType === refTypes['Usage']) { + res.push({ + id : fileGroupsId[fileId] + reference.id, + name : createLabel(reference), + refType : parentNode.refType, + nodeInfo : reference, + hasChildren : false, + cssClass : getCssClass(reference) + }); + } + }); + return res; + } + }); + } else { + res.push({ + name : createLabel(reference), + refType : parentNode.refType, + nodeInfo : reference, + hasChildren : false, + cssClass : getCssClass(reference) + }); + } + }); + + return res; + } + + /** + * This function returns file references children. + * @param parentNode Reference type node in Info Tree. + */ + function loadFileReferenceNodes(parentNode) { + var res = []; + + var references = model.javaservice.getFileReferences( + parentNode.nodeInfo.id, + parentNode.refType); + + references.forEach(function (reference) { + res.push({ + name : createLabel(reference), + refType : parentNode.refType, + nodeInfo : reference, + hasChildren : false, + cssClass : getCssClass(reference) + }); + }); + + return res; + } + + function createRootNode(elementInfo) { + var rootLabel + = '' + + (elementInfo instanceof AstNodeInfo + ? elementInfo.symbolType + : 'File') + + ''; + + var rootValue + = '' + + (elementInfo instanceof AstNodeInfo + ? elementInfo.astNodeValue + : elementInfo.name) + + ''; + + var label = createTagLabels(elementInfo.tags) + + '' + + rootLabel + ': ' + rootValue + + ''; + + return { + id : 'root', + name : label, + cssClass : 'icon-info', + hasChildren : true, + getChildren : function () { + return that._store.query({ parent : 'root' }); + } + }; + } + + var javaInfoTree = { + id: 'java-infotree', + render : function (elementInfo) { + var ret = []; + + ret.push(createRootNode(elementInfo)); + + if (elementInfo instanceof AstNodeInfo) { + //--- Properties ---// + + var props = model.javaservice.getProperties(elementInfo.id); + + for (var propName in props) { + var propId = propName.replace(/ /g, '-'); + var labelName = propName.replace('<','<').replace('>','>'); + var labelValue = + props[propName].replace('<','<').replace('>','>'); + + var label + = '' + labelName + ': ' + + '' + labelValue + ''; + + ret.push({ + name : label, + parent : 'root', + nodeInfo : elementInfo, + cssClass : 'icon-' + propId, + hasChildren : false + }); + } + + //--- References ---// + + var refTypes = model.javaservice.getReferenceTypes(elementInfo.id); + for (var refType in refTypes) { + var refCount = + model.javaservice.getReferenceCount( + elementInfo.id, refTypes[refType]); + + if (refCount) + ret.push({ + name : createReferenceCountLabel(refType, refCount), + parent : 'root', + refType : refTypes[refType], + cssClass : 'icon-' + refType.replace(/ /g, '-'), + hasChildren : true, + getChildren : function () { + return loadReferenceNodes(this, elementInfo, refTypes); + } + }); + }; + + } else if (elementInfo instanceof FileInfo) { + + //--- File references ---// + + var refTypes = model.javaservice.getFileReferenceTypes(elementInfo.id); + for (var refType in refTypes) { + var refCount = model.javaservice.getFileReferenceCount( + elementInfo.id, refTypes[refType]); + + if (refCount) + ret.push({ + name : createReferenceCountLabel(refType, refCount), + parent : 'root', + nodeInfo : elementInfo, + refType : refTypes[refType], + cssClass : 'icon-' + refType.replace(/ /g, '-'), + hasChildren : true, + getChildren : function () { + return loadFileReferenceNodes(this); + } + }); + }; + + } + + return ret; + } + }; + + viewHandler.registerModule(javaInfoTree, { + type : viewHandler.moduleType.InfoTree, + service : model.javaservice + }); +}); diff --git a/plugins/java/webgui/js/javaMenu.js b/plugins/java/webgui/js/javaMenu.js new file mode 100644 index 000000000..183b3007d --- /dev/null +++ b/plugins/java/webgui/js/javaMenu.js @@ -0,0 +1,133 @@ +require([ + 'dojo/topic', + 'dijit/Menu', + 'dijit/MenuItem', + 'dijit/PopupMenuItem', + 'codecompass/astHelper', + 'codecompass/model', + 'codecompass/urlHandler', + 'codecompass/viewHandler'], +function (topic, Menu, MenuItem, PopupMenuItem, astHelper, model, urlHandler, viewHandler) { + + model.addService('javaservice', 'javaService', LanguageServiceClient); + + var getdefintion = { + id : 'java-text-getdefintion', + render : function (nodeInfo, fileInfo) { + return new MenuItem({ + label : 'Jump to definition', + accelKey : 'ctrl - click', + onClick : function () { + if (!nodeInfo || !fileInfo) + return; + + var languageService = model.getLanguageService(fileInfo.type); + astHelper.jumpToDef(nodeInfo.id, model.javaservice); + } + }); + } + }; + + viewHandler.registerModule(getdefintion, { + type : viewHandler.moduleType.TextContextMenu, + service : model.javaservice + }); + + var infoTree = { + id : 'java-text-infotree', + render : function (nodeInfo, fileInfo) { + return new MenuItem({ + label : 'Info Tree', + onClick : function () { + if (!nodeInfo || !fileInfo) + return; + + topic.publish('codecompass/infotree', { + fileType : fileInfo.type, + elementInfo : nodeInfo + }); + } + }); + } + }; + + viewHandler.registerModule(infoTree, { + type : viewHandler.moduleType.TextContextMenu, + service : model.javaservice + }); + + var infobox = { + id : 'java-text-infobox', + render : function (nodeInfo, fileInfo) { + return new MenuItem({ + label : 'Documentation', + onClick : function () { + topic.publish('codecompass/documentation', { + fileType : fileInfo.type, + elementInfo : nodeInfo + }); + + if (gtag) { + gtag ('event', 'documentation', { + 'event_category' : urlHandler.getState('wsid'), + 'event_label' : urlHandler.getFileInfo().name + + ': ' + + nodeInfo.astNodeValue + }); + } + } + }); + } + }; + + viewHandler.registerModule(infobox, { + type : viewHandler.moduleType.TextContextMenu, + service : model.javaservice + }); + + var diagrams = { + id : 'java-text-diagrams', + render : function (nodeInfo, fileInfo) { + if (!nodeInfo || !fileInfo) + return; + + var submenu = new Menu(); + + var diagramTypes = model.javaservice.getDiagramTypes(nodeInfo.id); + for (diagramType in diagramTypes) + submenu.addChild(new MenuItem({ + label : diagramType, + type : diagramType, + onClick : function () { + var that = this; + + topic.publish('codecompass/openDiagram', { + handler : 'java-ast-diagram', + diagramType : diagramTypes[that.type], + node : nodeInfo.id + }); + } + })); + + submenu.addChild(new MenuItem({ + label : "CodeBites", + onClick : function () { + topic.publish('codecompass/codebites', { + node : nodeInfo + }); + } + })); + + if (Object.keys(diagramTypes).length !== 0) + return new PopupMenuItem({ + label : 'Diagrams', + popup : submenu + }); + } + }; + + viewHandler.registerModule(diagrams, { + type : viewHandler.moduleType.TextContextMenu, + service : model.javaservice + }); +}); diff --git a/plugins/search/lib/java/lucene-memory-4.9.0.jar b/plugins/search/lib/java/lucene-memory-4.9.0.jar old mode 100755 new mode 100644 diff --git a/scripts/CodeCompass_logger b/scripts/CodeCompass_logger old mode 100755 new mode 100644 diff --git a/scripts/remover.sh b/scripts/remover.sh old mode 100755 new mode 100644 diff --git a/service/language/CMakeLists.txt b/service/language/CMakeLists.txt index 8cb21a098..e38ba89ff 100644 --- a/service/language/CMakeLists.txt +++ b/service/language/CMakeLists.txt @@ -15,7 +15,7 @@ add_custom_command( ${CMAKE_CURRENT_BINARY_DIR}/gen-cpp ${CMAKE_CURRENT_BINARY_DIR}/gen-js COMMAND - ${THRIFT_EXECUTABLE} --gen cpp --gen js + ${THRIFT_EXECUTABLE} --gen cpp --gen js --gen java -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/language.thrift DEPENDS @@ -31,4 +31,16 @@ add_library(languagethrift STATIC target_compile_options(languagethrift PUBLIC -fPIC) add_dependencies(languagethrift projectthrift) + +set(CMAKE_JAVA_INCLUDE_PATH + ${PROJECT_SOURCE_DIR}/lib/java/* + ${PROJECT_BINARY_DIR}/service/project/gen-java) + +add_jar(languagethriftjava + ${CMAKE_CURRENT_BINARY_DIR}/gen-java/cc/service/language/AstNodeInfo.java + OUTPUT_NAME languagethrift) + +add_dependencies(languagethriftjava languagethrift) + +install_jar(languagethriftjava "${INSTALL_JAVA_LIB_DIR}") install_js_thrift() diff --git a/service/language/language.thrift b/service/language/language.thrift index 91591abe8..cfa2ea9cb 100644 --- a/service/language/language.thrift +++ b/service/language/language.thrift @@ -2,6 +2,7 @@ include "../project/common.thrift" include "../project/project.thrift" namespace cpp cc.service.language +namespace java cc.service.language struct AstNodeInfo { diff --git a/service/project/CMakeLists.txt b/service/project/CMakeLists.txt index ef2e9dcc1..6ae74d922 100644 --- a/service/project/CMakeLists.txt +++ b/service/project/CMakeLists.txt @@ -69,6 +69,7 @@ add_jar(corethriftjava ${CMAKE_CURRENT_BINARY_DIR}/gen-java/cc/service/core/FilePosition.java ${CMAKE_CURRENT_BINARY_DIR}/gen-java/cc/service/core/FileRange.java ${CMAKE_CURRENT_BINARY_DIR}/gen-java/cc/service/core/InvalidId.java + ${CMAKE_CURRENT_BINARY_DIR}/gen-java/cc/service/core/InvalidPos.java ${CMAKE_CURRENT_BINARY_DIR}/gen-java/cc/service/core/InvalidInput.java ${CMAKE_CURRENT_BINARY_DIR}/gen-java/cc/service/core/Position.java ${CMAKE_CURRENT_BINARY_DIR}/gen-java/cc/service/core/Range.java diff --git a/service/project/common.thrift b/service/project/common.thrift index 973befa42..7951463b0 100644 --- a/service/project/common.thrift +++ b/service/project/common.thrift @@ -14,27 +14,6 @@ namespace java cc.service.core typedef string AstNodeId typedef string FileId -/********************** - * General Exceptions * - **********************/ - -exception InvalidId -{ - 1:string msg, - 2:optional FileId fid, - 3:optional AstNodeId nodeid -} - -exception InvalidInput -{ - 1:string msg -} - -exception Timeout -{ - 1:string msg -} - /****************************** * File positions & locations * ******************************/ @@ -63,6 +42,33 @@ struct FileRange 2:Range range } +/********************** + * General Exceptions * + **********************/ + +exception InvalidId +{ + 1:string msg, + 2:optional FileId fid, + 3:optional AstNodeId nodeid +} + +exception InvalidInput +{ + 1:string msg +} + +exception InvalidPos +{ + 1:string msg, + 2:optional FilePosition fpos +} + +exception Timeout +{ + 1:string msg +} + /*************** * Description * ***************/ diff --git a/webgui/package.json b/webgui/package.json index 313299225..c11bb48bf 100644 --- a/webgui/package.json +++ b/webgui/package.json @@ -14,7 +14,7 @@ "codemirror" : ">=5.19.0", "jquery.fancytree" : ">=2.19.0", "svg-pan-zoom" : ">=2.19.0", - "marked" : ">=0.3.6", + "marked" : "2.1.3", "jsplumb" : "2.2.1", "jquery" : ">=3.1.1", "js-cookie" : "^2.2.1" diff --git a/webgui/scripts/codecompass/viewHandler.js b/webgui/scripts/codecompass/viewHandler.js index 08bcc4dd6..e239f78a8 100644 --- a/webgui/scripts/codecompass/viewHandler.js +++ b/webgui/scripts/codecompass/viewHandler.js @@ -28,7 +28,7 @@ function (lang, Deferred, model) { ContextButton : 3, /** Context button menu item (eg. File outline) **/ TextContextMenu : 4, /** Text module context menu. **/ Diagram : 5, /** Diagram (eg. CppDiagram)**/ - InfoTree : 6, /** Info tree item (eg. CppInforTree)**/ + InfoTree : 6, /** Info tree item (eg. CppInfoTree)**/ FileManagerContextMenu : 7, /** File manager context menu (eg. Diagram directory)**/ InfoPage : 8, /** Info page (eg. Credit, User Guide) **/ }, diff --git a/webgui/style/icons.css b/webgui/style/icons.css index 7589b8f69..6ac1872d2 100644 --- a/webgui/style/icons.css +++ b/webgui/style/icons.css @@ -62,6 +62,11 @@ font-size: 0.8em; } +.icon-package-private::before{ + content: '>'; + color: purple; +} + /** Icons * */ .icon-question::before{