@@ -0,0 +1,10 @@ | |||||
.DS_Store | |||||
.idea/ | |||||
*.iml | |||||
target/ | |||||
2017* | |||||
.project | |||||
.classpath | |||||
.settings/ | |||||
*.log | |||||
bin/ |
@@ -0,0 +1,257 @@ | |||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<groupId>com.educoder</groupId> | |||||
<artifactId>webssh</artifactId> | |||||
<packaging>war</packaging> | |||||
<version>1.0-SNAPSHOT</version> | |||||
<name>webssh Maven Webapp</name> | |||||
<url>http://maven.apache.org</url> | |||||
<properties> | |||||
<swagger2.version>2.6.1</swagger2.version> | |||||
<spring.version>4.3.6.RELEASE</spring.version> | |||||
<freemarker.version>2.3.25-incubating</freemarker.version> | |||||
<jsch.version>0.1.54</jsch.version> | |||||
<javax.version>7.0</javax.version> | |||||
<commons-lang.version>2.6</commons-lang.version> | |||||
<commons-io.version>2.4</commons-io.version> | |||||
<slf4j.version>1.7.21</slf4j.version> | |||||
<fastjson.version>1.2.20</fastjson.version> | |||||
<jackson.version>2.8.6</jackson.version> | |||||
<codec.version>1.10</codec.version> | |||||
<maven.compiler.source>1.8</maven.compiler.source> | |||||
<maven.compiler.target>1.8</maven.compiler.target> | |||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||||
</properties> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>org.freemarker</groupId> | |||||
<artifactId>freemarker</artifactId> | |||||
<version>${freemarker.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jcraft</groupId> | |||||
<artifactId>jsch</artifactId> | |||||
<version>${jsch.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>io.springfox</groupId> | |||||
<artifactId>springfox-swagger2</artifactId> | |||||
<version>${swagger2.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>io.springfox</groupId> | |||||
<artifactId>springfox-swagger-ui</artifactId> | |||||
<version>${swagger2.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework</groupId> | |||||
<artifactId>spring-webmvc</artifactId> | |||||
<version>${spring.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework</groupId> | |||||
<artifactId>spring-websocket</artifactId> | |||||
<version>${spring.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework</groupId> | |||||
<artifactId>spring-context-support</artifactId> | |||||
<version>${spring.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework</groupId> | |||||
<artifactId>spring-test</artifactId> | |||||
<version>${spring.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>javax</groupId> | |||||
<artifactId>javaee-api</artifactId> | |||||
<version>${javax.version}</version> | |||||
<scope>provided</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>commons-lang</groupId> | |||||
<artifactId>commons-lang</artifactId> | |||||
<version>${commons-lang.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>commons-io</groupId> | |||||
<artifactId>commons-io</artifactId> | |||||
<version>${commons-io.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>junit</groupId> | |||||
<artifactId>junit</artifactId> | |||||
<version>4.12</version> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework</groupId> | |||||
<artifactId>spring-aop</artifactId> | |||||
<version>4.3.6.RELEASE</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.aspectj</groupId> | |||||
<artifactId>aspectjrt</artifactId> | |||||
<version>1.8.10</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework</groupId> | |||||
<artifactId>spring-aspects</artifactId> | |||||
<version>${spring.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework</groupId> | |||||
<artifactId>spring-test</artifactId> | |||||
<version>${spring.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>ch.qos.logback</groupId> | |||||
<artifactId>logback-classic</artifactId> | |||||
<version>1.2.3</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.logback-extensions</groupId> | |||||
<artifactId>logback-ext-spring</artifactId> | |||||
<version>0.1.4</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.alibaba</groupId> | |||||
<artifactId>fastjson</artifactId> | |||||
<version>${fastjson.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>commons-codec</groupId> | |||||
<artifactId>commons-codec</artifactId> | |||||
<version>${codec.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.fasterxml.jackson.core</groupId> | |||||
<artifactId>jackson-core</artifactId> | |||||
<version>${jackson.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.fasterxml.jackson.core</groupId> | |||||
<artifactId>jackson-annotations</artifactId> | |||||
<version>${jackson.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.fasterxml.jackson.core</groupId> | |||||
<artifactId>jackson-databind</artifactId> | |||||
<version>${jackson.version}</version> | |||||
</dependency> | |||||
</dependencies> | |||||
<build> | |||||
<plugins> | |||||
<plugin> | |||||
<groupId>org.apache.maven.plugins</groupId> | |||||
<artifactId>maven-surefire-plugin</artifactId> | |||||
<version>2.18.1</version> | |||||
<configuration> | |||||
<skipTests>true</skipTests> | |||||
</configuration> | |||||
</plugin> | |||||
<plugin> | |||||
<groupId>org.apache.maven.plugins</groupId> | |||||
<artifactId>maven-shade-plugin</artifactId> | |||||
<version>3.0.0</version> | |||||
<executions> | |||||
<execution> | |||||
<phase>package</phase> | |||||
<goals> | |||||
<goal>shade</goal> | |||||
</goals> | |||||
<configuration> | |||||
<dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation> | |||||
<transformers> | |||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> | |||||
<resource>META-INF/spring.handlers</resource> | |||||
</transformer> | |||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> | |||||
<resource>META-INF/spring.schemas</resource> | |||||
</transformer> | |||||
</transformers> | |||||
<filters> | |||||
<filter> | |||||
<artifact>*:*</artifact> | |||||
<excludes> | |||||
<exclude>META-INF/*.SF</exclude> | |||||
<exclude>META-INF/*.DSA</exclude> | |||||
<exclude>META-INF/*.RSA</exclude> | |||||
</excludes> | |||||
</filter> | |||||
</filters> | |||||
</configuration> | |||||
</execution> | |||||
</executions> | |||||
</plugin> | |||||
<plugin> | |||||
<groupId>org.apache.tomcat.maven</groupId> | |||||
<artifactId>tomcat7-maven-plugin</artifactId> | |||||
<version>2.2</version> | |||||
<configuration> | |||||
<address>0.0.0.0</address> | |||||
<port>9001</port> | |||||
<path>/</path> | |||||
<uriEncoding>UTF-8</uriEncoding> | |||||
<finalName>webssh</finalName> | |||||
<server>tomcat7</server> | |||||
</configuration> | |||||
</plugin> | |||||
<plugin> | |||||
<groupId>org.apache.maven.plugins</groupId> | |||||
<artifactId>maven-compiler-plugin</artifactId> | |||||
<version>2.3.2</version> | |||||
<executions> | |||||
<execution> | |||||
<id>compile</id> | |||||
<phase>compile</phase> | |||||
<goals> | |||||
<goal>compile</goal> | |||||
</goals> | |||||
</execution> | |||||
<execution> | |||||
<id>testCompile</id> | |||||
<phase>test-compile</phase> | |||||
<goals> | |||||
<goal>testCompile</goal> | |||||
</goals> | |||||
</execution> | |||||
</executions> | |||||
<configuration> | |||||
<source>1.8</source> | |||||
<target>1.8</target> | |||||
</configuration> | |||||
</plugin> | |||||
</plugins> | |||||
<finalName>webssh</finalName> | |||||
</build> | |||||
</project> |
@@ -0,0 +1,48 @@ | |||||
package excel | |||||
import ( | |||||
"fmt" | |||||
"reflect" | |||||
"github.com/360EntSecGroup-Skylar/excelize" | |||||
) | |||||
// WriteXlsx 生成表格到本地服务器 | |||||
func WriteXlsx(sheet string, records interface{}) *excelize.File { | |||||
f := excelize.NewFile() // new file | |||||
index := f.NewSheet(sheet) // new sheet | |||||
f.SetActiveSheet(index) // set active (default) sheet | |||||
firstCharacter := 65 // start from 'A' line | |||||
t := reflect.TypeOf(records) | |||||
if t.Kind() != reflect.Slice { | |||||
return f | |||||
} | |||||
s := reflect.ValueOf(records) | |||||
for i := 0; i < s.Len(); i++ { | |||||
elem := s.Index(i).Interface() | |||||
elemType := reflect.TypeOf(elem) | |||||
elemValue := reflect.ValueOf(elem) | |||||
for j := 0; j < elemType.NumField(); j++ { | |||||
field := elemType.Field(j) | |||||
// 结构体中xlsx 类似json | |||||
tag := field.Tag.Get("xlsx") | |||||
name := tag | |||||
column := string(firstCharacter + j) | |||||
if tag == "" { | |||||
continue | |||||
} | |||||
// 设置表头 | |||||
if i == 0 { | |||||
f.SetCellValue(sheet, fmt.Sprintf("%s%d", column, i+1), name) | |||||
} | |||||
// 设置列宽 | |||||
f.SetColWidth(sheet, "A", fmt.Sprintf("%s", column), 20) | |||||
// 设置内容 | |||||
f.SetCellValue(sheet, fmt.Sprintf("%s%d", column, i+2), elemValue.Field(j).Interface()) | |||||
} | |||||
} | |||||
return f | |||||
} |
@@ -0,0 +1,24 @@ | |||||
package main | |||||
import "fmt" | |||||
type Publisher struct { | |||||
name string | |||||
address string | |||||
} | |||||
type Book struct { | |||||
title, name string | |||||
publisher Publisher | |||||
} | |||||
func main() { | |||||
var book = Book{"title","name",Publisher{"pub","beijing"}} | |||||
/* 这是我的第一个简单的程序 */ | |||||
fmt.Println("Hello, World!") | |||||
fmt.Println(book) | |||||
} | |||||
func test (book Book) (b1 Book, b2 Book){ | |||||
return book,book | |||||
} |
@@ -0,0 +1,4 @@ | |||||
func main(){ | |||||
printf('hello wold') | |||||
io.out('ok') | |||||
} |
@@ -0,0 +1,7 @@ | |||||
package test | |||||
type Books struct { | |||||
title string | |||||
author string | |||||
subject string | |||||
book_id int | |||||
} |
@@ -0,0 +1,25 @@ | |||||
package com.educoder.bridge.controller; | |||||
import org.springframework.web.bind.annotation.ModelAttribute; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import javax.servlet.http.HttpServletResponse; | |||||
import javax.servlet.http.HttpSession; | |||||
/** | |||||
* @author lqk | |||||
* @version 0.1 | |||||
*/ | |||||
public class BaseController { | |||||
protected HttpServletRequest request; | |||||
protected HttpServletResponse response; | |||||
protected HttpSession session; | |||||
@ModelAttribute | |||||
public void setReqAndRes(HttpServletRequest request, HttpServletResponse response) { | |||||
this.request = request; | |||||
this.response = response; | |||||
this.session = request.getSession(); | |||||
} | |||||
} |
@@ -0,0 +1,49 @@ | |||||
package com.educoder.bridge.controller; | |||||
import io.swagger.annotations.Api; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.springframework.web.bind.annotation.RequestMapping; | |||||
import org.springframework.web.bind.annotation.RequestMethod; | |||||
import org.springframework.web.bind.annotation.RequestParam; | |||||
import org.springframework.web.bind.annotation.RestController; | |||||
import org.springframework.web.servlet.ModelAndView; | |||||
/** | |||||
* @author guange | |||||
* | |||||
* @date 2017/08/02 | |||||
*/ | |||||
@Api(value = "提供webssh连接", hidden = true) | |||||
@RestController | |||||
public class MainController extends BaseController { | |||||
private final static Logger logger = LoggerFactory.getLogger(MainController.class); | |||||
// | |||||
// @RequestMapping(value={"/"}, method= RequestMethod.GET) | |||||
// public ModelAndView index(@RequestParam("host")String host, | |||||
// @RequestParam("port")int port, | |||||
// @RequestParam("username")String username, | |||||
// @RequestParam("password")String password, | |||||
// @RequestParam("rows")int rows) { | |||||
// logger.debug("/ssh: 接收到连接请求, host: {}, port: {}", host, port); | |||||
// ModelAndView mv = new ModelAndView(); | |||||
// mv.setViewName("index"); | |||||
// mv.addObject("host", host); | |||||
// mv.addObject("port", port); | |||||
// mv.addObject("username", username); | |||||
// mv.addObject("password", password); | |||||
// mv.addObject("rows", rows); | |||||
// mv.addObject("digest", System.currentTimeMillis()); | |||||
// return mv; | |||||
// } | |||||
@RequestMapping(value={"/"}, method= RequestMethod.GET) | |||||
public ModelAndView index() { | |||||
ModelAndView mv = new ModelAndView(); | |||||
mv.setViewName("index"); | |||||
mv.addObject("digest", System.currentTimeMillis()); | |||||
return mv; | |||||
} | |||||
} |
@@ -0,0 +1,38 @@ | |||||
package com.educoder.bridge.handler; | |||||
import com.educoder.bridge.service.JchService; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.web.socket.CloseStatus; | |||||
import org.springframework.web.socket.TextMessage; | |||||
import org.springframework.web.socket.WebSocketSession; | |||||
import org.springframework.web.socket.handler.TextWebSocketHandler; | |||||
public class WebsshHandler extends TextWebSocketHandler { | |||||
@Autowired | |||||
JchService jchService; | |||||
@Override | |||||
public void afterConnectionEstablished(WebSocketSession wsSession) throws Exception { | |||||
super.afterConnectionEstablished(wsSession); | |||||
jchService.add(wsSession); | |||||
} | |||||
/** | |||||
* 重写handleTextMessage方法,用于处理从websocket接收到的信息 | |||||
*/ | |||||
@Override | |||||
protected void handleTextMessage(WebSocketSession wsSession, TextMessage message) throws Exception { | |||||
jchService.recv(message.getPayload(), wsSession); | |||||
super.handleTextMessage(wsSession, message); | |||||
} | |||||
@Override | |||||
public void afterConnectionClosed(WebSocketSession wsSession, CloseStatus status) throws Exception { | |||||
super.afterConnectionClosed(wsSession, status); | |||||
jchService.closeByWebSocket(wsSession); | |||||
} | |||||
} |
@@ -0,0 +1,41 @@ | |||||
package com.educoder.bridge.model; | |||||
public class SSHInfo { | |||||
private String host; | |||||
private String port; | |||||
private String username; | |||||
private String password; | |||||
public void setHost(String host) { | |||||
this.host = host; | |||||
} | |||||
public void setPort(String port) { | |||||
this.port = port; | |||||
} | |||||
public void setUsername(String username) { | |||||
this.username = username; | |||||
} | |||||
public void setPassword(String password) { | |||||
this.password = password; | |||||
} | |||||
public String getHost() { | |||||
return host; | |||||
} | |||||
public int getPort() { | |||||
return Integer.parseInt(port); | |||||
} | |||||
public String getUsername() { | |||||
return username; | |||||
} | |||||
public String getPassword() { | |||||
return password; | |||||
} | |||||
} |
@@ -0,0 +1,45 @@ | |||||
package com.educoder.bridge.model; | |||||
import com.jcraft.jsch.ChannelShell; | |||||
import org.springframework.web.socket.WebSocketSession; | |||||
import java.io.OutputStream; | |||||
public class SSHSession { | |||||
private WebSocketSession webSocketSession; | |||||
private OutputStream outputStream; | |||||
private ChannelShell channel; | |||||
private SSHInfo SSHInfo; | |||||
public SSHInfo getSSHInfo() { | |||||
return SSHInfo; | |||||
} | |||||
public void setSSHInfo(SSHInfo SSHInfo) { | |||||
this.SSHInfo = SSHInfo; | |||||
} | |||||
public ChannelShell getChannel() { | |||||
return channel; | |||||
} | |||||
public void setChannel(ChannelShell channel) { | |||||
this.channel = channel; | |||||
} | |||||
public WebSocketSession getWebSocketSession() { | |||||
return webSocketSession; | |||||
} | |||||
public void setWebSocketSession(WebSocketSession webSocketSession) { | |||||
this.webSocketSession = webSocketSession; | |||||
} | |||||
public OutputStream getOutputStream() { | |||||
return outputStream; | |||||
} | |||||
public void setOutputStream(OutputStream outputStream) { | |||||
this.outputStream = outputStream; | |||||
} | |||||
} |
@@ -0,0 +1,261 @@ | |||||
package com.educoder.bridge.service; | |||||
import com.alibaba.fastjson.JSONObject; | |||||
import com.educoder.bridge.model.SSHInfo; | |||||
import com.educoder.bridge.model.SSHSession; | |||||
import com.educoder.bridge.utils.Base64Util; | |||||
import com.jcraft.jsch.ChannelShell; | |||||
import com.jcraft.jsch.JSch; | |||||
import com.jcraft.jsch.Session; | |||||
import com.jcraft.jsch.UserInfo; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.springframework.stereotype.Service; | |||||
import org.springframework.web.socket.TextMessage; | |||||
import org.springframework.web.socket.WebSocketSession; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.io.OutputStream; | |||||
import java.util.Arrays; | |||||
import java.util.List; | |||||
import java.util.Optional; | |||||
import java.util.concurrent.CopyOnWriteArrayList; | |||||
import java.util.concurrent.ExecutorService; | |||||
import java.util.concurrent.Executors; | |||||
@Service | |||||
public class JchService { | |||||
private static List<SSHSession> sshSessionQueue = new CopyOnWriteArrayList<>(); | |||||
private ExecutorService executorService = Executors.newCachedThreadPool(); | |||||
private Logger logger = LoggerFactory.getLogger(getClass()); | |||||
com.jcraft.jsch.Logger jschLogger = new com.jcraft.jsch.Logger() { | |||||
@Override | |||||
public boolean isEnabled(int arg0) { | |||||
return true; | |||||
} | |||||
@Override | |||||
public void log(int arg0, String arg1) { | |||||
if (logger.isTraceEnabled()) { | |||||
logger.trace("JSch Log [Level " + arg0 + "]: " + arg1); | |||||
} | |||||
} | |||||
}; | |||||
/** | |||||
* 在webSocket连接时,初始化一个ssh连接 | |||||
* | |||||
* @param webSocketSession webSocket连接 | |||||
*/ | |||||
public void add(WebSocketSession webSocketSession) { | |||||
SSHSession sshSession = new SSHSession(); | |||||
sshSession.setWebSocketSession(webSocketSession); | |||||
sshSessionQueue.add(sshSession); | |||||
} | |||||
/** | |||||
* 处理客户端发过来的数据 | |||||
* @param buffer 数据 | |||||
* @param webSocketSession webSocket连接 | |||||
*/ | |||||
public void recv(String buffer, WebSocketSession webSocketSession) { | |||||
SSHSession sshSession = null; | |||||
try { | |||||
logger.debug("webSocketSessionID: {}, 信息: {}", webSocketSession.getId(), buffer); | |||||
JSONObject info = JSONObject.parseObject(buffer); | |||||
String tp = info.getString("tp"); | |||||
sshSession = findByWebSocketSession(webSocketSession); | |||||
//初始化连接 | |||||
if ("init".equals(tp)) { | |||||
// {"tp":"init","data":{"host":"127.0.0.1","port":"41080","username":"root","password":"123123"}} | |||||
SSHInfo sshInfo = info.getObject("data", SSHInfo.class); | |||||
sshSession.setSSHInfo(sshInfo); | |||||
if (sshSession != null) { | |||||
SSHSession finalSSHSession = sshSession; | |||||
// 新开一个线程建立连接,连接开启之后以一直监听来自客户端的输入 | |||||
executorService.execute(() -> { | |||||
connectTossh(finalSSHSession); | |||||
}); | |||||
} | |||||
} else if ("client".equals(tp)) { | |||||
String data = info.getString("data"); | |||||
// 将网页输入的数据传送给后端服务器 | |||||
if (sshSession != null) { | |||||
transTossh(sshSession.getOutputStream(), data); | |||||
} | |||||
} | |||||
} catch (Exception e) { | |||||
logger.error("转发命令到ssh出错: {}", e); | |||||
close(sshSession); | |||||
} | |||||
} | |||||
/** | |||||
* 将数据传送给服务端作为SSH的输入 | |||||
* | |||||
* @param outputStream | |||||
* @param data | |||||
* @throws IOException | |||||
*/ | |||||
private void transTossh(OutputStream outputStream, String data) throws IOException { | |||||
if (outputStream != null) { | |||||
outputStream.write(data.getBytes()); | |||||
outputStream.flush(); | |||||
} | |||||
} | |||||
/** | |||||
* 连接ssh | |||||
* | |||||
* @param sshSession ssh连接需要的信息 | |||||
*/ | |||||
private void connectTossh(SSHSession sshSession){ | |||||
Session jschSession = null; | |||||
SSHInfo SSHInfo = sshSession.getSSHInfo(); | |||||
try { | |||||
JSch jsch = new JSch(); | |||||
JSch.setLogger(jschLogger); | |||||
//启动线程 | |||||
java.util.Properties config = new java.util.Properties(); | |||||
config.put("StrictHostKeyChecking", "no"); | |||||
jschSession = jsch.getSession(SSHInfo.getUsername(), SSHInfo.getHost(), SSHInfo.getPort()); | |||||
jschSession.setConfig(config); | |||||
jschSession.setPassword(SSHInfo.getPassword()); | |||||
jschSession.setUserInfo(new UserInfo() { | |||||
@Override | |||||
public String getPassphrase() { | |||||
return null; | |||||
} | |||||
@Override | |||||
public String getPassword() { | |||||
return null; | |||||
} | |||||
@Override | |||||
public boolean promptPassword(String s) { | |||||
return false; | |||||
} | |||||
@Override | |||||
public boolean promptPassphrase(String s) { | |||||
return false; | |||||
} | |||||
@Override | |||||
public boolean promptYesNo(String s) { | |||||
return true; | |||||
} // Accept all server keys | |||||
@Override | |||||
public void showMessage(String s) { | |||||
} | |||||
}); | |||||
jschSession.connect(); | |||||
ChannelShell channel = (ChannelShell) jschSession.openChannel("shell"); | |||||
channel.setPtyType("xterm"); | |||||
channel.connect(); | |||||
sshSession.setChannel(channel); | |||||
InputStream inputStream = channel.getInputStream(); | |||||
sshSession.setOutputStream(channel.getOutputStream()); | |||||
sshSession.setSSHInfo(SSHInfo); | |||||
logger.debug("主机: {} 连接成功!", SSHInfo.getHost()); | |||||
// 循环读取,jsch的输入为服务器执行命令之后的返回数据 | |||||
byte[] buf = new byte[1024]; | |||||
while (true) { | |||||
int length = inputStream.read(buf); | |||||
if (length < 0) { | |||||
close(sshSession); | |||||
throw new Exception("读取出错,数据长度:" + length); | |||||
} | |||||
sendMsg(sshSession.getWebSocketSession(), Arrays.copyOfRange(buf, 0, length)); | |||||
} | |||||
} catch (Exception e) { | |||||
logger.error("ssh连接出错, e: {}", e); | |||||
} finally { | |||||
logger.info("连接关闭, {}", SSHInfo.getHost()); | |||||
if (jschSession != null) { | |||||
jschSession.disconnect(); | |||||
} | |||||
close(sshSession); | |||||
} | |||||
} | |||||
/** | |||||
* 发送数据回websocket | |||||
* | |||||
* @param webSocketSession webSocket连接 | |||||
* @param buffer 数据 | |||||
* @throws IOException | |||||
*/ | |||||
public void sendMsg(WebSocketSession webSocketSession, byte[] buffer) throws IOException { | |||||
logger.debug("服务端返回的数据: {}", new String(buffer, "UTF-8")); | |||||
webSocketSession.sendMessage(new TextMessage(Base64Util.encodeBytes(buffer))); | |||||
} | |||||
/** | |||||
* 通过webSocket连接在队列中找到对应的SSH连接 | |||||
* | |||||
* @param webSocketSession webSocket连接 | |||||
*/ | |||||
public SSHSession findByWebSocketSession(WebSocketSession webSocketSession) { | |||||
Optional<SSHSession> optional = sshSessionQueue.stream().filter(webscoketObj -> webscoketObj.getWebSocketSession() == webSocketSession).findFirst(); | |||||
if (optional.isPresent()) { | |||||
return optional.get(); | |||||
} | |||||
return null; | |||||
} | |||||
/** | |||||
* 关闭ssh和websocket连接 | |||||
* | |||||
* @param sshSession ssh连接 | |||||
*/ | |||||
private void close(SSHSession sshSession) { | |||||
if (sshSession != null) { | |||||
sshSession.getChannel().disconnect(); | |||||
try { | |||||
sshSession.getWebSocketSession().close(); | |||||
sshSession.getOutputStream().close(); | |||||
} catch (IOException e) { | |||||
logger.error("连接关闭失败!e: {}", e); | |||||
} | |||||
sshSessionQueue.remove(sshSession); | |||||
} | |||||
} | |||||
/** | |||||
* 通过webSocketSession关闭ssh与webSocket连接 | |||||
* | |||||
* @param webSocketSession | |||||
*/ | |||||
public void closeByWebSocket(WebSocketSession webSocketSession) { | |||||
close(findByWebSocketSession(webSocketSession)); | |||||
} | |||||
} |
@@ -0,0 +1,169 @@ | |||||
package depends.extractor.cpp.cdt; | |||||
import depends.entity.GenericName; | |||||
import depends.entity.TypeEntity; | |||||
import org.eclipse.cdt.core.dom.ast.*; | |||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier; | |||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; | |||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration; | |||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTemplateId; | |||||
import org.eclipse.cdt.internal.core.model.ASTStringUtil; | |||||
import java.lang.reflect.InvocationTargetException; | |||||
import java.lang.reflect.Method; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
/** | |||||
* This extends the CDT ASTStringUtil class. | |||||
* A tricky point here is that we have to use some of the reflection mechanism to invoke | |||||
* some private functions in ASTStringUtils class | |||||
* It is not good, but it seems the most easiest one to reuse existing functions | |||||
*/ | |||||
public class ASTStringUtilExt extends ASTStringUtil { | |||||
public static String getName(IASTDeclSpecifier decl) { | |||||
StringBuilder buffer = new StringBuilder(); | |||||
String name = appendBareDeclSpecifierString(buffer, decl).toString().replace("::", ".").replace("...", ""); | |||||
return name; | |||||
} | |||||
public static String getName(IASTLiteralExpression expr) { | |||||
return expr.getRawSignature().replace("::", ".").replace("...", ""); | |||||
} | |||||
public static String getTypeIdString(IASTTypeId typeId) { | |||||
StringBuilder sb = new StringBuilder(); | |||||
return appendBareTypeIdString(sb, typeId).toString().replace("::", "."); | |||||
} | |||||
/** | |||||
* retrieve template parameters from declSpecifier | |||||
*/ | |||||
public static List<GenericName> getTemplateParameters(IASTDeclSpecifier declSpecifier) { | |||||
List<GenericName> parameters = new ArrayList<>(); | |||||
declSpecifier.accept(new TemplateParameterASTVisitor(parameters)); | |||||
return parameters; | |||||
} | |||||
private static StringBuilder appendBareDeclSpecifierString(StringBuilder buffer, IASTDeclSpecifier declSpecifier) { | |||||
if (declSpecifier instanceof IASTCompositeTypeSpecifier) { | |||||
final IASTCompositeTypeSpecifier compositeTypeSpec = (IASTCompositeTypeSpecifier) declSpecifier; | |||||
appendBareNameString(buffer, compositeTypeSpec.getName()); | |||||
} else if (declSpecifier instanceof IASTElaboratedTypeSpecifier) { | |||||
final IASTElaboratedTypeSpecifier elaboratedTypeSpec = (IASTElaboratedTypeSpecifier) declSpecifier; | |||||
appendBareNameString(buffer, elaboratedTypeSpec.getName()); | |||||
} else if (declSpecifier instanceof IASTEnumerationSpecifier) { | |||||
final IASTEnumerationSpecifier enumerationSpec = (IASTEnumerationSpecifier) declSpecifier; | |||||
appendBareNameString(buffer, enumerationSpec.getName()); | |||||
} else if (declSpecifier instanceof IASTSimpleDeclSpecifier) { | |||||
buffer.append(TypeEntity.buildInType.getRawName()); | |||||
} else if (declSpecifier instanceof IASTNamedTypeSpecifier) { | |||||
final IASTNamedTypeSpecifier namedTypeSpec = (IASTNamedTypeSpecifier) declSpecifier; | |||||
appendBareNameString(buffer, namedTypeSpec.getName()); | |||||
} | |||||
return buffer; | |||||
} | |||||
private static StringBuilder appendBareNameString(StringBuilder buffer, IASTName name) { | |||||
if (name instanceof ICPPASTQualifiedName) { | |||||
final ICPPASTQualifiedName qualifiedName = (ICPPASTQualifiedName) name; | |||||
final ICPPASTNameSpecifier[] segments = qualifiedName.getAllSegments(); | |||||
for (int i = 0; i < segments.length; i++) { | |||||
if (i > 0) { | |||||
buffer.append("."); | |||||
} | |||||
appendQualifiedNameStringWithReflection(buffer, segments[i]); | |||||
} | |||||
} else if (name instanceof CPPASTTemplateId) { | |||||
appendQualifiedNameStringWithReflection(buffer,(CPPASTTemplateId)name); | |||||
} else if (name != null) { | |||||
buffer.append(name.getSimpleID()); | |||||
} | |||||
return buffer; | |||||
} | |||||
private static void appendQualifiedNameStringWithReflection(StringBuilder buffer, IASTName name) { | |||||
try { | |||||
Method m = ASTStringUtil.class.getDeclaredMethod("appendQualifiedNameString", StringBuilder.class, | |||||
IASTName.class); | |||||
m.setAccessible(true); // if security settings allow this | |||||
m.invoke(null, buffer, name); // use null if the method is static | |||||
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | |||||
| InvocationTargetException e) { | |||||
System.err.println("Error: cannot invoke ASTStringUtils method of <appendQualifiedNameString>"); | |||||
} | |||||
} | |||||
private static void appendQualifiedNameStringWithReflection(StringBuilder buffer, | |||||
CPPASTTemplateId templateId) { | |||||
appendQualifiedNameStringWithReflection(buffer,templateId.getTemplateName()); | |||||
} | |||||
private static void appendQualifiedNameStringWithReflection(StringBuilder buffer, | |||||
ICPPASTNameSpecifier nameSpecifier) { | |||||
if (nameSpecifier instanceof CPPASTTemplateId) { | |||||
appendQualifiedNameStringWithReflection(buffer,(CPPASTTemplateId)nameSpecifier); | |||||
return; | |||||
} | |||||
try { | |||||
Method m = ASTStringUtil.class.getDeclaredMethod("appendQualifiedNameString", StringBuilder.class, | |||||
ICPPASTNameSpecifier.class); | |||||
m.setAccessible(true); // if security settings allow this | |||||
m.invoke(null, buffer, nameSpecifier); // use null if the method is static | |||||
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | |||||
| InvocationTargetException e) { | |||||
System.err.println("Error: cannot invoke ASTStringUtils method of <appendQualifiedNameString>"); | |||||
} | |||||
} | |||||
private static StringBuilder appendBareTypeIdString(StringBuilder buffer, IASTTypeId typeId) { | |||||
return appendBareDeclSpecifierString(buffer, typeId.getDeclSpecifier()); | |||||
} | |||||
public static String getName(IASTDeclarator declarator) { | |||||
return declarator.getName().toString().replace("::", "."); | |||||
} | |||||
public static String getName(ICPPASTUsingDeclaration declaration) { | |||||
return declaration.getName().toString().replace("::", "."); | |||||
} | |||||
public static String getName(IASTName name) { | |||||
return name.getRawSignature().toString().replace("::", "."); | |||||
} | |||||
private static StringBuilder appendBareNameString(StringBuilder buffer, ICPPASTNameSpecifier name) { | |||||
if (name instanceof ICPPASTQualifiedName) { | |||||
final ICPPASTQualifiedName qualifiedName = (ICPPASTQualifiedName) name; | |||||
final ICPPASTNameSpecifier[] segments = qualifiedName.getAllSegments(); | |||||
for (int i = 0; i < segments.length; i++) { | |||||
if (i > 0) { | |||||
buffer.append("."); | |||||
} | |||||
appendQualifiedNameStringWithReflection(buffer, segments[i]); | |||||
} | |||||
} else if (name instanceof CPPASTTemplateId) { | |||||
appendQualifiedNameStringWithReflection(buffer,(CPPASTTemplateId)name); | |||||
} else if (name != null) { | |||||
buffer.append(name.getRawSignature()); | |||||
} | |||||
return buffer; | |||||
} | |||||
public static String getName(ICPPASTNameSpecifier nameSpecifier) { | |||||
StringBuilder buffer = new StringBuilder(); | |||||
String name = appendBareNameString(buffer, nameSpecifier).toString().replace("::", ".").replace("...", ""); | |||||
return name; | |||||
} | |||||
} |
@@ -0,0 +1,248 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor; | |||||
import depends.entity.Entity; | |||||
import depends.entity.FileEntity; | |||||
import depends.entity.repo.BuiltInType; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.entity.repo.InMemoryEntityRepo; | |||||
import depends.relations.ImportLookupStrategy; | |||||
import depends.relations.IBindingResolver; | |||||
import multilang.depends.util.file.FileTraversal; | |||||
import multilang.depends.util.file.FileUtil; | |||||
import org.codehaus.plexus.util.FileUtils; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.lang.management.ManagementFactory; | |||||
import java.util.ArrayList; | |||||
import java.util.HashSet; | |||||
import java.util.List; | |||||
import java.util.Set; | |||||
abstract public class AbstractLangProcessor { | |||||
/** | |||||
* The name of the lang | |||||
* | |||||
* @return | |||||
*/ | |||||
public abstract String supportedLanguage(); | |||||
/** | |||||
* The file suffixes in the lang | |||||
* | |||||
* @return | |||||
*/ | |||||
public abstract String[] fileSuffixes(); | |||||
/** | |||||
* Strategy of how to lookup types and entities in the lang. | |||||
* | |||||
* @return | |||||
*/ | |||||
public abstract ImportLookupStrategy getImportLookupStrategy(); | |||||
/** | |||||
* The builtInType of the lang. | |||||
* | |||||
* @return | |||||
*/ | |||||
public abstract BuiltInType getBuiltInType(); | |||||
/** | |||||
* The language specific file parser | |||||
* | |||||
* @param fileFullPath | |||||
* @return | |||||
*/ | |||||
public abstract FileParser createFileParser(); | |||||
public IBindingResolver bindingResolver; | |||||
protected EntityRepo entityRepo; | |||||
protected String inputSrcPath; | |||||
public String[] includeDirs; | |||||
private Set<UnsolvedBindings> potentialExternalDependencies; | |||||
private List<String> includePaths; | |||||
private static Logger logger = LoggerFactory.getLogger(AbstractLangProcessor.class); | |||||
public AbstractLangProcessor() { | |||||
entityRepo = new InMemoryEntityRepo(); | |||||
} | |||||
/** | |||||
* The process steps of build dependencies. Step 1: parse all files, add | |||||
* entities and expression into repositories Step 2: resolve bindings of files | |||||
* (if not resolved yet) Step 3: identify dependencies | |||||
* | |||||
* @param inputDir | |||||
* @param includeDir | |||||
* @param bindingResolver | |||||
* @return | |||||
*/ | |||||
public EntityRepo buildDependencies(String inputDir, String[] includeDir, IBindingResolver bindingResolver) { | |||||
this.inputSrcPath = inputDir; | |||||
this.includeDirs = includeDir; | |||||
this.bindingResolver = bindingResolver; | |||||
logger.info("Start parsing files..."); | |||||
parseAllFiles(); | |||||
markAllEntitiesScope(); | |||||
if (logger.isInfoEnabled()) { | |||||
logger.info("Resolve types and bindings of variables, methods and expressions.... " + this.inputSrcPath); | |||||
logger.info("Heap Information: " + ManagementFactory.getMemoryMXBean().getHeapMemoryUsage()); | |||||
} | |||||
resolveBindings(); | |||||
if (logger.isInfoEnabled()) { | |||||
System.gc(); | |||||
logger.info("Heap Information: " + ManagementFactory.getMemoryMXBean().getHeapMemoryUsage()); | |||||
} | |||||
return entityRepo; | |||||
} | |||||
private void markAllEntitiesScope() { | |||||
entityRepo.getFileEntities().stream().forEach(entity -> { | |||||
Entity file = entity.getAncestorOfType(FileEntity.class); | |||||
try { | |||||
if (!file.getQualifiedName().startsWith(this.inputSrcPath)) { | |||||
entity.setInScope(false); | |||||
} | |||||
} catch (Exception e) { | |||||
} | |||||
}); | |||||
} | |||||
/** | |||||
* @return unsolved bindings | |||||
*/ | |||||
public void resolveBindings() { | |||||
System.out.println("Resolve types and bindings of variables, methods and expressions...."); | |||||
this.potentialExternalDependencies = bindingResolver.resolveAllBindings(this.isEagerExpressionResolve()); | |||||
if (getExternalDependencies().size() > 0) { | |||||
System.out.println("There are " + getExternalDependencies().size() + " items are potential external dependencies."); | |||||
} | |||||
System.out.println("types and bindings resolved successfully..."); | |||||
} | |||||
private final void parseAllFiles() { | |||||
System.out.println("Start parsing files..."); | |||||
Set<String> phase2Files = new HashSet<>(); | |||||
FileTraversal fileTransversal = new FileTraversal(new FileTraversal.IFileVisitor() { | |||||
@Override | |||||
public void visit(File file) { | |||||
String fileFullPath = file.getAbsolutePath(); | |||||
if (!fileFullPath.startsWith(inputSrcPath)) { | |||||
return; | |||||
} | |||||
parseFile(fileFullPath, phase2Files); | |||||
} | |||||
}); | |||||
fileTransversal.extensionFilter(this.fileSuffixes()); | |||||
fileTransversal.travers(this.inputSrcPath); | |||||
for (String f : phase2Files) { | |||||
parseFile(f, phase2Files); | |||||
} | |||||
System.out.println("all files procceed successfully..."); | |||||
} | |||||
protected void parseFile(String fileFullPath, Set<String> phase2Files) { | |||||
FileParser fileParser = createFileParser(); | |||||
try { | |||||
if (fileParser.isPhase2Files(fileFullPath)){ | |||||
phase2Files.add(fileFullPath); | |||||
}else { | |||||
fileParser.parse(fileFullPath); | |||||
} | |||||
} catch (IOException e) { | |||||
e.printStackTrace(); | |||||
} catch (Exception e) { | |||||
System.err.println("error occoured during parse file " + fileFullPath); | |||||
e.printStackTrace(); | |||||
} | |||||
} | |||||
public List<String> includePaths() { | |||||
if (this.includePaths ==null) { | |||||
this.includePaths = buildIncludePath(); | |||||
} | |||||
return includePaths; | |||||
} | |||||
private List<String> buildIncludePath() { | |||||
includePaths = new ArrayList<String>(); | |||||
for (String path : includeDirs) { | |||||
if (FileUtils.fileExists(path)) { | |||||
path = FileUtil.uniqFilePath(path); | |||||
if (!includePaths.contains(path)) | |||||
includePaths.add(path); | |||||
} | |||||
path = this.inputSrcPath + File.separator + path; | |||||
if (FileUtils.fileExists(path)) { | |||||
path = FileUtil.uniqFilePath(path); | |||||
if (!includePaths.contains(path)) | |||||
includePaths.add(path); | |||||
} | |||||
} | |||||
return includePaths; | |||||
} | |||||
public EntityRepo getEntityRepo() { | |||||
return this.entityRepo; | |||||
} | |||||
public abstract List<String> supportedRelations(); | |||||
public Set<UnsolvedBindings> getExternalDependencies() { | |||||
return potentialExternalDependencies; | |||||
} | |||||
public String getRelationMapping(String relation) { | |||||
return relation; | |||||
} | |||||
/** | |||||
* Whether to resolve expression immediately during parse | |||||
* @return | |||||
*/ | |||||
public boolean isEagerExpressionResolve(){ | |||||
return false; | |||||
} | |||||
/** | |||||
* Call as Impl: | |||||
* implicit call (for example polymorphic in cpp) | |||||
* @return | |||||
*/ | |||||
public boolean supportCallAsImpl(){return false;}; | |||||
} |
@@ -0,0 +1,203 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.entity; | |||||
import depends.relations.IBindingResolver; | |||||
import java.util.*; | |||||
public class AliasEntity extends Entity { | |||||
private Entity referToEntity = new EmptyTypeEntity(); | |||||
private GenericName originName; | |||||
private List<Entity> referPath = new ArrayList<>(); | |||||
private boolean deepResolve = false; | |||||
public AliasEntity() { | |||||
} | |||||
public AliasEntity(GenericName simpleName, Entity parent, Integer id, GenericName originTypeName) { | |||||
super(simpleName, parent, id); | |||||
this.originName = originTypeName; | |||||
} | |||||
public void inferLocalLevelEntities(IBindingResolver bindingResolver) { | |||||
if (!(referToEntity instanceof EmptyTypeEntity)) return; | |||||
Entity entity = bindingResolver.resolveName(this, originName, true); | |||||
while(entity instanceof AliasEntity) { | |||||
AliasEntity aliasEntity = (AliasEntity)entity; | |||||
if (this.referPath.contains(aliasEntity)) { | |||||
entity = null; | |||||
break; | |||||
} | |||||
this.referPath.add(aliasEntity); | |||||
entity = bindingResolver.resolveName(aliasEntity, aliasEntity.originName,true); | |||||
if (entity==null) break; | |||||
if (entity.equals(this)) { | |||||
entity = null; | |||||
break; | |||||
} | |||||
} | |||||
if (entity != null) | |||||
referToEntity = entity; | |||||
} | |||||
public Collection<Entity> getResolvedTypeParameters() { | |||||
if (!(referToEntity instanceof DecoratedEntity)) | |||||
return new ArrayList<>(); | |||||
DecoratedEntity origin = (DecoratedEntity) referToEntity; | |||||
return origin.getResolvedTypeParameters(); | |||||
} | |||||
public Collection<Entity> getResolvedAnnotations() { | |||||
if (!(referToEntity instanceof DecoratedEntity)) | |||||
return new ArrayList<>(); | |||||
DecoratedEntity origin = (DecoratedEntity) referToEntity; | |||||
return origin.getResolvedAnnotations(); | |||||
} | |||||
public ArrayList<VarEntity> getVars() { | |||||
if (!(referToEntity instanceof ContainerEntity)) | |||||
return new ArrayList<>(); | |||||
ContainerEntity origin = (ContainerEntity) referToEntity; | |||||
return origin.getVars(); | |||||
} | |||||
public ArrayList<FunctionEntity> getFunctions() { | |||||
if (!(referToEntity instanceof ContainerEntity)) | |||||
return new ArrayList<>(); | |||||
ContainerEntity origin = (ContainerEntity) referToEntity; | |||||
return origin.getFunctions(); | |||||
} | |||||
protected FunctionEntity lookupFunctionLocally(GenericName functionName) { | |||||
if (!(referToEntity instanceof ContainerEntity)) | |||||
return null; | |||||
ContainerEntity origin = (ContainerEntity) referToEntity; | |||||
return origin.lookupFunctionLocally(functionName); | |||||
} | |||||
public List<Entity> lookupFunctionInVisibleScope(GenericName functionName) { | |||||
if (!(referToEntity instanceof ContainerEntity)) | |||||
return null; | |||||
ContainerEntity origin = (ContainerEntity) referToEntity; | |||||
return origin.lookupFunctionInVisibleScope(functionName); | |||||
} | |||||
public Entity lookupVarsInVisibleScope(GenericName varName) { | |||||
if (!(referToEntity instanceof ContainerEntity)) | |||||
return null; | |||||
ContainerEntity origin = (ContainerEntity) referToEntity; | |||||
return origin.lookupVarInVisibleScope(varName); | |||||
} | |||||
public Collection<ContainerEntity> getResolvedMixins() { | |||||
if (!(referToEntity instanceof ContainerEntity)) | |||||
return new ArrayList<>(); | |||||
ContainerEntity origin = (ContainerEntity) referToEntity; | |||||
return origin.getResolvedMixins(); | |||||
} | |||||
public Collection<TypeEntity> getInheritedTypes() { | |||||
if (referToEntity instanceof TypeEntity) | |||||
return ((TypeEntity) referToEntity).getInheritedTypes(); | |||||
return new ArrayList<>(); | |||||
} | |||||
public Collection<TypeEntity> getImplementedTypes() { | |||||
if (referToEntity instanceof TypeEntity) | |||||
return ((TypeEntity) referToEntity).getImplementedTypes(); | |||||
return new ArrayList<>(); | |||||
} | |||||
public TypeEntity getInheritedType() { | |||||
if (referToEntity instanceof TypeEntity) | |||||
return ((TypeEntity) referToEntity).getInheritedType(); | |||||
return null; | |||||
} | |||||
public Collection<Entity> getReturnTypes() { | |||||
if (!(referToEntity instanceof FunctionEntity)) | |||||
return new ArrayList<>(); | |||||
FunctionEntity origin = (FunctionEntity) referToEntity; | |||||
return origin.getReturnTypes(); | |||||
} | |||||
public TypeEntity getType() { | |||||
return referToEntity.getType(); | |||||
} | |||||
public Collection<VarEntity> getParameters() { | |||||
if (!(referToEntity instanceof FunctionEntity)) | |||||
return new ArrayList<>(); | |||||
FunctionEntity origin = (FunctionEntity) referToEntity; | |||||
return origin.getParameters(); | |||||
} | |||||
public Collection<Entity> getThrowTypes() { | |||||
if (!(referToEntity instanceof FunctionEntity)) | |||||
return new ArrayList<>(); | |||||
FunctionEntity origin = (FunctionEntity) referToEntity; | |||||
return origin.getThrowTypes(); | |||||
} | |||||
public Entity getOriginType() { | |||||
return referToEntity; | |||||
} | |||||
public Entity getReferToEntity() { | |||||
return referToEntity; | |||||
} | |||||
public void setReferToEntity(Entity referToEntity) { | |||||
this.referToEntity = referToEntity; | |||||
} | |||||
public Entity deepResolve() { | |||||
if (!deepResolve) return this; | |||||
Set<Entity> searched = new HashSet<>(); | |||||
int i=0; | |||||
Entity current = this; | |||||
while(i<100) { //maximum 100 levels | |||||
if (searched.contains(current)) return current; //avoid a loop | |||||
if (!(current instanceof AliasEntity)) return current; | |||||
searched.add(current); | |||||
Entity originalFile = current.getAncestorOfType(FileEntity.class); | |||||
current = ((AliasEntity)current).getReferToEntity(); | |||||
if (current ==null) return this; | |||||
//if already out of current file, return current | |||||
if (!current.getAncestorOfType(FileEntity.class).equals(originalFile)) { | |||||
return current; | |||||
} | |||||
i++; | |||||
} | |||||
return current; | |||||
} | |||||
public boolean isDeepResolve() { | |||||
return deepResolve; | |||||
} | |||||
public void setDeepResolve(boolean deepResolve) { | |||||
this.deepResolve = deepResolve; | |||||
} | |||||
} |
@@ -0,0 +1,121 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor.java.context; | |||||
import java.lang.reflect.InvocationTargetException; | |||||
import java.lang.reflect.Method; | |||||
import java.util.ArrayList; | |||||
import java.util.Collection; | |||||
import java.util.HashSet; | |||||
import java.util.List; | |||||
import java.util.stream.Collectors; | |||||
import org.antlr.v4.runtime.RuleContext; | |||||
import org.codehaus.plexus.util.StringUtils; | |||||
import depends.entity.ContainerEntity; | |||||
import depends.entity.GenericName; | |||||
import depends.extractor.java.JavaParser.AnnotationContext; | |||||
public class AnnotationProcessor { | |||||
public AnnotationProcessor() { | |||||
} | |||||
public void processAnnotationModifier(RuleContext ctx, @SuppressWarnings("rawtypes") Class rootClass, | |||||
String toAnnotationPath,ContainerEntity container) { | |||||
List<ContainerEntity> list = new ArrayList<>() ; | |||||
list.add(container); | |||||
processAnnotationModifier(ctx, rootClass, | |||||
toAnnotationPath, list); | |||||
} | |||||
public void processAnnotationModifier(RuleContext ctx, @SuppressWarnings("rawtypes") Class rootClass, | |||||
String toAnnotationPath, List<?> containers) { | |||||
while (true) { | |||||
if (ctx == null) | |||||
break; | |||||
if (ctx.getClass().equals(rootClass)) | |||||
break; | |||||
ctx = ctx.parent; | |||||
} | |||||
if (ctx == null) | |||||
return; | |||||
try { | |||||
Object r = ctx; | |||||
String[] paths = toAnnotationPath.split("\\."); | |||||
for (String path : paths) { | |||||
r = invokeMethod(r, path); | |||||
if (r == null) | |||||
return; | |||||
} | |||||
Collection<AnnotationContext> contexts = new HashSet<>(); | |||||
mergeElements(contexts, r); | |||||
for (Object item : contexts) { | |||||
AnnotationContext annotation = (AnnotationContext) item; | |||||
String name = QualitiedNameContextHelper.getName(annotation.qualifiedName()); | |||||
containers.stream().forEach(container->((ContainerEntity)container).addAnnotation(GenericName.build(name))); | |||||
} | |||||
} catch (Exception e) { | |||||
return; | |||||
} | |||||
} | |||||
private void mergeElements(Collection<AnnotationContext> collection, Object r) { | |||||
if (r instanceof Collection) { | |||||
for (Object item : (Collection<?>) r) { | |||||
mergeElements(collection, item); | |||||
} | |||||
} else { | |||||
if (r instanceof AnnotationContext) | |||||
collection.add((AnnotationContext) r); | |||||
} | |||||
} | |||||
private Object invokeMethod(Object r, String path) { | |||||
if (StringUtils.isEmpty(path)) | |||||
return null; | |||||
if (r instanceof Collection) { | |||||
Collection<?> list = (Collection<?>) r; | |||||
return list.stream().map(item -> invokeMethod(item, path)).filter(item -> item != null) | |||||
.collect(Collectors.toSet()); | |||||
} | |||||
try { | |||||
Method m = r.getClass().getMethod(path); | |||||
return m.invoke(r); | |||||
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | |||||
| InvocationTargetException e) { | |||||
return null; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,297 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.relations; | |||||
import depends.entity.*; | |||||
import depends.entity.repo.BuiltInType; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.extractor.AbstractLangProcessor; | |||||
import depends.extractor.UnsolvedBindings; | |||||
import depends.extractor.empty.EmptyBuiltInType; | |||||
import depends.importtypes.Import; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import java.lang.management.ManagementFactory; | |||||
import java.util.*; | |||||
public class BindingResolver implements IBindingResolver{ | |||||
private BuiltInType buildInTypeManager = new EmptyBuiltInType(); | |||||
private ImportLookupStrategy importLookupStrategy; | |||||
private Set<UnsolvedBindings> unsolvedSymbols = new HashSet<>(); | |||||
private EntityRepo repo; | |||||
private boolean eagerExpressionResolve = false; | |||||
private boolean isCollectUnsolvedBindings = false; | |||||
private boolean isDuckTypingDeduce = true; | |||||
private static Logger logger = LoggerFactory.getLogger(IBindingResolver.class); | |||||
public BindingResolver(AbstractLangProcessor langProcessor, | |||||
boolean isCollectUnsolvedBindings, boolean isDuckTypingDeduce) { | |||||
this.repo = langProcessor.getEntityRepo(); | |||||
this.importLookupStrategy = langProcessor.getImportLookupStrategy(); | |||||
this.buildInTypeManager = langProcessor.getBuiltInType(); | |||||
this.isCollectUnsolvedBindings = isCollectUnsolvedBindings; | |||||
this.isDuckTypingDeduce = isDuckTypingDeduce; | |||||
unsolvedSymbols= new HashSet<>(); | |||||
importLookupStrategy.setBindingResolver(this); | |||||
} | |||||
@Override | |||||
public Set<UnsolvedBindings> resolveAllBindings(boolean isEagerExpressionResolve) { | |||||
System.out.println("Resolve type bindings...."); | |||||
if (logger.isInfoEnabled()) { | |||||
logger.info("Resolve type bindings..."); | |||||
} | |||||
resolveTypes(isEagerExpressionResolve); | |||||
System.out.println("Dependency analaysing...."); | |||||
if (logger.isInfoEnabled()) { | |||||
logger.info("Dependency analaysing..."); | |||||
} | |||||
logger.info("Heap Information: " + ManagementFactory.getMemoryMXBean().getHeapMemoryUsage()); | |||||
return unsolvedSymbols; | |||||
} | |||||
private void resolveTypes(boolean eagerExpressionResolve) { | |||||
this.eagerExpressionResolve = eagerExpressionResolve; | |||||
Iterator<Entity> iterator = repo.sortedFileIterator(); | |||||
while(iterator.hasNext()) { | |||||
Entity entity= iterator.next(); | |||||
entity.inferEntities(this); | |||||
} | |||||
} | |||||
@Override | |||||
public Collection<Entity> getImportedRelationEntities(List<Import> importedNames) { | |||||
return importLookupStrategy.getImportedRelationEntities(importedNames); | |||||
} | |||||
@Override | |||||
public Collection<Entity> getImportedTypes(List<Import> importedNames, FileEntity fileEntity) { | |||||
HashSet<UnsolvedBindings> unsolved = new HashSet<UnsolvedBindings>(); | |||||
Collection<Entity> result = importLookupStrategy.getImportedTypes(importedNames,unsolved); | |||||
for (UnsolvedBindings item:unsolved) { | |||||
item.setFromEntity(fileEntity); | |||||
addUnsolvedBinding(item); | |||||
} | |||||
return result; | |||||
} | |||||
private void addUnsolvedBinding(UnsolvedBindings item) { | |||||
if (!isCollectUnsolvedBindings) return; | |||||
this.unsolvedSymbols.add(item); | |||||
} | |||||
@Override | |||||
public Collection<Entity> getImportedFiles(List<Import> importedNames) { | |||||
return importLookupStrategy.getImportedFiles(importedNames); | |||||
} | |||||
@Override | |||||
public TypeEntity inferTypeFromName(Entity fromEntity, GenericName rawName) { | |||||
Entity data = resolveName(fromEntity, rawName, true); | |||||
if (data == null) | |||||
return null; | |||||
return data.getType(); | |||||
} | |||||
@Override | |||||
public Entity resolveName(Entity fromEntity, GenericName rawName, boolean searchImport) { | |||||
if (rawName==null) return null; | |||||
Entity entity = resolveNameInternal(fromEntity,rawName,searchImport); | |||||
if (entity==null ) { | |||||
if (!this.buildInTypeManager.isBuiltInType(rawName.getName())) { | |||||
addUnsolvedBinding(new UnsolvedBindings(rawName.getName(), fromEntity)); | |||||
} | |||||
} | |||||
return entity; | |||||
} | |||||
private Entity resolveNameInternal(Entity fromEntity, GenericName rawName, boolean searchImport) { | |||||
if (rawName==null || rawName.getName()==null) | |||||
return null; | |||||
if (buildInTypeManager.isBuiltInType(rawName.getName())) { | |||||
return TypeEntity.buildInType; | |||||
} | |||||
// qualified name will first try global name directly | |||||
if (rawName.startsWith(".")) { | |||||
rawName = rawName.substring(1); | |||||
if (repo.getEntity(rawName) != null) | |||||
return repo.getEntity(rawName); | |||||
} | |||||
Entity entity = null; | |||||
int indexCount = 0; | |||||
String name = rawName.getName(); | |||||
if (fromEntity==null) return null; | |||||
do { | |||||
entity = lookupEntity(fromEntity, name, searchImport); | |||||
if (entity!=null ) { | |||||
break; | |||||
} | |||||
if (importLookupStrategy.supportGlobalNameLookup()) { | |||||
if (repo.getEntity(name) != null) { | |||||
entity = repo.getEntity(name); | |||||
break; | |||||
} | |||||
} | |||||
indexCount++; | |||||
if (name.contains(".")) | |||||
name = name.substring(0,name.lastIndexOf('.')); | |||||
else | |||||
break; | |||||
}while (true); | |||||
if (entity == null) { | |||||
return null; | |||||
} | |||||
String[] names = rawName.getName().split("\\."); | |||||
if (names.length == 0) | |||||
return null; | |||||
if (names.length == 1) { | |||||
return entity; | |||||
} | |||||
// then find the subsequent symbols | |||||
return findEntitySince(entity, names, names.length-indexCount); | |||||
} | |||||
private Entity lookupEntity(Entity fromEntity, String name, boolean searchImport) { | |||||
if (name.equals("this") || name.equals("class") ) { | |||||
TypeEntity entityType = (TypeEntity) (fromEntity.getAncestorOfType(TypeEntity.class)); | |||||
return entityType; | |||||
} else if (name.equals("super")) { | |||||
TypeEntity parent = (TypeEntity) (fromEntity.getAncestorOfType(TypeEntity.class)); | |||||
if (parent != null) { | |||||
TypeEntity parentType = parent.getInheritedType(); | |||||
if (parentType!=null) | |||||
return parentType; | |||||
} | |||||
} | |||||
Entity inferData = findEntityUnderSamePackage(fromEntity, name); | |||||
if (inferData != null) { | |||||
return inferData; | |||||
} | |||||
if (searchImport) | |||||
inferData = lookupTypeInImported((FileEntity)(fromEntity.getAncestorOfType(FileEntity.class)), name); | |||||
return inferData; | |||||
} | |||||
/** | |||||
* To lookup entity in case of a.b.c from a; | |||||
* @param precendenceEntity | |||||
* @param names | |||||
* @param nameIndex | |||||
* @return | |||||
*/ | |||||
private Entity findEntitySince(Entity precendenceEntity, String[] names, int nameIndex) { | |||||
if (nameIndex >= names.length) { | |||||
return precendenceEntity; | |||||
} | |||||
if (nameIndex == -1) { | |||||
System.err.println("No expected symbols: names"+Arrays.toString(names) +", index=" + nameIndex); | |||||
return null; | |||||
} | |||||
//If it is not an entity with types (not a type, var, function), fall back to itself | |||||
if (precendenceEntity.getType()==null) | |||||
return precendenceEntity; | |||||
for (Entity child : precendenceEntity.getType().getChildren()) { | |||||
if (child.getRawName().getName().equals(names[nameIndex])) { | |||||
return findEntitySince(child, names, nameIndex + 1); | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
@Override | |||||
public Entity lookupTypeInImported(FileEntity fileEntity, String name) { | |||||
if (fileEntity == null) | |||||
return null; | |||||
Entity type = importLookupStrategy.lookupImportedType(name, fileEntity); | |||||
if (type != null) | |||||
return type; | |||||
return null; | |||||
} | |||||
/** | |||||
* In Java/C++ etc, the same package names should take priority of resolving. | |||||
* the entity lookup is implemented recursively. | |||||
* @param fromEntity | |||||
* @param name | |||||
* @return | |||||
*/ | |||||
private Entity findEntityUnderSamePackage(Entity fromEntity, String name) { | |||||
while (true) { | |||||
Entity entity = fromEntity.getByName(name, new HashSet<>()); | |||||
if (entity!=null) return entity; | |||||
fromEntity = fromEntity.getParent(); | |||||
if (fromEntity == null) | |||||
break; | |||||
} | |||||
return null; | |||||
} | |||||
@Override | |||||
public List<TypeEntity> calculateCandidateTypes(VarEntity fromEntity, List<FunctionCall> functionCalls) { | |||||
if (buildInTypeManager.isBuildInTypeMethods(functionCalls)) { | |||||
return new ArrayList<>(); | |||||
} | |||||
if (!isDuckTypingDeduce) | |||||
return new ArrayList<>(); | |||||
return searchTypesInRepo(fromEntity, functionCalls); | |||||
} | |||||
private List<TypeEntity> searchTypesInRepo(VarEntity fromEntity, List<FunctionCall> functionCalls) { | |||||
List<TypeEntity> types = new ArrayList<>(); | |||||
Iterator<Entity> iterator = repo.sortedFileIterator(); | |||||
while(iterator.hasNext()) { | |||||
Entity f = iterator.next(); | |||||
if (f instanceof FileEntity) { | |||||
for (TypeEntity type:((FileEntity)f).getDeclaredTypes()) { | |||||
FunctionMatcher functionMatcher = new FunctionMatcher(type.getFunctions()); | |||||
if (functionMatcher.containsAll(functionCalls)) { | |||||
types.add(type); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
return types; | |||||
} | |||||
@Override | |||||
public boolean isEagerExpressionResolve() { | |||||
return eagerExpressionResolve; | |||||
} | |||||
@Override | |||||
public EntityRepo getRepo() { | |||||
return repo; | |||||
} | |||||
} |
@@ -0,0 +1,110 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.entity.repo; | |||||
import depends.entity.FunctionCall; | |||||
import depends.entity.TypeEntity; | |||||
import java.util.HashSet; | |||||
import java.util.List; | |||||
import java.util.Set; | |||||
public abstract class BuiltInType { | |||||
public BuiltInType(){ | |||||
createBuiltInTypes(); | |||||
} | |||||
/** | |||||
* Init the build in types data | |||||
*/ | |||||
private void createBuiltInTypes() { | |||||
for(String prefix: getBuiltInTypePrefix()) { | |||||
builtInPrefix.add(prefix); | |||||
} | |||||
for (String type: getBuiltInTypeName()) { | |||||
builtInType.add(type); | |||||
} | |||||
for (String method:getBuiltInMethods()) { | |||||
builtInMethod.add(method); | |||||
} | |||||
} | |||||
protected String[] getBuiltInMethods(){return new String[]{};} | |||||
protected String[] getBuiltInTypeName(){return new String[]{};} | |||||
protected String[] getBuiltInTypePrefix() {return new String[]{};} | |||||
private Set<String> builtInType = new HashSet<>(); | |||||
private Set<String> builtInPrefix = new HashSet<>(); | |||||
private Set<String> builtInMethod = new HashSet<>(); | |||||
/** | |||||
* To determine whether a type name is built-in | |||||
* @param typeName | |||||
* @return | |||||
*/ | |||||
public boolean isBuiltInType(String typeName) { | |||||
return TypeEntity.buildInType.getRawName().uniqName().equals(typeName) || | |||||
builtInType.contains(typeName)|| | |||||
isBuiltInTypePrefix(typeName); | |||||
} | |||||
/** | |||||
* To determine a typeName is a built-in type based on prefix. | |||||
* For example, in Java language, name start with java.*, javax.*, com.sun.* | |||||
* is build-in types | |||||
* @param typeName | |||||
* @return | |||||
*/ | |||||
private boolean isBuiltInTypePrefix(String typeName) { | |||||
for (String prefix:builtInPrefix) { | |||||
if (typeName.startsWith(prefix)) return true; | |||||
} | |||||
return false; | |||||
} | |||||
/** | |||||
* In some language, there are common methods, like in ruby, | |||||
* object_id is a method for all type | |||||
* @param name | |||||
* @return | |||||
*/ | |||||
public boolean isBuildInMethod(String name) { | |||||
return builtInMethod.contains(name); | |||||
} | |||||
/** | |||||
* Used by duck typing deduce feature: | |||||
* - if all calls of a type are build in method, | |||||
* then no duck typing is deduced | |||||
* Refer to Python built-in type for example | |||||
* | |||||
* @param functionCalls | |||||
* @return | |||||
*/ | |||||
public boolean isBuildInTypeMethods(List<FunctionCall> functionCalls) { | |||||
return false; | |||||
} | |||||
} |
@@ -0,0 +1,311 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.entity; | |||||
import depends.relations.IBindingResolver; | |||||
import depends.relations.Relation; | |||||
import java.util.*; | |||||
public class CandidateTypes extends TypeEntity { | |||||
private List<TypeEntity> candidateTypes; | |||||
public CandidateTypes() { | |||||
candidateTypes = new ArrayList<>(); | |||||
} | |||||
public CandidateTypes(List<TypeEntity> candidateTypes, Integer id) { | |||||
super(GenericName.build("candidateTypes"), null, id); | |||||
this.candidateTypes = candidateTypes; | |||||
} | |||||
public List<TypeEntity> getCandidateTypes() { | |||||
return candidateTypes; | |||||
} | |||||
@Override | |||||
public Collection<TypeEntity> getInheritedTypes() { | |||||
List<TypeEntity> result = new ArrayList<>(); | |||||
for (TypeEntity type:candidateTypes) { | |||||
result.addAll(type.getInheritedTypes()); | |||||
} | |||||
return result; | |||||
} | |||||
@Override | |||||
public Collection<TypeEntity> getImplementedTypes() { | |||||
List<TypeEntity> result = new ArrayList<>(); | |||||
for (TypeEntity type:candidateTypes) { | |||||
result.addAll(type.getImplementedTypes()); | |||||
} | |||||
return result; | |||||
} | |||||
@Override | |||||
public ArrayList<FunctionEntity> getFunctions() { | |||||
ArrayList<FunctionEntity> result = new ArrayList<>(); | |||||
for (TypeEntity type:candidateTypes) { | |||||
result.addAll(type.getFunctions()); | |||||
} | |||||
return result; | |||||
} | |||||
@Override | |||||
public TypeEntity getInheritedType() { | |||||
return inheritedType; | |||||
} | |||||
@Override | |||||
public List<Entity> lookupFunctionInVisibleScope(GenericName functionName) { | |||||
List<Entity> functions = new ArrayList<>(); | |||||
for (TypeEntity type:candidateTypes) { | |||||
List<Entity> f = type.lookupFunctionInVisibleScope(functionName); | |||||
if (f!=null) { | |||||
functions.addAll(f); | |||||
} | |||||
} | |||||
if (functions.size()==0) | |||||
return null; | |||||
return functions; | |||||
} | |||||
@Override | |||||
public Entity lookupVarInVisibleScope(GenericName varName) { | |||||
for (TypeEntity type:candidateTypes) { | |||||
Entity v = type.lookupVarInVisibleScope(varName); | |||||
if (v!=null) return v; | |||||
} | |||||
return null; | |||||
} | |||||
@Override | |||||
public VarEntity lookupVarLocally(String varName) { | |||||
for (TypeEntity type:candidateTypes) { | |||||
VarEntity v = type.lookupVarLocally(varName); | |||||
if (v!=null) return v; | |||||
} | |||||
return null; | |||||
} | |||||
@Override | |||||
public TypeEntity getType() { | |||||
if (candidateTypes.size()>0) return candidateTypes.get(0); | |||||
return null; | |||||
} | |||||
@Override | |||||
public void inferLocalLevelEntities(IBindingResolver bindingResolver) { | |||||
System.err.println("error: inferLocalLevelEntities should not been invoked"); | |||||
super.inferLocalLevelEntities(bindingResolver); | |||||
} | |||||
@Override | |||||
public void addImplements(GenericName typeName) { | |||||
System.err.println("error: addImplements should not been invoked"); | |||||
super.addImplements(typeName); | |||||
} | |||||
@Override | |||||
public void addExtends(GenericName typeName) { | |||||
System.err.println("error: addExtends should not been invoked"); | |||||
super.addExtends(typeName); | |||||
} | |||||
@Override | |||||
public void addVar(VarEntity var) { | |||||
System.err.println("error: addVar should not been invoked"); | |||||
super.addVar(var); | |||||
} | |||||
@Override | |||||
public ArrayList<VarEntity> getVars() { | |||||
System.err.println("error: getVars should not been invoked"); | |||||
return super.getVars(); | |||||
} | |||||
@Override | |||||
public void addFunction(FunctionEntity functionEntity) { | |||||
System.err.println("error: addFunction should not been invoked"); | |||||
super.addFunction(functionEntity); | |||||
} | |||||
@Override | |||||
public HashMap<Object, Expression> expressions() { | |||||
System.err.println("error: expressions should not been invoked"); | |||||
return super.expressions(); | |||||
} | |||||
@Override | |||||
public void addExpression(Object key, Expression expression) { | |||||
System.err.println("error: addExpression should not been invoked"); | |||||
super.addExpression(key, expression); | |||||
} | |||||
public void resolveExpressions(IBindingResolver bindingResolver) { | |||||
System.err.println("error: resolveExpressions should not been invoked"); | |||||
super.resolveExpressions(bindingResolver); | |||||
} | |||||
@Override | |||||
public void addMixin(GenericName moduleName) { | |||||
System.err.println("error: addMixin should not been invoked"); | |||||
super.addMixin(moduleName); | |||||
} | |||||
@Override | |||||
public Collection<ContainerEntity> getResolvedMixins() { | |||||
System.err.println("error: getResolvedMixins should not been invoked"); | |||||
return super.getResolvedMixins(); | |||||
} | |||||
@Override | |||||
public void addAnnotation(GenericName name) { | |||||
System.err.println("error: addAnnotation should not been invoked"); | |||||
super.addAnnotation(name); | |||||
} | |||||
@Override | |||||
public Collection<Entity> getResolvedTypeParameters() { | |||||
System.err.println("error: getResolvedTypeParameters should not been invoked"); | |||||
return super.getResolvedTypeParameters(); | |||||
} | |||||
@Override | |||||
public Collection<Entity> getResolvedAnnotations() { | |||||
System.err.println("error: getResolvedAnnotations should not been invoked"); | |||||
return super.getResolvedAnnotations(); | |||||
} | |||||
@Override | |||||
public boolean isGenericTypeParameter(GenericName rawType) { | |||||
System.err.println("error: isGenericTypeParameter should not been invoked"); | |||||
return super.isGenericTypeParameter(rawType); | |||||
} | |||||
@Override | |||||
protected Collection<Entity> identiferToEntities(IBindingResolver bindingResolver, Collection<GenericName> identifiers) { | |||||
System.err.println("error: identiferToTypes should not been invoked"); | |||||
return super.identiferToEntities(bindingResolver, identifiers); | |||||
} | |||||
@Override | |||||
public GenericName getRawName() { | |||||
return super.getRawName(); | |||||
} | |||||
@Override | |||||
public Integer getId() { | |||||
return super.getId(); | |||||
} | |||||
@Override | |||||
public void addRelation(Relation relation) { | |||||
System.err.println("error: addRelation should not been invoked"); | |||||
super.addRelation(relation); | |||||
} | |||||
@Override | |||||
public ArrayList<Relation> getRelations() { | |||||
System.err.println("error: getRelations should not been invoked"); | |||||
return super.getRelations(); | |||||
} | |||||
@Override | |||||
public void addChild(Entity child) { | |||||
System.err.println("error: addChild should not been invoked"); | |||||
super.addChild(child); | |||||
} | |||||
@Override | |||||
public Entity getParent() { | |||||
return null; | |||||
} | |||||
@Override | |||||
public void setParent(Entity parent) { | |||||
System.err.println("error: setParent should not been invoked"); | |||||
super.setParent(parent); | |||||
} | |||||
@Override | |||||
public Collection<Entity> getChildren() { | |||||
List<Entity> children = new ArrayList<>(); | |||||
for (Entity entity:this.candidateTypes) { | |||||
children.addAll(entity.getChildren()); | |||||
} | |||||
return children; | |||||
} | |||||
@Override | |||||
public void setQualifiedName(String qualifiedName) { | |||||
System.err.println("error: setQualifiedName should not been invoked"); | |||||
super.setQualifiedName(qualifiedName); | |||||
} | |||||
@Override | |||||
public void setRawName(GenericName rawName) { | |||||
System.err.println("error: setRawName should not been invoked"); | |||||
super.setRawName(rawName); | |||||
} | |||||
@Override | |||||
public String getQualifiedName(boolean overrideFileWithPackage) { | |||||
System.err.println("error: getQualifiedName should not been invoked"); | |||||
return super.getQualifiedName(overrideFileWithPackage); | |||||
} | |||||
@Override | |||||
public Entity getAncestorOfType(@SuppressWarnings("rawtypes") Class classType) { | |||||
return null; | |||||
} | |||||
@Override | |||||
public void inferEntities(IBindingResolver bindingResolver) { | |||||
System.err.println("error: inferEntities should not been invoked"); | |||||
super.inferEntities(bindingResolver); | |||||
} | |||||
@Override | |||||
public String getDisplayName() { | |||||
System.err.println("error: getDisplayName should not been invoked"); | |||||
return super.getDisplayName(); | |||||
} | |||||
@Override | |||||
public Entity getByName(String name, HashSet<Entity> searched) { | |||||
Entity entity = super.getByName(name, searched); | |||||
if (entity!=null) return entity; | |||||
for (TypeEntity type:getCandidateTypes()) { | |||||
if (searched.contains(type)) continue; | |||||
Entity e = type.getByName(name, searched); | |||||
if (e !=null) return e; | |||||
} | |||||
return null; | |||||
} | |||||
} |
@@ -0,0 +1,86 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor.cpp.cdt; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.extractor.cpp.CppFileParser; | |||||
import depends.extractor.cpp.MacroRepo; | |||||
import depends.relations.IBindingResolver; | |||||
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; | |||||
import java.io.IOException; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
public class CdtCppFileParser extends CppFileParser { | |||||
private PreprocessorHandler preprocessorHandler ; | |||||
private IBindingResolver bindingResolver; | |||||
private MacroRepo macroRepo; | |||||
public CdtCppFileParser(EntityRepo entityRepo, PreprocessorHandler preprocessorHandler, IBindingResolver bindingResolver, MacroRepo macroRepo) { | |||||
super(entityRepo); | |||||
this.preprocessorHandler = preprocessorHandler; | |||||
this.bindingResolver = bindingResolver; | |||||
this.macroRepo= macroRepo; | |||||
} | |||||
@Override | |||||
protected void parseFile(String fileFullPath) throws IOException { | |||||
Map<String, String> macroMap = new HashMap<>(macroRepo.getDefaultMap()); | |||||
parse(fileFullPath,macroMap); | |||||
} | |||||
/** | |||||
* | |||||
* @param isInScope whether the parse is invoked by project file or an 'indirect' included file | |||||
* @return | |||||
*/ | |||||
public void parse(String fileFullPath,Map<String, String> macroMap) throws IOException { | |||||
CppVisitor bridge = new CppVisitor(fileFullPath, entityRepo, preprocessorHandler, bindingResolver); | |||||
IASTTranslationUnit tu = (new CDTParser(preprocessorHandler.getIncludePaths())).parse(fileFullPath,macroMap); | |||||
boolean containsIncludes = false; | |||||
for (String incl:preprocessorHandler.getDirectIncludedFiles(tu.getAllPreprocessorStatements(),fileFullPath)) { | |||||
CdtCppFileParser importedParser = new CdtCppFileParser(entityRepo, preprocessorHandler, bindingResolver,macroRepo); | |||||
importedParser.parse(incl); | |||||
Map<String, String> macros = macroRepo.get(incl); | |||||
if (macros!=null) | |||||
macroMap.putAll(macros); | |||||
containsIncludes = true; | |||||
} | |||||
if (containsIncludes) { | |||||
tu = (new CDTParser(preprocessorHandler.getIncludePaths())).parse(fileFullPath,macroMap); | |||||
} | |||||
macroRepo.putMacros(fileFullPath,macroMap,tu.getMacroDefinitions()); | |||||
tu.accept(bridge); | |||||
return; | |||||
} | |||||
@Override | |||||
protected boolean isPhase2Files(String fileFullPath) { | |||||
if (fileFullPath.endsWith(".h") || fileFullPath.endsWith(".hh") || fileFullPath.endsWith(".hpp") | |||||
|| fileFullPath.endsWith(".hxx")) | |||||
return true; | |||||
return false; | |||||
} | |||||
} |
@@ -0,0 +1,467 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.entity; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.relations.IBindingResolver; | |||||
import depends.relations.Relation; | |||||
import multilang.depends.util.file.TemporaryFile; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import java.io.*; | |||||
import java.lang.ref.WeakReference; | |||||
import java.util.*; | |||||
/** | |||||
* ContainerEntity for example file, class, method, etc. they could contain | |||||
* vars, functions, ecpressions, type parameters, etc. | |||||
*/ | |||||
public abstract class ContainerEntity extends DecoratedEntity { | |||||
private static final Logger logger = LoggerFactory.getLogger(ContainerEntity.class); | |||||
private ArrayList<VarEntity> vars; | |||||
private ArrayList<FunctionEntity> functions; | |||||
WeakReference<HashMap<Object, Expression>> expressionWeakReference; | |||||
private ArrayList<Expression> expressionList; | |||||
private int expressionCount = 0; | |||||
private Collection<GenericName> mixins; | |||||
private Collection<ContainerEntity> resolvedMixins; | |||||
private ArrayList<VarEntity> vars() { | |||||
if (vars==null) | |||||
vars = new ArrayList<>(); | |||||
return this.vars; | |||||
} | |||||
private Collection<GenericName> mixins() { | |||||
if (mixins==null) | |||||
mixins = new ArrayList<>(); | |||||
return this.mixins; | |||||
} | |||||
private ArrayList<FunctionEntity> functions() { | |||||
if (functions==null) | |||||
functions = new ArrayList<>(); | |||||
return this.functions; | |||||
} | |||||
public ContainerEntity() { | |||||
} | |||||
public ContainerEntity(GenericName rawName, Entity parent, Integer id) { | |||||
super(rawName, parent, id); | |||||
} | |||||
public void addVar(VarEntity var) { | |||||
if (logger.isDebugEnabled()) { | |||||
logger.debug("var found: " + var.getRawName() + ":" + var.getRawType()); | |||||
} | |||||
this.vars().add(var); | |||||
} | |||||
public ArrayList<VarEntity> getVars() { | |||||
if (vars==null) | |||||
return new ArrayList<>(); | |||||
return this.vars(); | |||||
} | |||||
public void addFunction(FunctionEntity functionEntity) { | |||||
this.functions().add(functionEntity); | |||||
} | |||||
public ArrayList<FunctionEntity> getFunctions() { | |||||
if (functions==null) | |||||
return new ArrayList<>(); | |||||
return this.functions; | |||||
} | |||||
public HashMap<Object, Expression> expressions() { | |||||
if (expressionWeakReference==null) | |||||
expressionWeakReference= new WeakReference<HashMap<Object, Expression>>(new HashMap<>()); | |||||
HashMap<Object, Expression> r = expressionWeakReference.get(); | |||||
if (r==null) return new HashMap<>(); | |||||
return r; | |||||
} | |||||
public void addExpression(Object key, Expression expression) { | |||||
expressions().put(key, expression); | |||||
expressionList().add(expression); | |||||
expressionCount = expressionList.size(); | |||||
} | |||||
public boolean containsExpression(Object key) { | |||||
return expressions().containsKey(key); | |||||
} | |||||
/** | |||||
* For all data in the class, infer their types. Should be override in | |||||
* sub-classes | |||||
*/ | |||||
public void inferLocalLevelEntities(IBindingResolver bindingResolver) { | |||||
super.inferLocalLevelEntities(bindingResolver); | |||||
for (VarEntity var : this.vars()) { | |||||
if (var.getParent()!=this) { | |||||
var.inferLocalLevelEntities(bindingResolver); | |||||
} | |||||
} | |||||
for (FunctionEntity func : this.getFunctions()) { | |||||
if (func.getParent()!=this) { | |||||
func.inferLocalLevelEntities(bindingResolver); | |||||
} | |||||
} | |||||
if (bindingResolver.isEagerExpressionResolve()) { | |||||
reloadExpression(bindingResolver.getRepo()); | |||||
resolveExpressions(bindingResolver); | |||||
cacheExpressions(); | |||||
} | |||||
resolvedMixins = identiferToContainerEntity(bindingResolver, getMixins()); | |||||
} | |||||
private Collection<GenericName> getMixins() { | |||||
if (mixins==null) | |||||
return new ArrayList<>(); | |||||
return mixins; | |||||
} | |||||
private Collection<ContainerEntity> identiferToContainerEntity(IBindingResolver bindingResolver, Collection<GenericName> identifiers) { | |||||
if (identifiers.size()==0) return null; | |||||
ArrayList<ContainerEntity> r = new ArrayList<>(); | |||||
for (GenericName identifier : identifiers) { | |||||
Entity entity = bindingResolver.resolveName(this, identifier, true); | |||||
if (entity == null) { | |||||
continue; | |||||
} | |||||
if (entity instanceof ContainerEntity) | |||||
r.add((ContainerEntity) entity); | |||||
} | |||||
return r; | |||||
} | |||||
/** | |||||
* Resolve all expression's type | |||||
* | |||||
* @param bindingResolver | |||||
*/ | |||||
public void resolveExpressions(IBindingResolver bindingResolver) { | |||||
if (this instanceof FunctionEntity) { | |||||
((FunctionEntity)this).linkReturnToLastExpression(); | |||||
} | |||||
if (expressionList==null) return; | |||||
if(expressionList.size()>10000) return; | |||||
for (Expression expression : expressionList) { | |||||
// 1. if expression's type existed, break; | |||||
if (expression.getType() != null) | |||||
continue; | |||||
if (expression.isDot()) { // wait for previous | |||||
continue; | |||||
} | |||||
if (expression.getRawType() == null && expression.getIdentifier() == null) | |||||
continue; | |||||
// 2. if expression's rawType existed, directly infer type by rawType | |||||
// if expression's rawType does not existed, infer type based on identifiers | |||||
if (expression.getRawType() != null) { | |||||
expression.setType(bindingResolver.inferTypeFromName(this, expression.getRawType()), null, bindingResolver); | |||||
if (expression.getType() != null) { | |||||
continue; | |||||
} | |||||
} | |||||
if (expression.getIdentifier() != null) { | |||||
Entity entity = bindingResolver.resolveName(this, expression.getIdentifier(), true); | |||||
String composedName = expression.getIdentifier().toString(); | |||||
Expression theExpr = expression; | |||||
if (entity==null) { | |||||
while(theExpr.getParent()!=null && theExpr.getParent().isDot()) { | |||||
theExpr = theExpr.getParent(); | |||||
if (theExpr.getIdentifier()==null) break; | |||||
composedName = composedName + "." + theExpr.getIdentifier().toString(); | |||||
entity = bindingResolver.resolveName(this, GenericName.build(composedName), true); | |||||
if (entity!=null) | |||||
break; | |||||
} | |||||
} | |||||
if (entity != null) { | |||||
expression.setType(entity.getType(), entity, bindingResolver); | |||||
continue; | |||||
} | |||||
if (expression.isCall()) { | |||||
List<Entity> funcs = this.lookupFunctionInVisibleScope(expression.getIdentifier()); | |||||
if (funcs != null) { | |||||
for (Entity func:funcs) { | |||||
expression.setType(func.getType(), func, bindingResolver); | |||||
} | |||||
} | |||||
} else { | |||||
Entity varEntity = this.lookupVarInVisibleScope(expression.getIdentifier()); | |||||
if (varEntity != null) { | |||||
expression.setType(varEntity.getType(), varEntity, bindingResolver); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
public void cacheChildExpressions() { | |||||
cacheExpressions(); | |||||
for (Entity child:getChildren()) { | |||||
if (child instanceof ContainerEntity) { | |||||
((ContainerEntity)child).cacheChildExpressions(); | |||||
} | |||||
} | |||||
} | |||||
public void cacheExpressions() { | |||||
if (expressionWeakReference==null) return; | |||||
if (expressionList==null) return; | |||||
this.expressions().clear(); | |||||
this.expressionWeakReference.clear(); | |||||
cacheExpressionListToFile(); | |||||
this.expressionList.clear(); | |||||
this.expressionList=null; | |||||
this.expressionList = new ArrayList<>(); | |||||
} | |||||
public void clearExpressions() { | |||||
if (expressionWeakReference==null) return; | |||||
if (expressionList==null) return; | |||||
this.expressions().clear(); | |||||
this.expressionWeakReference.clear(); | |||||
this.expressionList.clear(); | |||||
this.expressionList=null; | |||||
this.expressionList = new ArrayList<>(); | |||||
this.expressionUseList = null; | |||||
} | |||||
private void cacheExpressionListToFile() { | |||||
if (expressionCount ==0) return; | |||||
try { | |||||
FileOutputStream fileOut = new FileOutputStream(TemporaryFile.getInstance().exprPath(this.id)); | |||||
ObjectOutputStream out = new ObjectOutputStream(fileOut); | |||||
out.writeObject(this.expressionList); | |||||
out.close(); | |||||
fileOut.close(); | |||||
} catch (IOException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
} | |||||
@SuppressWarnings("unchecked") | |||||
public void reloadExpression(EntityRepo repo) { | |||||
if (expressionCount ==0) return; | |||||
try | |||||
{ | |||||
FileInputStream fileIn = new FileInputStream(TemporaryFile.getInstance().exprPath(this.id)); | |||||
ObjectInputStream in = new ObjectInputStream(fileIn); | |||||
expressionList = (ArrayList<Expression>) in.readObject(); | |||||
if (expressionList==null) expressionList = new ArrayList<>(); | |||||
for (Expression expr:expressionList) { | |||||
expr.reload(repo,expressionList); | |||||
} | |||||
in.close(); | |||||
fileIn.close(); | |||||
}catch(IOException | ClassNotFoundException i) | |||||
{ | |||||
return; | |||||
} | |||||
} | |||||
public List<Expression> expressionList() { | |||||
if (expressionList==null) | |||||
expressionList = new ArrayList<>(); | |||||
return expressionList; | |||||
} | |||||
public boolean containsExpression() { | |||||
return expressions().size() > 0; | |||||
} | |||||
/** | |||||
* The entry point of lookup functions. It will treat multi-declare entities and | |||||
* normal entity differently. - for multiDeclare entity, it means to lookup all | |||||
* entities - for normal entity, it means to lookup entities from current scope | |||||
* still root | |||||
* | |||||
* @param functionName | |||||
* @return | |||||
*/ | |||||
public List<Entity> lookupFunctionInVisibleScope(GenericName functionName) { | |||||
List<Entity> functions = new ArrayList<>(); | |||||
if (this.getMutliDeclare() != null) { | |||||
for (Entity fromEntity : this.getMutliDeclare().getEntities()) { | |||||
Entity f = lookupFunctionBottomUpTillTopContainer(functionName, fromEntity); | |||||
if (f != null) { | |||||
functions.add(f); | |||||
return functions; | |||||
} | |||||
} | |||||
} else { | |||||
ContainerEntity fromEntity = this; | |||||
Entity f = lookupFunctionBottomUpTillTopContainer(functionName, fromEntity); | |||||
if (f != null) { | |||||
functions.add(f); | |||||
return functions; | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
/** | |||||
* lookup function bottom up till the most outside container | |||||
* | |||||
* @param functionName | |||||
* @param fromEntity | |||||
* @return | |||||
*/ | |||||
private Entity lookupFunctionBottomUpTillTopContainer(GenericName functionName, Entity fromEntity) { | |||||
while (fromEntity != null) { | |||||
if (fromEntity instanceof ContainerEntity) { | |||||
FunctionEntity func = ((ContainerEntity) fromEntity).lookupFunctionLocally(functionName); | |||||
if (func != null) | |||||
return func; | |||||
} | |||||
for (Entity child:this.getChildren()) { | |||||
if (child instanceof AliasEntity) { | |||||
if (child.getRawName().equals(functionName)) | |||||
return child; | |||||
} | |||||
} | |||||
fromEntity = (ContainerEntity) this.getAncestorOfType(ContainerEntity.class); | |||||
} | |||||
return null; | |||||
} | |||||
/** | |||||
* lookup function in local entity. It could be override such as the type entity | |||||
* (it should also lookup the inherit/implemented types | |||||
* | |||||
* @param functionName | |||||
* @return | |||||
*/ | |||||
public FunctionEntity lookupFunctionLocally(GenericName functionName) { | |||||
for (FunctionEntity func : getFunctions()) { | |||||
if (func.getRawName().equals(functionName)) | |||||
return func; | |||||
} | |||||
return null; | |||||
} | |||||
/** | |||||
* The entry point of lookup var. It will treat multi-declare entities and | |||||
* normal entity differently. - for multiDeclare entity, it means to lookup all | |||||
* entities - for normal entity, it means to lookup entities from current scope | |||||
* still root | |||||
* | |||||
* @param varName | |||||
* @return | |||||
*/ | |||||
public Entity lookupVarInVisibleScope(GenericName varName) { | |||||
ContainerEntity fromEntity = this; | |||||
return lookupVarBottomUpTillTopContainer(varName, fromEntity); | |||||
} | |||||
/** | |||||
* To found the var. | |||||
* | |||||
* @param fromEntity | |||||
* @param varName | |||||
* @return | |||||
*/ | |||||
private Entity lookupVarBottomUpTillTopContainer(GenericName varName, ContainerEntity fromEntity) { | |||||
while (fromEntity != null) { | |||||
if (fromEntity instanceof ContainerEntity) { | |||||
VarEntity var = ((ContainerEntity) fromEntity).lookupVarLocally(varName); | |||||
if (var != null) | |||||
return var; | |||||
} | |||||
for (Entity child:this.getChildren()) { | |||||
if (child instanceof AliasEntity) { | |||||
if (child.getRawName().equals(varName)) | |||||
return child; | |||||
} | |||||
} | |||||
fromEntity = (ContainerEntity) this.getAncestorOfType(ContainerEntity.class); | |||||
} | |||||
return null; | |||||
} | |||||
public VarEntity lookupVarLocally(GenericName varName) { | |||||
for (VarEntity var : getVars()) { | |||||
if (var.getRawName().equals(varName)) | |||||
return var; | |||||
} | |||||
return null; | |||||
} | |||||
public VarEntity lookupVarLocally(String varName) { | |||||
return this.lookupVarLocally(GenericName.build(varName)); | |||||
} | |||||
public void addMixin(GenericName moduleName) { | |||||
mixins().add(moduleName); | |||||
} | |||||
public Collection<ContainerEntity> getResolvedMixins() { | |||||
if (resolvedMixins==null) return new ArrayList<>(); | |||||
return resolvedMixins; | |||||
} | |||||
HashMap<String,Set<Expression>> expressionUseList = null; | |||||
public void addRelation(Expression expression, Relation relation) { | |||||
String key = relation.getEntity().qualifiedName+relation.getType(); | |||||
if (this.expressionUseList==null) | |||||
expressionUseList = new HashMap<>(); | |||||
if (expressionUseList.containsKey(key)){ | |||||
Set<Expression> expressions = expressionUseList.get(key); | |||||
for (Expression expr:expressions){ | |||||
if (linkedExpr(expr,expression)) return; | |||||
} | |||||
}else{ | |||||
expressionUseList.put(key,new HashSet<>()); | |||||
} | |||||
expressionUseList.get(key).add(expression); | |||||
super.addRelation(relation); | |||||
} | |||||
private boolean linkedExpr(Expression a, Expression b) { | |||||
Expression parent = a.getParent(); | |||||
while(parent!=null){ | |||||
if (parent==b) return true; | |||||
parent = parent.getParent(); | |||||
} | |||||
parent = b.getParent(); | |||||
while(parent!=null){ | |||||
if (parent==a) return true; | |||||
parent = parent.getParent(); | |||||
} | |||||
return false; | |||||
} | |||||
} |
@@ -0,0 +1,66 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor.cpp; | |||||
import depends.entity.repo.BuiltInType; | |||||
public class CppBuiltInType extends BuiltInType { | |||||
@Override | |||||
protected String[] getBuiltInTypeName() { | |||||
return new String[] { "alignas", "alignof", "asm", "auto", "bool", "break", "case", "catch", "char", | |||||
"char16_t", "char32_t", "class", "const", "constexpr", "const_cast", "continue", "decltype", | |||||
"default", "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern", | |||||
"false", "final", "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable", | |||||
"namespace", "new", "noexcept", "nullptr", "operator", "override", "private", "protected", "public", | |||||
"register", "reinterpret_cast", "return", "short", "signed", "sizeof", "static", "static_assert", | |||||
"static_cast", "struct", "switch", "template", "this", "thread_local", "throw", "true", "try", | |||||
"typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", "void", "volatile", | |||||
"wchar_t", "while", "<Built-in>", | |||||
"__cplusplus","_cpp_aggregate_bases","__cpp_aggregate_nsdmi","__cpp_alias_templates","__cpp_aligned_new", | |||||
"__cpp_attributes","__cpp_binary_literals","__cpp_capture_star_this","__cpp_constexpr","__cpp_decltype", | |||||
"__cpp_decltype_auto","__cpp_deduction_guides","__cpp_delegating_constructors", | |||||
"__cpp_enumerator_attributes","__cpp_explicit_bool","__cpp_fold_expressions","__cpp_generic_lambdas", | |||||
"__cpp_guaranteed_copy_elision","__cpp_hex_float","__cpp_if_constexpr","__cpp_inheriting_constructors", | |||||
"__cpp_init_captures","__cpp_initializer_lists","__cpp_inline_variables","__cpp_lambdas", | |||||
"__cpp_namespace_attributes","__cpp_noexcept_function_type","__cpp_nontype_template_args", | |||||
"__cpp_nontype_template_parameter_auto","__cpp_nontype_template_parameter_class","__cpp_nsdmi" | |||||
+ "","__cpp_range_based_for","__cpp_raw_strings","__cpp_ref_qualifiers","__cpp_return_type_deduction" | |||||
,"__cpp_rvalue_references","__cpp_sized_deallocation","__cpp_static_assert","__cpp_structured_bindings", | |||||
"__cpp_template_template_args","__cpp_threadsafe_static_init","__cpp_unicode_characters","__cpp_unicode_literals", | |||||
"__cpp_user_defined_literals","__cpp_variable_templates","__cpp_variadic_templates","__cpp_variadic_using", | |||||
"__DATE__","__FILE__","__LINE__","__STDC__","__STDC_ANALYZABLE__","__STDC_HOSTED__","__STDC_IEC_559__", | |||||
"__STDC_IEC_559_COMPLEX__","__STDC_ISO_10646__","__STDC_LIB_EXT1__","__STDC_MB_MIGHT_NEQ_WC__", | |||||
"__STDC_NO_ATOMICS__","__STDC_NO_COMPLEX__","__STDC_NO_THREADS__","__STDC_NO_VLA__", | |||||
"__STDCPP_DEFAULT_NEW_ALIGNMENT__","__STDCPP_STRICT_POINTER_SAFETY__","__STDCPP_THREADS__", | |||||
"__STDC_UTF_16__","__STDC_UTF_32__","__STDC_VERSION__","__TIME__" | |||||
}; | |||||
} | |||||
@Override | |||||
protected String[] getBuiltInTypePrefix() { | |||||
return new String[] {"__"}; | |||||
} | |||||
} |
@@ -0,0 +1,133 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor.cpp; | |||||
import depends.entity.Entity; | |||||
import depends.entity.FileEntity; | |||||
import depends.entity.GenericName; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.extractor.UnsolvedBindings; | |||||
import depends.importtypes.FileImport; | |||||
import depends.importtypes.Import; | |||||
import depends.relations.ImportLookupStrategy; | |||||
import java.util.*; | |||||
public class CppImportLookupStrategy extends ImportLookupStrategy { | |||||
public CppImportLookupStrategy(EntityRepo repo){ | |||||
super(repo); | |||||
} | |||||
@Override | |||||
public Entity lookupImportedType(String name, FileEntity fileEntity) { | |||||
String importedString = fileEntity.importedSuffixMatch(name); | |||||
if (importedString!=null) { | |||||
Entity r = repo.getEntity(importedString); | |||||
if (r!=null) return r; | |||||
} | |||||
HashSet<Integer> fileSet = getIncludedFiles(fileEntity); | |||||
for (Integer file:fileSet) { | |||||
Entity importedItem = repo.getEntity(file); | |||||
if (importedItem instanceof FileEntity) { | |||||
FileEntity importedFile = (FileEntity) repo.getEntity(file); | |||||
if (importedFile==null) continue; | |||||
Entity entity = bindingResolver.resolveName(importedFile,GenericName.build(name), false); | |||||
if (entity!=null) return entity; | |||||
Collection<Entity> namespaces = fileEntity.getImportedTypes(); | |||||
for (Entity ns:namespaces) { | |||||
String nameWithPrefix = ns.getQualifiedName() + "." + name; | |||||
entity = bindingResolver.resolveName(importedFile,GenericName.build(nameWithPrefix), false); | |||||
if (entity!=null) return entity; | |||||
} | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
private Map<Integer, HashSet<Integer> > includedFiles = new HashMap<>(); | |||||
private HashSet<Integer> getIncludedFiles(FileEntity fileEntity) { | |||||
if (includedFiles.containsKey(fileEntity.getId())) { | |||||
return includedFiles.get(fileEntity.getId()); | |||||
} | |||||
HashSet<Integer> fileSet = new HashSet<>(); | |||||
foundIncludedFiles(fileSet, fileEntity.getImportedFiles()); | |||||
includedFiles.put(fileEntity.getId(), fileSet); | |||||
return fileSet; | |||||
} | |||||
private void foundIncludedFiles(HashSet<Integer> fileSet, Collection<Entity> importedFiles) { | |||||
for (Entity file:importedFiles) { | |||||
if (file==null ) continue; | |||||
if (!(file instanceof FileEntity)) continue; | |||||
if (fileSet.contains(file.getId())) continue; | |||||
fileSet.add(file.getId()); | |||||
foundIncludedFiles(fileSet,((FileEntity)file).getImportedFiles()); | |||||
} | |||||
} | |||||
@Override | |||||
public List<Entity> getImportedRelationEntities(List<Import> importedList) { | |||||
ArrayList<Entity> result = new ArrayList<>(); | |||||
for (Import importedItem:importedList) { | |||||
if (importedItem instanceof FileImport) { | |||||
Entity imported = repo.getEntity(importedItem.getContent()); | |||||
if (imported==null) continue; | |||||
result.add(imported); | |||||
} | |||||
} | |||||
return result; | |||||
} | |||||
@Override | |||||
public List<Entity> getImportedTypes(List<Import> importedList, Set<UnsolvedBindings> unsolvedBindings) { | |||||
ArrayList<Entity> result = new ArrayList<>(); | |||||
for (Import importedItem:importedList) { | |||||
if (!(importedItem instanceof FileImport)) { | |||||
Entity imported = repo.getEntity(importedItem.getContent()); | |||||
if (imported==null) { | |||||
unsolvedBindings.add(new UnsolvedBindings(importedItem.getContent(),null)); | |||||
continue; | |||||
} | |||||
result.add(imported); | |||||
} | |||||
} | |||||
return result; | |||||
} | |||||
@Override | |||||
public List<Entity> getImportedFiles(List<Import> importedList) { | |||||
return getImportedRelationEntities(importedList); | |||||
} | |||||
@Override | |||||
public boolean supportGlobalNameLookup() { | |||||
return false; | |||||
} | |||||
} |
@@ -0,0 +1,338 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor.cpp.cdt; | |||||
import depends.entity.*; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.entity.repo.IdGenerator; | |||||
import depends.extractor.cpp.CppHandlerContext; | |||||
import depends.importtypes.ExactMatchImport; | |||||
import depends.importtypes.FileImport; | |||||
import depends.importtypes.PackageWildCardImport; | |||||
import depends.relations.IBindingResolver; | |||||
import org.codehaus.plexus.util.StringUtils; | |||||
import org.eclipse.cdt.core.dom.ast.*; | |||||
import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier.IASTEnumerator; | |||||
import org.eclipse.cdt.core.dom.ast.cpp.*; | |||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier; | |||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.*; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import java.util.ArrayList; | |||||
import java.util.HashSet; | |||||
import java.util.List; | |||||
public class CppVisitor extends ASTVisitor { | |||||
private static final Logger logger = LoggerFactory.getLogger(CppVisitor.class); | |||||
private CppHandlerContext context; | |||||
private IdGenerator idGenerator; | |||||
private PreprocessorHandler preprocessorHandler; | |||||
IBindingResolver bindingResolver; | |||||
private ExpressionUsage expressionUsage; | |||||
HashSet<String> file; | |||||
public CppVisitor(String fileFullPath, EntityRepo entityRepo, PreprocessorHandler preprocessorHandler, IBindingResolver bindingResolver) { | |||||
super(true); | |||||
this.shouldVisitAmbiguousNodes = true; | |||||
this.shouldVisitImplicitNames = true; | |||||
this.includeInactiveNodes = true; | |||||
this.context = new CppHandlerContext(entityRepo, bindingResolver); | |||||
idGenerator = entityRepo; | |||||
this.bindingResolver = bindingResolver; | |||||
this.preprocessorHandler = preprocessorHandler; | |||||
expressionUsage = new ExpressionUsage(context,entityRepo); | |||||
file = new HashSet<>(); | |||||
context.startFile(fileFullPath); | |||||
file.add(this.context.currentFile().getQualifiedName()); | |||||
} | |||||
@Override | |||||
public int visit(IASTTranslationUnit tu) { | |||||
for (String incl:preprocessorHandler.getDirectIncludedFiles(tu.getAllPreprocessorStatements(),context.currentFile().getQualifiedName())) { | |||||
context.foundNewImport(new FileImport(incl)); | |||||
} | |||||
MacroExtractor macroExtractor = new MacroExtractor(tu.getAllPreprocessorStatements(),context.currentFile().getQualifiedName()); | |||||
macroExtractor.extract(context); | |||||
for (IASTNode child:tu.getChildren()) { | |||||
if (notLocalFile(child)) continue; | |||||
child.accept(this); | |||||
} | |||||
return ASTVisitor.PROCESS_SKIP; | |||||
} | |||||
@Override | |||||
public int visit(IASTProblem problem) { | |||||
if (notLocalFile(problem)) return ASTVisitor.PROCESS_SKIP; | |||||
System.out.println("warning: parse error " + problem.getOriginalNode().getRawSignature() + problem.getMessageWithLocation()); | |||||
return super.visit(problem); | |||||
} | |||||
private boolean notLocalFile(IASTNode node) { | |||||
if (file.contains(node.getFileLocation().getFileName())) { | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
// PACKAGES | |||||
@Override | |||||
public int visit(ICPPASTNamespaceDefinition namespaceDefinition) { | |||||
if (notLocalFile(namespaceDefinition)) return ASTVisitor.PROCESS_SKIP; | |||||
String ns = namespaceDefinition.getName().toString().replace("::", "."); | |||||
logger.trace("enter ICPPASTNamespaceDefinition " + ns); | |||||
Entity pkg = context.foundNamespace(ns,namespaceDefinition.getFileLocation().getStartingLineNumber()); | |||||
context.foundNewImport(new PackageWildCardImport(ns)); | |||||
return super.visit(namespaceDefinition); | |||||
} | |||||
@Override | |||||
public int leave(ICPPASTNamespaceDefinition namespaceDefinition) { | |||||
if (notLocalFile(namespaceDefinition)) return ASTVisitor.PROCESS_SKIP; | |||||
context.exitLastedEntity(); | |||||
return super.leave(namespaceDefinition); | |||||
} | |||||
// Types | |||||
@Override | |||||
public int visit(IASTDeclSpecifier declSpec) { | |||||
if (notLocalFile(declSpec)) return ASTVisitor.PROCESS_SKIP; | |||||
logger.trace("enter IASTDeclSpecifier " + declSpec.getClass().getSimpleName()); | |||||
if (declSpec instanceof IASTCompositeTypeSpecifier) { | |||||
IASTCompositeTypeSpecifier type = (IASTCompositeTypeSpecifier)declSpec; | |||||
String name = ASTStringUtilExt.getName(type); | |||||
List<GenericName> param = ASTStringUtilExt.getTemplateParameters(type); | |||||
TypeEntity typeEntity = context.foundNewType(name, type.getFileLocation().getStartingLineNumber()); | |||||
if (declSpec instanceof ICPPASTCompositeTypeSpecifier) { | |||||
ICPPASTBaseSpecifier[] baseSpecififers = ((ICPPASTCompositeTypeSpecifier)declSpec).getBaseSpecifiers(); | |||||
for (ICPPASTBaseSpecifier baseSpecififer:baseSpecififers) { | |||||
String extendName = ASTStringUtilExt.getName(baseSpecififer.getNameSpecifier()); | |||||
context.foundExtends(extendName); | |||||
} | |||||
} | |||||
} | |||||
else if (declSpec instanceof IASTEnumerationSpecifier) { | |||||
context.foundNewType(ASTStringUtilExt.getName(declSpec), declSpec.getFileLocation().getStartingLineNumber()); | |||||
}else { | |||||
//we do not care other types | |||||
} | |||||
return super.visit(declSpec); | |||||
} | |||||
@Override | |||||
public int leave(IASTDeclSpecifier declSpec) { | |||||
if (notLocalFile(declSpec)) return ASTVisitor.PROCESS_SKIP; | |||||
if (declSpec instanceof IASTCompositeTypeSpecifier) { | |||||
context.exitLastedEntity(); | |||||
} | |||||
else if (declSpec instanceof IASTEnumerationSpecifier) { | |||||
context.exitLastedEntity(); | |||||
}else { | |||||
//we do not care other types | |||||
} | |||||
return super.leave(declSpec); | |||||
} | |||||
//Function or Methods | |||||
@Override | |||||
public int visit(IASTDeclarator declarator) { | |||||
if (notLocalFile(declarator)) return ASTVisitor.PROCESS_SKIP; | |||||
logger.trace("enter IASTDeclarator " + declarator.getClass().getSimpleName()); | |||||
if (declarator instanceof IASTFunctionDeclarator){ | |||||
GenericName returnType = null; | |||||
if ( declarator.getParent() instanceof IASTSimpleDeclaration) { | |||||
IASTSimpleDeclaration decl = (IASTSimpleDeclaration)(declarator.getParent()); | |||||
returnType = buildGenericNameFromDeclSpecifier(decl.getDeclSpecifier()); | |||||
String rawName = ASTStringUtilExt.getName(declarator); | |||||
List<Entity> namedEntity = context.currentFile().lookupFunctionInVisibleScope(GenericName.build(rawName)); | |||||
if (namedEntity!=null) { | |||||
rawName = namedEntity.get(0).getQualifiedName(); | |||||
} | |||||
returnType = reMapIfConstructDeconstruct(rawName,returnType); | |||||
context.foundMethodDeclaratorProto(rawName, returnType,decl.getFileLocation().getStartingLineNumber()); | |||||
} | |||||
else if ( declarator.getParent() instanceof IASTFunctionDefinition) { | |||||
IASTFunctionDefinition decl = (IASTFunctionDefinition)declarator.getParent(); | |||||
returnType = buildGenericNameFromDeclSpecifier(decl.getDeclSpecifier()); | |||||
String rawName = ASTStringUtilExt.getName(declarator); | |||||
List<Entity> namedEntity = context.currentFile().lookupFunctionInVisibleScope(GenericName.build(rawName)); | |||||
if (namedEntity!=null) { | |||||
rawName = namedEntity.get(0).getQualifiedName(); | |||||
} | |||||
returnType = reMapIfConstructDeconstruct(rawName,returnType); | |||||
context.foundMethodDeclaratorImplementation(rawName, returnType,decl.getFileLocation().getStartingLineNumber()); | |||||
} | |||||
} | |||||
return super.visit(declarator); | |||||
} | |||||
private GenericName buildGenericNameFromDeclSpecifier(IASTDeclSpecifier decl) { | |||||
String name = ASTStringUtilExt.getName(decl); | |||||
List<GenericName> templateParams = ASTStringUtilExt.getTemplateParameters(decl); | |||||
if (name==null) | |||||
return null; | |||||
return new GenericName(name,templateParams); | |||||
} | |||||
/** | |||||
* In case of return type is empty, it maybe a construct/deconstruct function | |||||
* @param functionname | |||||
* @param returnType | |||||
* @return | |||||
*/ | |||||
private GenericName reMapIfConstructDeconstruct(String functionname, GenericName returnType) { | |||||
if (returnType!=null && returnType.uniqName().length()>0) | |||||
return returnType; | |||||
if (functionname.contains("::")) { | |||||
return new GenericName(functionname.substring(0, functionname.indexOf("::"))); | |||||
}else { | |||||
return new GenericName(functionname); | |||||
} | |||||
} | |||||
@Override | |||||
public int leave(IASTDeclarator declarator) { | |||||
if (notLocalFile(declarator)) return ASTVisitor.PROCESS_SKIP; | |||||
if (declarator instanceof IASTFunctionDeclarator){ | |||||
if ( declarator.getParent() instanceof IASTSimpleDeclaration) { | |||||
String rawName = ASTStringUtilExt.getName(declarator); | |||||
if (rawName.equals(context.lastContainer().getRawName().getName())) { | |||||
context.exitLastedEntity(); | |||||
}else { | |||||
System.err.println("unexpected symbol"); | |||||
} | |||||
} | |||||
} | |||||
return super.leave(declarator); | |||||
} | |||||
@Override | |||||
public int leave(IASTDeclaration declaration) { | |||||
if (notLocalFile(declaration)) return ASTVisitor.PROCESS_SKIP; | |||||
if ( declaration instanceof IASTFunctionDefinition) { | |||||
context.exitLastedEntity(); | |||||
} | |||||
return super.leave(declaration); | |||||
} | |||||
// Variables | |||||
@Override | |||||
public int visit(IASTDeclaration declaration) { | |||||
if (notLocalFile(declaration)) return ASTVisitor.PROCESS_SKIP; | |||||
logger.trace("enter IASTDeclaration " + declaration.getClass().getSimpleName()); | |||||
if (declaration instanceof ICPPASTUsingDeclaration) { | |||||
String ns = ASTStringUtilExt.getName((ICPPASTUsingDeclaration)declaration); | |||||
context.foundNewImport(new PackageWildCardImport(ns)); | |||||
} | |||||
else if (declaration instanceof ICPPASTUsingDirective) { | |||||
String ns = ((ICPPASTUsingDirective)declaration).getQualifiedName().toString().replace("::", "."); | |||||
context.foundNewImport(new ExactMatchImport(ns)); | |||||
} | |||||
else if (declaration instanceof IASTSimpleDeclaration ) { | |||||
for (IASTDeclarator declarator:((IASTSimpleDeclaration) declaration).getDeclarators()) { | |||||
IASTDeclSpecifier declSpecifier = ((IASTSimpleDeclaration) declaration).getDeclSpecifier(); | |||||
//Found new typedef definition | |||||
if (declSpecifier.getStorageClass()==IASTDeclSpecifier.sc_typedef) { | |||||
context.foundNewAlias(ASTStringUtilExt.getName(declarator),ASTStringUtilExt.getName(declSpecifier)); | |||||
}else if (!(declarator instanceof IASTFunctionDeclarator)) { | |||||
String varType = ASTStringUtilExt.getName(declSpecifier); | |||||
String varName = ASTStringUtilExt.getName(declarator); | |||||
if (!StringUtils.isEmpty(varType)) { | |||||
context.foundVarDefinition(varName, GenericName.build(varType), ASTStringUtilExt.getTemplateParameters(declSpecifier),declarator.getFileLocation().getStartingLineNumber()); | |||||
}else { | |||||
expressionUsage.foundCallExpressionOfFunctionStyle(varName,declarator); | |||||
} | |||||
} | |||||
} | |||||
}else if (declaration instanceof IASTFunctionDefinition){ | |||||
//handled in declarator | |||||
}else if (declaration instanceof CPPASTVisibilityLabel){ | |||||
//we ignore the visibility in dependency check | |||||
}else if (declaration instanceof CPPASTLinkageSpecification){ | |||||
}else if (declaration instanceof CPPASTTemplateDeclaration){ | |||||
}else if (declaration instanceof CPPASTProblemDeclaration){ | |||||
System.err.println("parsing error \n" + declaration.getRawSignature()); | |||||
}else if (declaration instanceof ICPPASTAliasDeclaration){ | |||||
IASTName name = ((ICPPASTAliasDeclaration)declaration).getAlias(); | |||||
String alias = ASTStringUtilExt.getSimpleName(name).replace("::", "."); | |||||
ICPPASTTypeId mapped = ((ICPPASTAliasDeclaration)declaration).getMappingTypeId(); | |||||
String originalName1 = ASTStringUtilExt.getTypeIdString(mapped); | |||||
context.foundNewAlias(alias, originalName1); | |||||
}else if (declaration instanceof CPPASTNamespaceAlias){ | |||||
IASTName name = ((CPPASTNamespaceAlias)declaration).getAlias(); | |||||
String alias = ASTStringUtilExt.getSimpleName(name).replace("::", "."); | |||||
IASTName mapped = ((CPPASTNamespaceAlias)declaration).getMappingName(); | |||||
String originalName = ASTStringUtilExt.getName(mapped); | |||||
context.foundNewAlias(alias, originalName); | |||||
} | |||||
else if(declaration instanceof CPPASTStaticAssertionDeclaration) | |||||
{ | |||||
}else if (declaration instanceof CPPASTTemplateSpecialization) { | |||||
} | |||||
else{ | |||||
System.out.println("not handled type: " + declaration.getClass().getName()); | |||||
System.out.println(declaration.getRawSignature()); | |||||
} | |||||
return super.visit(declaration); | |||||
} | |||||
@Override | |||||
public int visit(IASTEnumerator enumerator) { | |||||
if (notLocalFile(enumerator)) return ASTVisitor.PROCESS_SKIP; | |||||
logger.trace("enter IASTEnumerator " + enumerator.getClass().getSimpleName()); | |||||
VarEntity var = context.foundVarDefinition(enumerator.getName().toString(), context.currentType().getRawName(), new ArrayList<>(),enumerator.getFileLocation().getStartingLineNumber()); | |||||
return super.visit(enumerator); | |||||
} | |||||
@Override | |||||
public int visit(IASTExpression expression) { | |||||
if (notLocalFile(expression)) return ASTVisitor.PROCESS_SKIP; | |||||
Expression expr = expressionUsage.foundExpression(expression); | |||||
expr.setLine(expression.getFileLocation().getStartingLineNumber()); | |||||
return super.visit(expression); | |||||
} | |||||
@Override | |||||
public int visit(IASTParameterDeclaration parameterDeclaration) { | |||||
if (notLocalFile(parameterDeclaration)) return ASTVisitor.PROCESS_SKIP; | |||||
logger.trace("enter IASTParameterDeclaration " + parameterDeclaration.getClass().getSimpleName()); | |||||
String parameterName = ASTStringUtilExt.getName(parameterDeclaration.getDeclarator()); | |||||
String parameterType = ASTStringUtilExt.getName(parameterDeclaration.getDeclSpecifier()); | |||||
if (context.currentFunction()!=null) { | |||||
VarEntity var = new VarEntity(GenericName.build(parameterName),GenericName.build(parameterType),context.currentFunction(),idGenerator.generateId()); | |||||
context.currentFunction().addParameter(var ); | |||||
}else { | |||||
//System.out.println("** parameterDeclaration = " + parameter); | |||||
} | |||||
return super.visit(parameterDeclaration); | |||||
} | |||||
} |
@@ -0,0 +1,148 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.entity; | |||||
import depends.relations.IBindingResolver; | |||||
import java.util.ArrayList; | |||||
import java.util.Collection; | |||||
import java.util.List; | |||||
public abstract class DecoratedEntity extends Entity{ | |||||
private Collection<GenericName> annotations; | |||||
private Collection<Entity> resolvedAnnotations; | |||||
private Collection<Entity> resolvedTypeParameters; | |||||
public DecoratedEntity() { | |||||
} | |||||
public DecoratedEntity(GenericName rawName, Entity parent, Integer id) { | |||||
super(rawName, parent, id); | |||||
} | |||||
public void addAnnotation(GenericName name) { | |||||
if(this.annotations==null) | |||||
annotations = new ArrayList<>(); | |||||
this.annotations.add(name); | |||||
} | |||||
public void addTypeParameter(List<GenericName> parameters) { | |||||
this.getRawName().appendArguments(parameters); | |||||
} | |||||
public void addTypeParameter(GenericName parameter) { | |||||
this.getRawName().appendArguments(parameter); | |||||
} | |||||
protected void appendTypeParameters(Collection<Entity> typeParameterEntities) { | |||||
if (resolvedTypeParameters==null) | |||||
resolvedTypeParameters = new ArrayList<>(); | |||||
resolvedTypeParameters.addAll(typeParameterEntities); | |||||
} | |||||
/** | |||||
* For all data in the class, infer their types. | |||||
* Should be override in sub-classes | |||||
*/ | |||||
public void inferLocalLevelEntities(IBindingResolver bindingResolver) { | |||||
Collection<Entity> typeParameterEntities = typeParametersToEntities(bindingResolver); | |||||
appendTypeParameters(typeParameterEntities); | |||||
// if (this.getAncestorOfType(FileEntity.class).getRawName().contains("/examples/usersession/server.py") | |||||
// ) { | |||||
// System.out.print("dd"); | |||||
// } | |||||
resolvedAnnotations = identiferToEntities(bindingResolver, annotations); | |||||
} | |||||
private Collection<Entity> typeParametersToEntities(IBindingResolver bindingResolver) { | |||||
ArrayList<Entity> r = new ArrayList<>(); | |||||
for (GenericName typeParameter:this.getRawName().getArguments()) { | |||||
toEntityList(bindingResolver, r,typeParameter); | |||||
} | |||||
return r; | |||||
} | |||||
protected void toEntityList(IBindingResolver bindingResolver, ArrayList<Entity> r, GenericName typeParameter) { | |||||
Entity entity = resolveEntity(bindingResolver, typeParameter); | |||||
if (entity != null) | |||||
r.add(entity); | |||||
for (GenericName arg: typeParameter.getArguments()) { | |||||
toEntityList(bindingResolver,r,arg); | |||||
} | |||||
} | |||||
public Collection<Entity> getResolvedTypeParameters() { | |||||
if (resolvedTypeParameters==null) | |||||
return new ArrayList<>(); | |||||
return resolvedTypeParameters; | |||||
} | |||||
public Collection<Entity> getResolvedAnnotations() { | |||||
if (resolvedAnnotations==null) | |||||
return new ArrayList<>(); | |||||
return resolvedAnnotations; | |||||
} | |||||
public boolean isGenericTypeParameter(GenericName rawType) { | |||||
boolean foundInCurrentLevel = rawType.find(rawType); | |||||
if (foundInCurrentLevel) return true; | |||||
if (this.getParent()==null || !(this.getParent() instanceof ContainerEntity)) | |||||
return false; | |||||
return ((ContainerEntity)getParent()).isGenericTypeParameter(rawType); | |||||
} | |||||
/** | |||||
* A common utility function used to transfer the identifiers | |||||
* to types. | |||||
* @param bindingResolver - the inferer object | |||||
* @param identifiers - the identifiers will be translated | |||||
* @return The translated Types | |||||
*/ | |||||
protected Collection<Entity> identiferToEntities(IBindingResolver bindingResolver, Collection<GenericName> identifiers) { | |||||
if (identifiers==null) return null; | |||||
if (identifiers.size()==0) return null; | |||||
ArrayList<Entity> r = new ArrayList<>(); | |||||
for (GenericName name : identifiers) { | |||||
Entity entity = resolveEntity(bindingResolver, name); | |||||
if (entity != null) | |||||
r.add(entity); | |||||
} | |||||
return r; | |||||
} | |||||
private Entity resolveEntity(IBindingResolver bindingResolver, GenericName name) { | |||||
Entity entity = bindingResolver.resolveName(this, name, true); | |||||
if (entity==null) { | |||||
if (((ContainerEntity)getParent()).isGenericTypeParameter(name)) { | |||||
entity = TypeEntity.genericParameterType; | |||||
} | |||||
} | |||||
return entity; | |||||
} | |||||
} |
@@ -0,0 +1,160 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.generator; | |||||
import depends.entity.CandidateTypes; | |||||
import depends.entity.Entity; | |||||
import depends.entity.EntityNameBuilder; | |||||
import depends.entity.FileEntity; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.matrix.core.DependencyDetail; | |||||
import depends.matrix.core.DependencyMatrix; | |||||
import depends.matrix.core.LocationInfo; | |||||
import depends.matrix.transform.OrderedMatrixGenerator; | |||||
import depends.relations.Relation; | |||||
import multilang.depends.util.file.path.EmptyFilenameWritter; | |||||
import multilang.depends.util.file.path.FilenameWritter; | |||||
import multilang.depends.util.file.strip.EmptyLeadingNameStripper; | |||||
import multilang.depends.util.file.strip.ILeadingNameStrippper; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import java.util.ArrayList; | |||||
import java.util.Collections; | |||||
import java.util.Iterator; | |||||
import java.util.List; | |||||
import static depends.deptypes.DependencyType.DuckTypingLabel; | |||||
public abstract class DependencyGenerator { | |||||
private static Logger logger = LoggerFactory.getLogger(DependencyGenerator.class); | |||||
public DependencyMatrix identifyDependencies(EntityRepo entityRepo, List<String> typeFilter) { | |||||
System.out.println("dependencie data generating..."); | |||||
DependencyMatrix dependencyMatrix = build(entityRepo, typeFilter); | |||||
entityRepo.clear(); | |||||
System.out.println("reorder dependency matrix..."); | |||||
dependencyMatrix = new OrderedMatrixGenerator(dependencyMatrix).build(); | |||||
System.out.println("Dependencies data generating done successfully..."); | |||||
logger.info("Dependencies data generating done successfully..."); | |||||
return dependencyMatrix; | |||||
} | |||||
/** | |||||
* Build the dependency matrix (without re-mapping file id) | |||||
* @param entityRepo which contains entities and relations | |||||
* @return the generated dependency matrix | |||||
*/ | |||||
public DependencyMatrix build(EntityRepo entityRepo,List<String> typeFilter) { | |||||
DependencyMatrix dependencyMatrix = new DependencyMatrix(typeFilter); | |||||
Iterator<Entity> iterator = entityRepo.entityIterator(); | |||||
System.out.println("Start create dependencies matrix...."); | |||||
while(iterator.hasNext()) { | |||||
Entity entity = iterator.next(); | |||||
if (!entity.inScope()) continue; | |||||
if (outputLevelMatch(entity)){ | |||||
dependencyMatrix.addNode(nameOf(entity),entity.getId()); | |||||
} | |||||
int entityFrom = upToOutputLevelEntityId(entityRepo, entity); | |||||
if (entityFrom==-1) continue; | |||||
for (Relation relation:entity.getRelations()) { | |||||
Entity relatedEntity = relation.getEntity(); | |||||
if (relatedEntity==null) continue; | |||||
List<Entity> relatedEntities = expandEntity(relatedEntity); | |||||
String duckTypingFlag = relatedEntity instanceof CandidateTypes? DuckTypingLabel:""; | |||||
relatedEntities.forEach(theEntity->{ | |||||
if (theEntity.getId()>=0) { | |||||
int entityTo = upToOutputLevelEntityId(entityRepo,theEntity); | |||||
if (entityTo!=-1) { | |||||
DependencyDetail detail = buildDescription(entity, theEntity, relation.getFromLine()); | |||||
detail = rewriteDetail(detail); | |||||
dependencyMatrix.addDependency(relation.getType()+duckTypingFlag, entityFrom,entityTo,1,detail); | |||||
} | |||||
} | |||||
}); | |||||
} | |||||
} | |||||
System.out.println("Finish create dependencies matrix...."); | |||||
return dependencyMatrix; | |||||
} | |||||
private List<Entity> expandEntity(Entity relatedEntity) { | |||||
List<Entity> entities = new ArrayList<>(); | |||||
if (relatedEntity instanceof CandidateTypes) { | |||||
entities = Collections.unmodifiableList((List) ((CandidateTypes) relatedEntity).getCandidateTypes()); | |||||
}else { | |||||
entities.add(relatedEntity); | |||||
} | |||||
return entities; | |||||
} | |||||
private DependencyDetail rewriteDetail(DependencyDetail detail) { | |||||
if (detail==null) return null; | |||||
String srcFile = filenameWritter.reWrite( | |||||
stripper.stripFilename(detail.getSrc().getFile()) | |||||
); | |||||
String dstFile = filenameWritter.reWrite( | |||||
stripper.stripFilename(detail.getDest().getFile())); | |||||
return new DependencyDetail( | |||||
new LocationInfo(detail.getSrc().getObject(), | |||||
srcFile, detail.getSrc().getLineNumber()) | |||||
, | |||||
new LocationInfo(detail.getDest().getObject(), | |||||
dstFile, detail.getDest().getLineNumber())); | |||||
} | |||||
protected abstract int upToOutputLevelEntityId(EntityRepo entityRepo, Entity entity); | |||||
protected abstract String nameOf(Entity entity); | |||||
protected abstract boolean outputLevelMatch(Entity entity); | |||||
protected ILeadingNameStrippper stripper = new EmptyLeadingNameStripper(); | |||||
protected FilenameWritter filenameWritter = new EmptyFilenameWritter(); | |||||
private boolean generateDetail = false; | |||||
public void setLeadingStripper(ILeadingNameStrippper stripper) { | |||||
this.stripper = stripper; | |||||
} | |||||
protected DependencyDetail buildDescription(Entity fromEntity, Entity toEntity, Integer fromLineNumber) { | |||||
if (!generateDetail) return null; | |||||
String fromObject = EntityNameBuilder.build(fromEntity); | |||||
String toObject = EntityNameBuilder.build(toEntity); | |||||
Entity fromFile = fromEntity.getAncestorOfType(FileEntity.class); | |||||
Entity toFile = toEntity.getAncestorOfType(FileEntity.class); | |||||
return new DependencyDetail( | |||||
new LocationInfo(stripper.stripFilename(fromObject),stripper.stripFilename(fromFile.getQualifiedName()),fromLineNumber), | |||||
new LocationInfo(stripper.stripFilename(toObject),stripper.stripFilename(toFile.getQualifiedName()),toEntity.getLine())); | |||||
} | |||||
public void setFilenameRewritter(FilenameWritter filenameWritter) { | |||||
this.filenameWritter = filenameWritter; | |||||
} | |||||
public void setGenerateDetail(boolean generateDetail) { | |||||
this.generateDetail = generateDetail; | |||||
} | |||||
} |
@@ -0,0 +1,102 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.matrix.core; | |||||
import multilang.depends.util.file.path.FilenameWritter; | |||||
import java.util.ArrayList; | |||||
import java.util.Collection; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import static depends.deptypes.DependencyType.DuckTypingLabel; | |||||
public class DependencyMatrix { | |||||
private HashMap<String, DependencyPair> dependencyPairs = new HashMap<>(); | |||||
private ArrayList<String> nodes = new ArrayList<>(); | |||||
private HashMap<Integer,String> nodeIdToName = new HashMap<>(); | |||||
private List<String> typeFilter; | |||||
public DependencyMatrix() { | |||||
} | |||||
public DependencyMatrix(int size) { | |||||
dependencyPairs = new HashMap<>(size); | |||||
} | |||||
public DependencyMatrix(List<String> typeFilter) { | |||||
this.typeFilter = typeFilter; | |||||
} | |||||
public Collection<DependencyPair> getDependencyPairs() { | |||||
return dependencyPairs.values(); | |||||
} | |||||
public void addNode(String name, int id) { | |||||
this.nodes.add(name); | |||||
this.nodeIdToName.put(id, name); | |||||
} | |||||
public void addDependency(String depType, Integer from, Integer to, int weight,List<DependencyDetail> details) { | |||||
if (typeFilter!=null && (!typeFilter.contains(depType))) | |||||
return; | |||||
if(from.equals(to) || from == -1 || to == -1) { | |||||
return; | |||||
} | |||||
if (dependencyPairs.get(DependencyPair.key(from,to))==null) { | |||||
dependencyPairs.put(DependencyPair.key(from,to),new DependencyPair(from,to)); | |||||
} | |||||
DependencyPair dependencyPair = dependencyPairs.get(DependencyPair.key(from,to)); | |||||
dependencyPair.addDependency(depType,weight,details); | |||||
} | |||||
public void addDependency(String depType, Integer from, Integer to, int weight,DependencyDetail detail) { | |||||
if (typeFilter!=null && (!typeFilter.contains(depType.replace(DuckTypingLabel,"")))) | |||||
return; | |||||
if(from.equals(to) || from == -1 || to == -1) { | |||||
return; | |||||
} | |||||
if (dependencyPairs.get(DependencyPair.key(from,to))==null) { | |||||
dependencyPairs.put(DependencyPair.key(from,to),new DependencyPair(from,to)); | |||||
} | |||||
DependencyPair dependencyPair = dependencyPairs.get(DependencyPair.key(from,to)); | |||||
dependencyPair.addDependency(depType,weight,detail); | |||||
} | |||||
public ArrayList<String> getNodes() { | |||||
return nodes; | |||||
} | |||||
public DependencyMatrix reWriteFilenamePattern(FilenameWritter filenameRewritter) { | |||||
this.nodeIdToName = new HashMap<>(); | |||||
for (int i=0;i<nodes.size();i++) { | |||||
String name = filenameRewritter.reWrite(nodes.get(i)); | |||||
nodes.set(i, name ); | |||||
nodeIdToName.put(i, name); | |||||
} | |||||
return this; | |||||
} | |||||
public String getNodeName(Integer key) { | |||||
return nodeIdToName.get(key); | |||||
} | |||||
} |
@@ -0,0 +1,161 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import depends.deptypes.DependencyType; | |||||
import depends.extractor.LangProcessorRegistration; | |||||
import picocli.CommandLine.Command; | |||||
import picocli.CommandLine.Option; | |||||
import picocli.CommandLine.Parameters; | |||||
@Command(name = "depends") | |||||
public class DependsCommand { | |||||
public static class SupportedLangs extends ArrayList<String> { | |||||
private static final long serialVersionUID = 1L; | |||||
public SupportedLangs() { super( LangProcessorRegistration.getRegistry().getLangs()); } | |||||
} | |||||
public static class SupportedTypes extends ArrayList<String> { | |||||
private static final long serialVersionUID = 1L; | |||||
public SupportedTypes() { super( DependencyType.allDependencies()); } | |||||
} | |||||
@Parameters(index = "0", completionCandidates = DependsCommand.SupportedLangs.class, description = "The lanauge of project files: [${COMPLETION-CANDIDATES}]") | |||||
private String lang; | |||||
@Parameters(index = "1", description = "The directory to be analyzed") | |||||
private String src; | |||||
@Parameters(index = "2", description = "The output file name") | |||||
private String output; | |||||
@Option(names = {"-f", "--format"},split=",", description = "the output format: [json(default),xml,excel,detail,dot,plantuml]") | |||||
private String[] format=new String[]{"json"}; | |||||
@Option(names = {"-d", "--dir"}, description = "The output directory") | |||||
private String dir; | |||||
@Option(names = {"-m", "--map"}, description = "Output DV8 dependency map file.") | |||||
private boolean dv8map = true; | |||||
@Option(names = {"-s", "--strip-leading-path"}, description = "Strip the leading path.") | |||||
private boolean stripLeadingPath = false; | |||||
@Option(names = {"--strip-paths"}, split=",", description = "The path(s) to be stripped. if -s enabled, the path(s) start after <src>. " | |||||
+ "Otherwise, the path(s) should be valid.") | |||||
private String[] strippedPaths = new String[]{}; | |||||
@Option(names = {"-g", "--granularity"}, description = "Granularity of dependency.[file(default),method,structure,L#(the level of folder. e.g. L1=1st level folder)]") | |||||
private String granularity="file"; | |||||
@Option(names = {"-p", "--namepattern"}, description = "The name path pattern.[dot(.), unix(/) or windows(\\)") | |||||
private String namePathPattern=""; | |||||
@Option(names = {"-i","--includes"},split=",", description = "The files of searching path") | |||||
private String[] includes = new String[] {}; | |||||
@Option(names = {"--auto-include"},split=",", description = "auto include all paths under the source path (please notice the potential side effect)") | |||||
private boolean autoInclude = false; | |||||
@Option(names = {"--detail"},split=",", description = "add detail dependency information to output (only applicable for JSON output format)") | |||||
private boolean detail = false; | |||||
@Option(names = {"--auto-stub"},split=",", description = "create stub files for unsolved symbols (exprimental feature, only for java)") | |||||
private boolean autoStub = false; | |||||
@Option(names = {"--type-filter"},split=",", completionCandidates = DependsCommand.SupportedTypes.class, description = "only filter the listed dependency types[${COMPLETION-CANDIDATES}]") | |||||
private String[] typeFilter=new String[]{}; | |||||
@Option(names = {"--external-deps"}, description = "Output external dependencies") | |||||
private boolean outputExternalDependencies = false; | |||||
@Option(names = {"--duck-typing-deduce"}, description = "Deduce implicit variable types") | |||||
private boolean duckTypingDeduce = true; | |||||
@Option(names = {"-h","--help"}, usageHelp = true, description = "display this help and exit") | |||||
boolean help; | |||||
public DependsCommand() { | |||||
} | |||||
public String getLang() { | |||||
return lang; | |||||
} | |||||
public void setLang(String lang) { | |||||
this.lang = lang; | |||||
} | |||||
public String getSrc() { | |||||
return src; | |||||
} | |||||
public void setSrc(String src) { | |||||
this.src = src; | |||||
} | |||||
public String getOutputName() { | |||||
return output; | |||||
} | |||||
public void setOutput(String output) { | |||||
this.output = output; | |||||
} | |||||
public String[] getFormat() { | |||||
return format; | |||||
} | |||||
public String getOutputDir() { | |||||
if (dir==null) { | |||||
dir = System.getProperty("user.dir"); | |||||
} | |||||
return dir; | |||||
} | |||||
public boolean isDv8map() { | |||||
return dv8map; | |||||
} | |||||
public String[] getIncludes() { | |||||
return includes; | |||||
} | |||||
public boolean isHelp() { | |||||
return help; | |||||
} | |||||
public String getGranularity() { | |||||
return granularity; | |||||
} | |||||
public String getNamePathPattern() { | |||||
return namePathPattern; | |||||
} | |||||
public boolean isStripLeadingPath() { | |||||
return stripLeadingPath; | |||||
} | |||||
public boolean isAutoInclude () { | |||||
return autoInclude; | |||||
} | |||||
public boolean isDetail () { | |||||
return detail; | |||||
} | |||||
public String[] getStrippedPaths() { | |||||
return strippedPaths; | |||||
} | |||||
public void setStrippedPaths(String[] strippedPaths) { | |||||
this.strippedPaths = strippedPaths; | |||||
} | |||||
public boolean isAutoStub() { | |||||
return autoStub; | |||||
} | |||||
public List<String> getTypeFilter() { | |||||
if (typeFilter.length==0) { | |||||
return DependencyType.allDependencies(); | |||||
} | |||||
return java.util.Arrays.asList(typeFilter); | |||||
} | |||||
public boolean isOutputExternalDependencies() { | |||||
return outputExternalDependencies; | |||||
} | |||||
public boolean isDuckTypingDeduce() { | |||||
return this.duckTypingDeduce; | |||||
} | |||||
} |
@@ -0,0 +1,222 @@ | |||||
/******************************************************************************* | |||||
* Copyright (c) 2007, 2014 BEA Systems, Inc. and others. | |||||
* | |||||
* This program and the accompanying materials | |||||
* are made available under the terms of the Eclipse Public License 2.0 | |||||
* which accompanies this distribution, and is available at | |||||
* https://www.eclipse.org/legal/epl-2.0/ | |||||
* | |||||
* SPDX-License-Identifier: EPL-2.0 | |||||
* | |||||
* Contributors: | |||||
* BEA Systems, Inc. - initial API and implementation | |||||
* Jesper Steen Moller - Bug 412150 [1.8] [compiler] Enable reflected parameter names during annotation processing | |||||
*******************************************************************************/ | |||||
package org.eclipse.jdt.apt.pluggable.tests; | |||||
import java.io.File; | |||||
import java.io.InputStream; | |||||
import java.net.URL; | |||||
import java.util.Map; | |||||
import javax.lang.model.SourceVersion; | |||||
import org.eclipse.core.resources.IFile; | |||||
import org.eclipse.core.resources.IProject; | |||||
import org.eclipse.core.runtime.CoreException; | |||||
import org.eclipse.core.runtime.IPath; | |||||
import org.eclipse.jdt.apt.core.internal.util.FactoryContainer; | |||||
import org.eclipse.jdt.apt.core.internal.util.FactoryContainer.FactoryType; | |||||
import org.eclipse.jdt.apt.core.internal.util.FactoryPath; | |||||
import org.eclipse.jdt.apt.core.util.AptConfig; | |||||
import org.eclipse.jdt.core.IClasspathAttribute; | |||||
import org.eclipse.jdt.core.IJavaProject; | |||||
import org.eclipse.jdt.core.JavaCore; | |||||
import org.eclipse.jdt.core.tests.builder.BuilderTests; | |||||
import org.eclipse.jdt.core.tests.util.Util; | |||||
import org.eclipse.jdt.internal.core.ClasspathEntry; | |||||
import junit.framework.Test; | |||||
public class TestBase extends BuilderTests | |||||
{ | |||||
protected static final String JAVA_16_COMPLIANCE = "1.6"; | |||||
protected static final String JAVA_18_COMPLIANCE = "1.8"; | |||||
protected static final String JAVA_9_COMPLIANCE = "9"; | |||||
protected String _projectName; | |||||
protected static int _projectSerial = 0; // used to create unique project names, to avoid resource deletion problems | |||||
public TestBase(String name) { | |||||
super(name); | |||||
} | |||||
public static Test suite() { | |||||
throw new IllegalStateException("This is a base test class whose suite() method must not be called.\n" | |||||
+ "This exception is thrown to avoid running org.eclipse.jdt.core.tests.builder.BuilderTests#suite() twice."); | |||||
} | |||||
/** | |||||
* Extract lib/annotations.jar from the test bundle and add it to the specified project | |||||
*/ | |||||
private static void addAnnotationJar(IJavaProject jproj, boolean addToModulePath) throws Exception { | |||||
final String resName = "lib/annotations.jar"; // name in bundle | |||||
final String libName = resName; // name in destination project | |||||
InputStream is = null; | |||||
URL resURL = Apt6TestsPlugin.thePlugin().getBundle().getEntry(resName); | |||||
is = resURL.openStream(); | |||||
IPath projPath = jproj.getPath(); | |||||
IProject proj = jproj.getProject(); | |||||
IFile libFile = proj.getFile(libName); | |||||
env.addFolder(projPath, "lib"); | |||||
if (libFile.exists()) { | |||||
libFile.setContents(is, true, false, null); | |||||
} else { | |||||
libFile.create(is, true, null); | |||||
} | |||||
if (addToModulePath) { | |||||
IClasspathAttribute[] attributes = { JavaCore.newClasspathAttribute(IClasspathAttribute.MODULE, "true") }; | |||||
env.addEntry(projPath, JavaCore.newLibraryEntry(libFile.getFullPath(), null, null, | |||||
ClasspathEntry.NO_ACCESS_RULES, attributes, false)); | |||||
} else { | |||||
env.addLibrary(projPath, libFile.getFullPath(), null, null); | |||||
} | |||||
} | |||||
/** | |||||
* Create a java project with java libraries and test annotations on classpath | |||||
* (compiler level is 1.6). Use "src" as source folder and "bin" as output folder. APT | |||||
* is not enabled. | |||||
* | |||||
* @param projectName | |||||
* @return a java project that has been added to the current workspace. | |||||
* @throws Exception | |||||
*/ | |||||
protected static IJavaProject createJavaProject(final String projectName) throws Exception | |||||
{ | |||||
IPath projectPath = env.addProject(projectName, JAVA_16_COMPLIANCE); | |||||
env.addExternalJars(projectPath, Util.getJavaClassLibs()); | |||||
// remove old package fragment root so that names don't collide | |||||
env.removePackageFragmentRoot(projectPath, ""); //$NON-NLS-1$ | |||||
env.addPackageFragmentRoot(projectPath, "src"); //$NON-NLS-1$ | |||||
env.setOutputFolder(projectPath, "bin"); //$NON-NLS-1$ | |||||
final IJavaProject javaProj = env.getJavaProject(projectPath); | |||||
addAnnotationJar(javaProj, false); | |||||
return javaProj; | |||||
} | |||||
/** | |||||
* Create a java project with java libraries and test annotations on classpath | |||||
* (compiler level is 1.8). Use "src" as source folder and "bin" as output folder. APT | |||||
* is not enabled. | |||||
* | |||||
* @param projectName | |||||
* @return a java project that has been added to the current workspace. | |||||
* @throws Exception | |||||
*/ | |||||
protected static IJavaProject createJava8Project(final String projectName) throws Exception { | |||||
// Note, make sure this is run only with a JRE 8 and above. | |||||
IPath projectPath = env.addProject(projectName, JAVA_18_COMPLIANCE); | |||||
env.addExternalJars(projectPath, Util.getJavaClassLibs()); | |||||
// remove old package fragment root so that names don't collide | |||||
env.removePackageFragmentRoot(projectPath, ""); //$NON-NLS-1$ | |||||
env.addPackageFragmentRoot(projectPath, "src"); //$NON-NLS-1$ | |||||
env.setOutputFolder(projectPath, "bin"); //$NON-NLS-1$ | |||||
final IJavaProject javaProj = env.getJavaProject(projectPath); | |||||
javaProj.getProject().getFolder("prebuilt").create(true, true, null); | |||||
javaProj.getProject().getFolder("prebuilt").getFolder("p").create(true, true, null); | |||||
env.addClassFolder(projectPath, projectPath.append("prebuilt"), true); | |||||
addAnnotationJar(javaProj, false); | |||||
return javaProj; | |||||
} | |||||
/** | |||||
* Create a java project with java libraries and test annotations on modulepath | |||||
* (compiler level is 1.9). Use "src" as source folder and "bin" as output folder. APT | |||||
* is not enabled. | |||||
* | |||||
* @param projectName | |||||
* @return a java project that has been added to the current workspace. | |||||
* @throws Exception | |||||
*/ | |||||
protected static IJavaProject createJava9Project(final String projectName) throws Exception { | |||||
// Note, make sure this is run only with a JRE 9 and above. | |||||
IPath projectPath = env.addProject(projectName, JAVA_9_COMPLIANCE); | |||||
env.addExternalJars(projectPath, Util.getJavaClassLibs()); | |||||
// remove old package fragment root so that names don't collide | |||||
env.removePackageFragmentRoot(projectPath, ""); //$NON-NLS-1$ | |||||
env.addPackageFragmentRoot(projectPath, "src"); //$NON-NLS-1$ | |||||
env.setOutputFolder(projectPath, "bin"); //$NON-NLS-1$ | |||||
final IJavaProject javaProj = env.getJavaProject(projectPath); | |||||
addAnnotationJar(javaProj, true); | |||||
return javaProj; | |||||
} | |||||
/** | |||||
* Ensure that there are no Java 5 processors on the factory path, as they can cause | |||||
* units to be multiply compiled, which can mess up tests that expect a certain number | |||||
* of compilations to occur. | |||||
* @param jproj the project whose factory path will be edited | |||||
* @throws CoreException | |||||
*/ | |||||
protected void disableJava5Factories(IJavaProject jproj) throws CoreException { | |||||
FactoryPath fp = (FactoryPath) AptConfig.getFactoryPath(jproj); | |||||
for (Map.Entry<FactoryContainer, FactoryPath.Attributes> entry : fp.getAllContainers().entrySet()) { | |||||
if (entry.getKey().getType() == FactoryType.PLUGIN) { | |||||
String id = entry.getKey().getId(); | |||||
if (!Apt6TestsPlugin.PLUGIN_ID.equals(id)) { | |||||
fp.disablePlugin(id); | |||||
} | |||||
} | |||||
} | |||||
AptConfig.setFactoryPath(jproj, fp); | |||||
} | |||||
/** | |||||
* Verify that an expected file exists within a project. | |||||
* @param fileName the filename relative to the project root. | |||||
*/ | |||||
protected void expectingFile(IProject proj, String fileName) throws Exception | |||||
{ | |||||
IPath path = proj.getLocation().append(fileName); | |||||
File file = new File(path.toOSString()); | |||||
assertTrue("Expected file " + fileName + " was missing from project", file != null && file.exists()); | |||||
} | |||||
/** | |||||
* Verify that an expected file exists within a project. | |||||
* @param fileName the filename relative to the project root. | |||||
*/ | |||||
protected void expectingNoFile(IProject proj, String fileName) throws Exception | |||||
{ | |||||
IPath path = proj.getLocation().append(fileName); | |||||
File file = new File(path.toOSString()); | |||||
boolean exists = file.exists(); | |||||
// work around a timing bug in some versions of JRE 1.6 on Linux: | |||||
// Before assuming the test has failed, wait half a second and try again. | |||||
// This delay is not encountered when the test is passing normally. | |||||
if (exists) { | |||||
Thread.sleep(500); | |||||
exists = file.exists(); | |||||
} | |||||
assertTrue("File " + fileName + " was expected to not exist", file == null || !exists); | |||||
} | |||||
@Override | |||||
protected void setUp() throws Exception | |||||
{ | |||||
super.setUp(); | |||||
env.setAutoBuilding(false); | |||||
_projectName = String.format("testproj%04d", ++_projectSerial); | |||||
} | |||||
public boolean canRunJava9() { | |||||
try { | |||||
SourceVersion.valueOf("RELEASE_9"); | |||||
} catch(IllegalArgumentException iae) { | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
} |
@@ -0,0 +1,275 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.entity; | |||||
import depends.relations.IBindingResolver; | |||||
import depends.relations.Relation; | |||||
import java.util.*; | |||||
/** | |||||
* Entity is the root of all entities, including file, package, module, | |||||
* class, method/function etc. | |||||
* Each entity has unique id, name,qualifiedName, parent, children | |||||
* We also use entity to record relations | |||||
*/ | |||||
public abstract class Entity { | |||||
Integer id=-1; | |||||
String qualifiedName = null; | |||||
GenericName rawName = GenericName.build(""); | |||||
Entity parent; | |||||
private MultiDeclareEntities mutliDeclare = null; | |||||
private Set<Entity> children; | |||||
ArrayList<Relation> relations; | |||||
private Entity actualReferTo = null; | |||||
private boolean inScope = true; | |||||
protected HashMap<String, Entity> visibleNames = new HashMap<>(); | |||||
private Location location = new Location(); | |||||
public Entity() {}; | |||||
public Entity(GenericName rawName, Entity parent, Integer id) { | |||||
this.qualifiedName = null; | |||||
this.rawName = rawName; | |||||
this.parent = parent; | |||||
this.id = id; | |||||
if (parent!=null) | |||||
parent.addChild(this); | |||||
deduceQualifiedName(); | |||||
visibleNames.put(rawName.getName(), this); | |||||
visibleNames.put(qualifiedName, this); | |||||
} | |||||
private Set<Entity> children() { | |||||
if (children==null) | |||||
children = new HashSet<>(); | |||||
return children; | |||||
} | |||||
/** | |||||
* Rule 1: if it start with '.' , then the name is equal to raw name | |||||
* Rule 2: if parent not exists, the name is equal to raw name | |||||
* Rule 3: if parent exists but no qualified name exists or empty, the name is equal to raw name | |||||
* Rule 4: otherwise, qualified name = parent_qualfied_name + "."+rawName | |||||
* Rule 5: make sure the qualified name do not start with '.' | |||||
*/ | |||||
private void deduceQualifiedName() { | |||||
rawName = rawName.replace("::","." ); | |||||
if (this.rawName.startsWith(".")) { | |||||
this.qualifiedName = this.rawName.uniqName().substring(1); | |||||
return; //already qualified | |||||
} | |||||
if (parent==null) { | |||||
this.qualifiedName = this.rawName.uniqName(); | |||||
return; | |||||
} | |||||
if (parent.getQualifiedName(true)==null) { | |||||
this.qualifiedName = this.rawName.uniqName(); | |||||
return; | |||||
} | |||||
if (parent.getQualifiedName(true).isEmpty()) { | |||||
this.qualifiedName = rawName.uniqName(); | |||||
return; | |||||
} | |||||
this.qualifiedName= parent.getQualifiedName(true)+"." + rawName.uniqName(); | |||||
} | |||||
public GenericName getRawName() { | |||||
return rawName; | |||||
} | |||||
public Integer getId() { | |||||
return id; | |||||
} | |||||
public void addRelation(Relation relation) { | |||||
if (relations==null) | |||||
relations = new ArrayList<>(); | |||||
if (relation.getEntity()==null) return; | |||||
relations.add(relation); | |||||
} | |||||
public ArrayList<Relation> getRelations() { | |||||
if (relations==null) | |||||
return new ArrayList<>(); | |||||
return relations; | |||||
} | |||||
public void addChild(Entity child) { | |||||
children().add(child); | |||||
visibleNames.put(child.getRawName().getName(), child); | |||||
visibleNames.put(child.getQualifiedName(), child); | |||||
} | |||||
public Entity getParent() { | |||||
return parent; | |||||
} | |||||
public void setParent(Entity parent) { | |||||
this.parent = parent; | |||||
} | |||||
public Collection<Entity> getChildren() { | |||||
if (children==null) | |||||
return new HashSet<>(); | |||||
return children; | |||||
} | |||||
public void setQualifiedName(String qualifiedName) { | |||||
this.qualifiedName = qualifiedName; | |||||
} | |||||
public void setRawName(GenericName rawName) { | |||||
this.rawName = rawName; | |||||
deduceQualifiedName(); | |||||
} | |||||
public final String getQualifiedName() { | |||||
return qualifiedName; | |||||
} | |||||
public String getQualifiedName(boolean overrideFileWithPackage) { | |||||
return qualifiedName; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return "Entity [id=" + id + ", qualifiedName=" + qualifiedName + ", rawName=" + rawName + "]"; | |||||
} | |||||
/** | |||||
* Get ancestor of type. | |||||
* @param classType | |||||
* @return null (if not exist) or the type | |||||
*/ | |||||
public Entity getAncestorOfType(@SuppressWarnings("rawtypes") Class classType) { | |||||
Entity fromEntity = this; | |||||
while(fromEntity!=null) { | |||||
if (fromEntity.getClass().equals(classType)) | |||||
return fromEntity; | |||||
if (fromEntity.getParent()==null) return null; | |||||
fromEntity = fromEntity.getParent(); | |||||
} | |||||
return null; | |||||
} | |||||
/** | |||||
* Invoke inferer to resolve the entity type etc. | |||||
* */ | |||||
public void inferEntities(IBindingResolver bindingResolver) { | |||||
inferLocalLevelEntities(bindingResolver); | |||||
for (Entity child:this.getChildren()) { | |||||
child.inferEntities(bindingResolver); | |||||
} | |||||
} | |||||
public abstract void inferLocalLevelEntities(IBindingResolver bindingResolver); | |||||
public TypeEntity getType() { | |||||
return null; | |||||
} | |||||
public String getDisplayName() { | |||||
return getRawName().uniqName(); | |||||
} | |||||
public MultiDeclareEntities getMutliDeclare() { | |||||
return mutliDeclare; | |||||
} | |||||
public void setMutliDeclare(MultiDeclareEntities mutliDeclare) { | |||||
this.mutliDeclare = mutliDeclare; | |||||
} | |||||
public Entity getActualReferTo() { | |||||
if (this.actualReferTo ==null) | |||||
return this; | |||||
return actualReferTo; | |||||
} | |||||
public void setActualReferTo(Entity actualReferTo) { | |||||
this.actualReferTo = actualReferTo; | |||||
} | |||||
public static void setParent(Entity child, Entity parent) { | |||||
if (parent == null) | |||||
return; | |||||
if (child == null) | |||||
return; | |||||
if (parent.equals(child.getParent())) | |||||
return; | |||||
child.setParent(parent); | |||||
parent.addChild(child); | |||||
} | |||||
@Override | |||||
public int hashCode() { | |||||
final int prime = 31; | |||||
int result = 1; | |||||
result = prime * result + ((id == null) ? 0 : id.hashCode()); | |||||
return result; | |||||
} | |||||
@Override | |||||
public boolean equals(Object obj) { | |||||
if (this == obj) | |||||
return true; | |||||
if (obj == null) | |||||
return false; | |||||
if (getClass() != obj.getClass()) | |||||
return false; | |||||
Entity other = (Entity) obj; | |||||
if (id == null) { | |||||
if (other.id != null) | |||||
return false; | |||||
} else if (!id.equals(other.id)) | |||||
return false; | |||||
return true; | |||||
} | |||||
public void setInScope(boolean value) { | |||||
this.inScope = value; | |||||
children().forEach(child->child.setInScope(value)); | |||||
} | |||||
public boolean inScope() { | |||||
return inScope; | |||||
} | |||||
public Entity getByName(String name, HashSet<Entity> searched) { | |||||
if (searched.contains(this)) return null; | |||||
searched.add(this); | |||||
return visibleNames.get(name); | |||||
} | |||||
public Integer getLine() { | |||||
return location.getLine(); | |||||
} | |||||
public void setLine(int lineNumber) { | |||||
this.location.setLine(lineNumber); | |||||
} | |||||
public Location getLocation() { | |||||
return this.location; | |||||
} | |||||
} |
@@ -0,0 +1,101 @@ | |||||
package depends.extractor.pom; | |||||
import org.junit.Before; | |||||
import org.junit.Test; | |||||
import java.io.IOException; | |||||
import static org.junit.Assert.assertEquals; | |||||
import static org.junit.Assert.assertNotNull; | |||||
public class EntityExtractTest extends MavenParserTest{ | |||||
@Before | |||||
public void setUp() { | |||||
super.init(); | |||||
} | |||||
@Test | |||||
public void use_package_contains() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/maven-code-examples/simple/log4j.pom", | |||||
}; | |||||
for (String src:srcs) { | |||||
PomFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
assertEquals(0,entityRepo.getEntity("org.log4j-test.log4j_1.2.12_").getRelations().size()); | |||||
} | |||||
@Test | |||||
public void should_use_parent_groupId() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/maven-code-examples/use_parent_groupId_and_version.pom", | |||||
}; | |||||
for (String src:srcs) { | |||||
PomFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
assertNotNull(entityRepo.getEntity("org.apache.maven.surefire.surefire-junit4_2.12.4_")); | |||||
} | |||||
@Test | |||||
public void should_parse_properties_in_same_pom() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/maven-code-examples/properties-test1.pom", | |||||
}; | |||||
for (String src:srcs) { | |||||
PomFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
PomArtifactEntity entity = (PomArtifactEntity)(entityRepo.getEntity("properties-test.test_1_")); | |||||
/* | |||||
<project.version>1.00</project.version> | |||||
<activeio-version>3.1.4</activeio-version> | |||||
<projectName>Apache ActiveMQ</projectName> | |||||
<siteId>activemq-${project.version}</siteId> */ | |||||
assertEquals("1.00",entity.getProperty("project.version")); | |||||
assertEquals("activemq-1.00",entity.getProperty("siteId")); | |||||
} | |||||
@Test | |||||
public void should_parse_multiple_properties_in_same_pom() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/maven-code-examples/properties-test1.pom", | |||||
}; | |||||
for (String src:srcs) { | |||||
PomFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
PomArtifactEntity entity = (PomArtifactEntity)(entityRepo.getEntity("properties-test.test_1_")); | |||||
/* | |||||
<project.version>1.00</project.version> | |||||
<activeio-version>3.1.4</activeio-version> | |||||
<projectName>Apache ActiveMQ</projectName> | |||||
<anotherId>activemq-${project.version}--${activeio-version}</anotherId> */ | |||||
assertEquals("activemq-1.00-3.1.4",entity.getProperty("anotherId")); | |||||
} | |||||
@Test | |||||
public void should_parse_multiple_properties_in_parent_pom() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/maven-code-examples/properties-test-child.pom" | |||||
}; | |||||
for (String src:srcs) { | |||||
PomFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
PomArtifactEntity entity = (PomArtifactEntity)(entityRepo.getEntity("properties-test.test_1_")); | |||||
assertEquals("13",entity.getProperty("project.version")); | |||||
} | |||||
} |
@@ -0,0 +1,120 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.format.excel; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.util.Collection; | |||||
import org.apache.poi.hssf.usermodel.HSSFCell; | |||||
import org.apache.poi.hssf.usermodel.HSSFRow; | |||||
import org.apache.poi.hssf.usermodel.HSSFSheet; | |||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; | |||||
import depends.format.AbstractFormatDependencyDumper; | |||||
import depends.matrix.core.DependencyMatrix; | |||||
import depends.matrix.core.DependencyPair; | |||||
import depends.matrix.core.DependencyValue; | |||||
public class ExcelXlsFormatDependencyDumper extends AbstractFormatDependencyDumper { | |||||
private HSSFWorkbook workbook; | |||||
private HSSFSheet sheet; | |||||
@Override | |||||
public String getFormatName() { | |||||
return "xls"; | |||||
} | |||||
public ExcelXlsFormatDependencyDumper(DependencyMatrix dependencyMatrix, String projectName, String outputDir) { | |||||
super(dependencyMatrix, projectName,outputDir); | |||||
} | |||||
@Override | |||||
public boolean output() { | |||||
String filename = composeFilename() + ".xls"; | |||||
if (matrix.getNodes().size() > 255) { | |||||
System.out.println("We can only export small matrix(<256 items) to excel" + "due to MS Office limitation"); | |||||
return false; | |||||
} | |||||
startFile(); | |||||
Collection<DependencyPair> dependencyPairs = matrix.getDependencyPairs(); | |||||
HSSFRow[] row = new HSSFRow[matrix.getNodes().size()]; | |||||
// create header row | |||||
HSSFRow header = sheet.createRow(0); | |||||
for (int i = 0; i < matrix.getNodes().size(); i++) { | |||||
HSSFCell cell = header.createCell(i + 2); | |||||
cell.setCellValue(i); | |||||
} | |||||
; | |||||
// create header col | |||||
for (int i = 0; i < matrix.getNodes().size(); i++) { | |||||
row[i] = sheet.createRow(i + 1); | |||||
String node = matrix.getNodes().get(i); | |||||
HSSFCell cell = row[i].createCell(0); | |||||
cell.setCellValue(i); | |||||
cell = row[i].createCell(1); | |||||
cell.setCellValue(node); | |||||
} | |||||
; | |||||
// create header col | |||||
for (int i = 0; i < matrix.getNodes().size(); i++) { | |||||
HSSFCell cell = row[i].createCell(i + 2); | |||||
cell.setCellValue("(" + i + ")"); | |||||
} | |||||
; | |||||
for (DependencyPair dependencyPair : dependencyPairs) { | |||||
HSSFCell cell = row[dependencyPair.getFrom()].createCell(dependencyPair.getTo() + 2); | |||||
cell.setCellValue(buildDependencyValues(dependencyPair.getDependencies())); | |||||
} | |||||
closeFile(filename); | |||||
return true; | |||||
} | |||||
private String buildDependencyValues(Collection<DependencyValue> dependencies) { | |||||
StringBuilder sb = new StringBuilder(); | |||||
for (DependencyValue dependency : dependencies) { | |||||
String comma = sb.length() > 0 ? "," : ""; | |||||
sb.append(comma).append(dependency.getType()).append("(").append(dependency.getWeight()).append(")"); | |||||
} | |||||
return sb.toString(); | |||||
} | |||||
private void closeFile(String filename) { | |||||
try { | |||||
workbook.write(new File(filename)); | |||||
workbook.close(); | |||||
} catch (IOException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
} | |||||
private void startFile() { | |||||
workbook = new HSSFWorkbook(); | |||||
sheet = workbook.createSheet("DSM"); | |||||
} | |||||
} |
@@ -0,0 +1,119 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.format.excel; | |||||
import java.io.FileOutputStream; | |||||
import java.io.IOException; | |||||
import java.util.Collection; | |||||
import org.apache.poi.xssf.usermodel.XSSFCell; | |||||
import org.apache.poi.xssf.usermodel.XSSFRow; | |||||
import org.apache.poi.xssf.usermodel.XSSFSheet; | |||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook; | |||||
import depends.format.AbstractFormatDependencyDumper; | |||||
import depends.matrix.core.DependencyMatrix; | |||||
import depends.matrix.core.DependencyPair; | |||||
import depends.matrix.core.DependencyValue; | |||||
public class ExcelXlsxFormatDependencyDumper extends AbstractFormatDependencyDumper { | |||||
private XSSFWorkbook workbook; | |||||
private XSSFSheet sheet; | |||||
@Override | |||||
public String getFormatName() { | |||||
return "xlsx"; | |||||
} | |||||
public ExcelXlsxFormatDependencyDumper(DependencyMatrix dependencyMatrix, String projectName, String outputDir) { | |||||
super(dependencyMatrix, projectName,outputDir); | |||||
} | |||||
@Override | |||||
public boolean output() { | |||||
String filename = composeFilename() + ".xlsx"; | |||||
startFile(); | |||||
Collection<DependencyPair> dependencyPairs = matrix.getDependencyPairs(); | |||||
XSSFRow[] row = new XSSFRow[matrix.getNodes().size()]; | |||||
// create header row | |||||
XSSFRow header = sheet.createRow(0); | |||||
for (int i = 0; i < matrix.getNodes().size(); i++) { | |||||
XSSFCell cell = header.createCell(i + 2); | |||||
cell.setCellValue(i); | |||||
} | |||||
; | |||||
// create header col | |||||
for (int i = 0; i < matrix.getNodes().size(); i++) { | |||||
row[i] = sheet.createRow(i + 1); | |||||
String node = matrix.getNodes().get(i); | |||||
XSSFCell cell = row[i].createCell(0); | |||||
cell.setCellValue(i); | |||||
cell = row[i].createCell(1); | |||||
cell.setCellValue(node); | |||||
} | |||||
; | |||||
// create header col | |||||
for (int i = 0; i < matrix.getNodes().size(); i++) { | |||||
XSSFCell cell = row[i].createCell(i + 2); | |||||
cell.setCellValue("(" + i + ")"); | |||||
} | |||||
; | |||||
for (DependencyPair dependencyPair : dependencyPairs) { | |||||
XSSFCell cell = row[dependencyPair.getFrom()].createCell(dependencyPair.getTo() + 2); | |||||
cell.setCellValue(buildDependencyValues(dependencyPair.getDependencies())); | |||||
} | |||||
closeFile(filename); | |||||
return true; | |||||
} | |||||
private String buildDependencyValues(Collection<DependencyValue> dependencies) { | |||||
StringBuilder sb = new StringBuilder(); | |||||
for (DependencyValue dependency : dependencies) { | |||||
String comma = sb.length() > 0 ? "," : ""; | |||||
sb.append(comma).append(dependency.getType()).append("(").append(dependency.getWeight()).append(")"); | |||||
} | |||||
return sb.toString(); | |||||
} | |||||
private void closeFile(String filename) { | |||||
try { | |||||
FileOutputStream out = new FileOutputStream(filename); | |||||
workbook.write(out); | |||||
workbook.close(); | |||||
out.close(); | |||||
} catch (IOException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
} | |||||
private void startFile() { | |||||
workbook = new XSSFWorkbook(); | |||||
sheet = workbook.createSheet("DSM"); | |||||
} | |||||
} |
@@ -0,0 +1,413 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.entity; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.relations.IBindingResolver; | |||||
import java.io.Serializable; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
/** | |||||
* Expression | |||||
*/ | |||||
public class Expression implements Serializable{ | |||||
private static final long serialVersionUID = 1L; | |||||
public Integer id; | |||||
private String text; // for debug purpose | |||||
private GenericName rawType; // the raw type name | |||||
private GenericName identifier; // the varName, or method name, etc. | |||||
private boolean isSet = false; // is a set relation from right to leftHand | |||||
private boolean isDot = false; // is a dot expression, will decuce variable tfype left to right | |||||
private boolean isCall = false; | |||||
private boolean isLogic = false; | |||||
private boolean isCreate = false; | |||||
private boolean isCast = false; | |||||
private boolean isThrow = false; | |||||
private boolean isStatement = false; //statement is only used for return type calcuation in some langs such as ruby | |||||
//they will not be treat as real expressions in case of relation calculation | |||||
private boolean deriveTypeFromChild = true; | |||||
private Integer deduceTypeBasedId; //by default, parent expression type determined by most left child | |||||
private Integer parentId = -1; | |||||
private transient Expression parent; | |||||
private transient List<VarEntity> deducedTypeVars = new ArrayList<>(); | |||||
private List<Integer> deducedTypeVarsId = new ArrayList<>(); | |||||
private transient List<FunctionEntity> deducedTypeFunctions= new ArrayList<>(); | |||||
private List<Integer> deducedTypeFunctionsId = new ArrayList<>(); | |||||
private Integer referredEntityId; | |||||
private transient Entity referredEntity; | |||||
private transient TypeEntity type; // the type we care - for relation calculation. | |||||
private Location location = new Location(); | |||||
//for leaf, it equals to referredEntity.getType. otherwise, depends on child's type strategy | |||||
/* | |||||
* */ | |||||
public Expression() { | |||||
deducedTypeVars = new ArrayList<>(); | |||||
deducedTypeFunctions = new ArrayList<>(); | |||||
} | |||||
public Expression(Integer id) { | |||||
this.id = id; | |||||
deducedTypeVars = new ArrayList<>(); | |||||
deducedTypeFunctions = new ArrayList<>(); | |||||
} | |||||
public void reload(EntityRepo repo, ArrayList<Expression> expressionList) { | |||||
this.deducedTypeFunctions = new ArrayList<>(); | |||||
this.deducedTypeVars = new ArrayList<>(); | |||||
//recover parent relation | |||||
if (parentId!=-1) { | |||||
for (Expression expr:expressionList) { | |||||
if (expr.id==parentId) { | |||||
parent = expr; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
//recover deducedTypeFunctionsId | |||||
if (deducedTypeFunctionsId!=null) { | |||||
for (Integer funcId:this.deducedTypeFunctionsId) { | |||||
this.deducedTypeFunctions.add((FunctionEntity) repo.getEntity(funcId)); | |||||
} | |||||
} | |||||
//recover deducedTypeVars | |||||
if (deducedTypeVarsId!=null) { | |||||
for (Integer varId:this.deducedTypeVarsId) { | |||||
this.deducedTypeVars.add((VarEntity) repo.getEntity(varId)); | |||||
} | |||||
} | |||||
//referer referredEntity -- TODO:maybe not require | |||||
if (this.referredEntityId!=null && this.referredEntity==null) { | |||||
this.referredEntity = repo.getEntity(this.referredEntityId); | |||||
if (this.referredEntity ==null){ | |||||
System.err.println("unexpected: referred Entity is null" + this.referredEntityId + this.text+this.id); | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Set type of the expression | |||||
* @param type | |||||
* @param referredEntity | |||||
* @param bindingResolver | |||||
*/ | |||||
public void setType(TypeEntity type, Entity referredEntity, IBindingResolver bindingResolver) { | |||||
if (this.getReferredEntity()==null && referredEntity!=null) { | |||||
this.setReferredEntity(referredEntity); | |||||
} | |||||
boolean changedType = false; | |||||
if (this.type==null && type!=null) { | |||||
this.type = type; | |||||
for (VarEntity var:deducedTypeVars) { | |||||
if (var!=null) { | |||||
var.setType(this.type); | |||||
} | |||||
} | |||||
for (FunctionEntity func:deducedTypeFunctions) { | |||||
if (func!=null) { | |||||
func.addReturnType(this.type); | |||||
} | |||||
} | |||||
changedType = true; | |||||
} | |||||
if (this.referredEntity==null) | |||||
this.setReferredEntity(this.type); | |||||
if (changedType) | |||||
deduceTheParentType(bindingResolver); | |||||
} | |||||
/** | |||||
* deduce type of parent based on child's type | |||||
* @param bindingResolver | |||||
*/ | |||||
private void deduceTheParentType(IBindingResolver bindingResolver) { | |||||
if (this.type==null) return; | |||||
if (this.parent==null) return; | |||||
Expression parent = this.parent; | |||||
if (parent.type != null)return; | |||||
if (!parent.deriveTypeFromChild) return; | |||||
//parent's type depends on first child's type | |||||
if (parent.deduceTypeBasedId!=this.id) return; | |||||
//if child is a built-in/external type, then parent must also a built-in/external type | |||||
if (this.type.equals(TypeEntity.buildInType)) { | |||||
parent.setType(TypeEntity.buildInType,TypeEntity.buildInType, bindingResolver); | |||||
return; | |||||
} | |||||
/* if it is a logic expression, the return type/type is boolean. */ | |||||
if (parent.isLogic) { | |||||
parent.setType(TypeEntity.buildInType,null, bindingResolver); | |||||
} | |||||
/* if it is a.b, and we already get a's type, b's type could be identified easily */ | |||||
else if (parent.isDot) { | |||||
if (parent.isCall()) { | |||||
List<Entity> funcs = this.getType().lookupFunctionInVisibleScope(parent.identifier); | |||||
setReferredFunctions(bindingResolver, parent, funcs); | |||||
}else { | |||||
Entity var = this.getType().lookupVarInVisibleScope(parent.identifier); | |||||
if (var!=null) { | |||||
parent.setType(var.getType(),var, bindingResolver); | |||||
parent.setReferredEntity(var); | |||||
}else { | |||||
List<Entity> funcs = this.getType().lookupFunctionInVisibleScope(parent.identifier); | |||||
setReferredFunctions(bindingResolver,parent,funcs); | |||||
} | |||||
} | |||||
if (parent.getType()==null) { | |||||
parent.setType(bindingResolver.inferTypeFromName(this.getType(), parent.identifier),null, bindingResolver); | |||||
} | |||||
} | |||||
/* if other situation, simple make the parent and child type same */ | |||||
else { | |||||
parent.setType(type, null, bindingResolver); | |||||
} | |||||
if (parent.getReferredEntity()==null) | |||||
parent.setReferredEntity(parent.type); | |||||
} | |||||
private void setReferredFunctions(IBindingResolver bindingResolver, Expression expr, List<Entity> funcs) { | |||||
if (funcs ==null ||funcs.size()==0) return; | |||||
Entity func = funcs.get(0); | |||||
if (funcs.size()==1){ | |||||
expr.setType(func.getType(), func, bindingResolver); | |||||
expr.setReferredEntity(func); | |||||
return; | |||||
} | |||||
MultiDeclareEntities m = new MultiDeclareEntities(func, bindingResolver.getRepo().generateId()); | |||||
bindingResolver.getRepo().add(m); | |||||
for (int i = 1; i< funcs.size(); i++) { | |||||
m.add(funcs.get(i)); | |||||
} | |||||
expr.setType(func.getType(), m, bindingResolver); | |||||
expr.setReferredEntity(m); | |||||
} | |||||
private void setReferredEntity(Entity referredEntity) { | |||||
this.referredEntity = referredEntity; | |||||
if (this.referredEntity!=null) { | |||||
this.referredEntityId = referredEntity.getId(); | |||||
} | |||||
} | |||||
public void addDeducedTypeVar(VarEntity var) { | |||||
this.deducedTypeVars.add(var); | |||||
this.deducedTypeVarsId.add(var.getId()); | |||||
} | |||||
public void addDeducedTypeFunction(FunctionEntity function) { | |||||
this.deducedTypeFunctions.add(function); | |||||
this.deducedTypeFunctionsId.add(function.id); | |||||
} | |||||
public void setParent(Expression parent) { | |||||
this.parent = parent; | |||||
if (parent!=null) | |||||
this.parentId = parent.id; | |||||
if (parent!=null) { | |||||
if (parent.deduceTypeBasedId==null) | |||||
parent.deduceTypeBasedId = id; | |||||
if (parent.isSet) { | |||||
parent.deduceTypeBasedId = id; | |||||
} | |||||
} | |||||
} | |||||
public GenericName getIdentifier() { | |||||
return this.identifier; | |||||
} | |||||
public GenericName getRawType() { | |||||
return this.rawType; | |||||
} | |||||
public void setIdentifier(String name) { | |||||
if (!validName(name)){ | |||||
return; | |||||
} | |||||
this.identifier = GenericName.build(name); | |||||
} | |||||
public void setIdentifier(GenericName name) { | |||||
if (name==null) return; | |||||
if (!validName(name.getName())){ | |||||
return; | |||||
} | |||||
this.identifier = name; | |||||
} | |||||
public void setRawType(GenericName name) { | |||||
if (name==null) return; | |||||
if (!validName(name.getName())){ | |||||
return; | |||||
} | |||||
this.rawType = name; | |||||
} | |||||
public void setRawType(String name) { | |||||
if (name==null) return; | |||||
if (!validName(name)){ | |||||
return; | |||||
} | |||||
this.rawType = GenericName.build(name); | |||||
} | |||||
public Expression getParent() { | |||||
return this.parent; | |||||
} | |||||
public void setText(String text) { | |||||
this.text = text; | |||||
} | |||||
public boolean isCall() { | |||||
return isCall; | |||||
} | |||||
public boolean isSet() { | |||||
return isSet; | |||||
} | |||||
public void setSet(boolean isSet) { | |||||
this.isSet = isSet; | |||||
} | |||||
public boolean isDot() { | |||||
return isDot; | |||||
} | |||||
public void setDot(boolean isDot) { | |||||
this.isDot = isDot; | |||||
} | |||||
public boolean isLogic() { | |||||
return isLogic; | |||||
} | |||||
public void setLogic(boolean isLogic) { | |||||
this.isLogic = isLogic; | |||||
} | |||||
public boolean isCreate() { | |||||
return isCreate; | |||||
} | |||||
public void setCreate(boolean isCreate) { | |||||
this.isCreate = isCreate; | |||||
} | |||||
public boolean isCast() { | |||||
return isCast; | |||||
} | |||||
public void setCast(boolean isCast) { | |||||
this.isCast = isCast; | |||||
} | |||||
public boolean isThrow() { | |||||
return isThrow; | |||||
} | |||||
public void setThrow(boolean isThrow) { | |||||
this.isThrow = isThrow; | |||||
} | |||||
public boolean isStatement() { | |||||
return isStatement; | |||||
} | |||||
public void setStatement(boolean isStatement) { | |||||
this.isStatement = isStatement; | |||||
} | |||||
public void setCall(boolean isCall) { | |||||
this.isCall = isCall; | |||||
} | |||||
public void disableDriveTypeFromChild() { | |||||
deriveTypeFromChild = false ; | |||||
} | |||||
public Entity getReferredEntity() { | |||||
return referredEntity; | |||||
} | |||||
public TypeEntity getType() { | |||||
return type; | |||||
} | |||||
private boolean validName(String name) { | |||||
if (name==null) return false; | |||||
if (name.toLowerCase().equals("<literal>")) return true; | |||||
if (name.toLowerCase().equals("<built-in>")) return true; | |||||
return true; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
StringBuilder s = new StringBuilder(); | |||||
s.append("[").append(text).append("]").append("|") | |||||
.append("rawType:").append(rawType).append("|") | |||||
.append("identifier:").append(identifier).append("|") | |||||
.append("prop:").append(isDot?"[dot]":"") | |||||
.append(isSet?"[set]":"") | |||||
.append(isLogic?"[bool]":"") | |||||
.append(isCall?"[call]":"") | |||||
.append(isCreate?"[new]":"") | |||||
.append(isThrow?"[throw]":"").append("|") | |||||
.append("parent:").append(parent==null?"nil":parent.text).append("|") | |||||
.append("type:").append(type).append("|"); | |||||
return s.toString(); | |||||
} | |||||
public void setLine(int lineNumber) { | |||||
this.location.setLine(lineNumber); | |||||
} | |||||
public Location getLocation() { | |||||
return location; | |||||
} | |||||
} |
@@ -0,0 +1,298 @@ | |||||
package depends.extractor.python.union; | |||||
import depends.entity.*; | |||||
import depends.entity.repo.IdGenerator; | |||||
import depends.extractor.HandlerContext; | |||||
import depends.extractor.python.PythonHandlerContext; | |||||
import depends.extractor.python.PythonParser.*; | |||||
import depends.extractor.python.PythonParserBaseVisitor; | |||||
import depends.relations.IBindingResolver; | |||||
import org.antlr.v4.runtime.ParserRuleContext; | |||||
import org.antlr.v4.runtime.RuleContext; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.regex.Matcher; | |||||
import java.util.regex.Pattern; | |||||
public class ExpressionUsage { | |||||
HandlerContext context; | |||||
IdGenerator idGenerator; | |||||
private boolean exprStarted=false; | |||||
private IBindingResolver bindingResolver; | |||||
public ExpressionUsage(PythonHandlerContext context, IdGenerator idGenerator, IBindingResolver bindingResolver) { | |||||
this.context = context; | |||||
this.idGenerator = idGenerator; | |||||
this.bindingResolver = bindingResolver; | |||||
} | |||||
/** | |||||
* Auto deduce variable type from assignment. for example: c = new C() then c is | |||||
* type of C | |||||
* | |||||
*/ | |||||
private void deduceVarTypeInCaseOfAssignment(Expr_stmtContext expr, Expression expression) { | |||||
List<String> names = getName(expr.testlist_star_expr()); | |||||
// TODO: should handle list properly; | |||||
String varName = null; | |||||
if (names.size() == 1) | |||||
varName = names.get(0); | |||||
if (varName == null) | |||||
return; | |||||
VarEntity var = context.lastContainer().lookupVarLocally(varName); | |||||
if (var != null) { | |||||
expression.addDeducedTypeVar(var); | |||||
} | |||||
} | |||||
private List<String> getName(Testlist_star_exprContext testlist_star_expr) { | |||||
List<String> names = new ArrayList<>(); | |||||
testlist_star_expr.accept(new NameCollector(names)); | |||||
return names; | |||||
} | |||||
public void foundExpression(ParserRuleContext ctx) { | |||||
if (!isStartOfContainerRule(ctx)) { | |||||
return ; | |||||
} | |||||
if (context.lastContainer().containsExpression(ctx)) return; | |||||
if (ctx.getParent() instanceof TrailerContext) return; | |||||
Expression parent = findParentInStack(ctx); | |||||
Expression expression = parent; | |||||
if (ctx.getParent().getChildCount()==1 && parent!=null) { | |||||
//如果就是自己,则无需创建新的Expression | |||||
}else { | |||||
/* create expression and link it with parent*/ | |||||
expression = new Expression(idGenerator.generateId()); | |||||
expression.setLine(ctx.getStart().getLine()); | |||||
expression.setText(ctx.getText()); | |||||
context.lastContainer().addExpression(ctx,expression); | |||||
expression.setParent(parent); | |||||
} | |||||
if (ctx instanceof Expr_stmtContext) { | |||||
Expr_stmtContext exprAssign = (Expr_stmtContext)ctx; | |||||
if (exprAssign.assign_part()!=null) { | |||||
expression.setSet(true); | |||||
expression.setIdentifier(exprAssign.testlist_star_expr().getText()); | |||||
if (isValidIdentifier(expression.getIdentifier())) { | |||||
makeSureVarExist(expression.getIdentifier(), ctx); | |||||
} | |||||
deduceVarTypeInCaseOfAssignment((Expr_stmtContext)ctx,expression); | |||||
} | |||||
} | |||||
if (ctx instanceof Raise_stmtContext) { | |||||
expression.setThrow (true); | |||||
} | |||||
if (ctx instanceof Return_stmtContext) { | |||||
deduceReturnTypeInCaseOfReturn((Return_stmtContext)ctx,expression); | |||||
} | |||||
if (ctx instanceof ExprContext) { | |||||
processExprContext((ExprContext)ctx, expression); | |||||
} | |||||
} | |||||
private void deduceReturnTypeInCaseOfReturn(Return_stmtContext ctx, Expression expression) { | |||||
FunctionEntity currentFunction = context.currentFunction(); | |||||
if (currentFunction == null) | |||||
return; | |||||
expression.addDeducedTypeFunction(currentFunction); | |||||
} | |||||
private void makeSureVarExist(GenericName identifier, ParserRuleContext ctx) { | |||||
if (null==context.foundEntityWithName(identifier)) { | |||||
VarEntity var = context.foundVarDefinition(context.lastContainer(), identifier.getName(),ctx.getStart().getLine()); | |||||
var.setLine(ctx.getStart().getLine()); | |||||
} | |||||
} | |||||
private boolean isValidIdentifier(GenericName identifier) { | |||||
Pattern p = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*"); | |||||
Matcher m = p.matcher(identifier.getName()); | |||||
return m.matches(); | |||||
} | |||||
private void processExprContext(ExprContext exprCtx, Expression expression) { | |||||
//func_call, member_access, subscript member, and atom | |||||
Expression lastExpression = null; | |||||
if (exprCtx.atom()!=null) { | |||||
//atom | |||||
Expression atomExpr = new Expression(idGenerator.generateId()); | |||||
atomExpr.setLine(exprCtx.atom().getStart().getLine()); | |||||
atomExpr.setParent(expression); | |||||
atomExpr.setText(exprCtx.atom().getText()); | |||||
atomExpr.setIdentifier(exprCtx.atom().getText()); | |||||
context.lastContainer().addExpression(exprCtx.atom(),atomExpr); | |||||
processAtom(exprCtx.atom(),atomExpr); | |||||
lastExpression = atomExpr; | |||||
if (exprCtx.trailer()==null || exprCtx.trailer().size()==0) { | |||||
//do nothing; it is just an id; | |||||
}else { | |||||
for (TrailerContext trailer:exprCtx.trailer()) { | |||||
if (trailer.name()!=null) { | |||||
Expression trailerExpr = new Expression(idGenerator.generateId()); | |||||
trailerExpr.setLine(trailer.getStart().getLine()); | |||||
trailerExpr.setText(trailer.getText()); | |||||
context.lastContainer().addExpression(trailer,trailerExpr); | |||||
trailerExpr.setParent(expression); | |||||
//doted name = member access or method call | |||||
trailerExpr.setDot(true);; | |||||
trailerExpr.setIdentifier(trailer.name().getText()); | |||||
if (trailer.arguments()!=null) { | |||||
if (trailer.arguments().OPEN_PAREN()!=null) { | |||||
foundCallStyleExpressionWithDot(trailerExpr,lastExpression.getIdentifier(), trailer); | |||||
}else { | |||||
//subscript list, do nothing | |||||
} | |||||
} | |||||
lastExpression.setParent(trailerExpr); | |||||
lastExpression = trailerExpr; | |||||
}else { | |||||
//direct call, or direct data access | |||||
if (trailer.arguments()!=null) { | |||||
if (trailer.arguments().OPEN_PAREN()!=null) { | |||||
foundCallStyleExpressionWithoutDot(lastExpression, trailer.arguments()); | |||||
}else { | |||||
//subscript list, do nothing | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}else { | |||||
/** expr | |||||
| <assoc=right> expr op=POWER expr | |||||
| op=(ADD | MINUS | NOT_OP) expr | |||||
| expr op=(STAR | DIV | MOD | IDIV | AT) expr | |||||
| expr op=(ADD | MINUS) expr | |||||
| expr op=(LEFT_SHIFT | RIGHT_SHIFT) expr | |||||
| expr op=AND_OP expr | |||||
| expr op=XOR expr | |||||
| expr op=OR_OP expr | |||||
;*/ | |||||
} | |||||
} | |||||
private boolean isStartOfContainerRule(ParserRuleContext ctx) { | |||||
if (this.exprStarted) return true; | |||||
return ctx instanceof ExprContext || | |||||
ctx instanceof Expr_stmtContext || | |||||
ctx instanceof Del_stmtContext || | |||||
ctx instanceof Return_stmtContext || | |||||
ctx instanceof Raise_stmtContext || | |||||
ctx instanceof Raise_stmtContext || | |||||
ctx instanceof Yield_stmtContext || | |||||
ctx instanceof Assert_stmtContext; | |||||
} | |||||
private void foundCallStyleExpressionWithDot(Expression theExpression, GenericName varName, ParserRuleContext ctx) { | |||||
GenericName funcName = theExpression.getIdentifier(); | |||||
Entity prefixEntity = context.foundEntityWithName(varName); | |||||
if (prefixEntity instanceof VarEntity) { | |||||
((VarEntity) prefixEntity).addFunctionCall(funcName); | |||||
} | |||||
Entity typeEntity = context.foundEntityWithName(funcName); | |||||
if (typeEntity instanceof TypeEntity && typeEntity.getId() > 0) { | |||||
theExpression.setCreate(true); | |||||
theExpression.setType(typeEntity.getType(), typeEntity, bindingResolver); | |||||
theExpression.setRawType(typeEntity.getRawName()); | |||||
return; | |||||
} | |||||
theExpression.setCall(true); | |||||
} | |||||
private void foundCallStyleExpressionWithoutDot(Expression theExpression, ParserRuleContext ctx) { | |||||
GenericName funcName = theExpression.getIdentifier(); | |||||
Entity typeEntity = context.foundEntityWithName(funcName); | |||||
if (typeEntity instanceof TypeEntity && typeEntity.getId() > 0) { | |||||
theExpression.getParent().setCreate(true); | |||||
theExpression.setType(typeEntity.getType(), typeEntity, bindingResolver); | |||||
theExpression.getParent().setRawType(typeEntity.getRawName()); | |||||
return; | |||||
} | |||||
theExpression.setCall(true); | |||||
} | |||||
private void processAtom(AtomContext atom, Expression expression) { | |||||
if (atom.name()!=null) { | |||||
expression.setIdentifier(atom.getText()); | |||||
return; | |||||
} | |||||
if (atom.STRING()!=null | |||||
|| atom.NONE()!=null | |||||
|| atom.number()!=null) { | |||||
expression.setRawType("<Built-in>"); | |||||
expression.setIdentifier("<Literal>"); | |||||
return; | |||||
} | |||||
if (atom.EXEC()!=null | |||||
|| atom.PRINT()!=null | |||||
|| atom.ELLIPSIS()!=null) { | |||||
return; | |||||
} | |||||
// : OPEN_PAREN (yield_expr | testlist_comp)? CLOSE_PAREN | |||||
// | OPEN_BRACKET testlist_comp? CLOSE_BRACKET | |||||
// | OPEN_BRACE dictorsetmaker? CLOSE_BRACE | |||||
// | REVERSE_QUOTE testlist COMMA? REVERSE_QUOTE | |||||
return; | |||||
} | |||||
private Expression findParentInStack(RuleContext ctx) { | |||||
if (ctx==null) return null; | |||||
if (ctx.parent==null) return null; | |||||
if (context.lastContainer()==null) { | |||||
return null; | |||||
} | |||||
if (context.lastContainer().expressions().containsKey(ctx.parent)) | |||||
return context.lastContainer().expressions().get(ctx.parent); | |||||
return findParentInStack(ctx.parent); | |||||
} | |||||
public void startExpr() { | |||||
this.exprStarted = true; | |||||
} | |||||
public void stopExpr() { | |||||
this.exprStarted = false; | |||||
} | |||||
} | |||||
class NameCollector extends PythonParserBaseVisitor<Void>{ | |||||
private List<String> names; | |||||
NameCollector(List<String> names){ | |||||
this.names = names; | |||||
} | |||||
@Override | |||||
public Void visitAtom(AtomContext ctx) { | |||||
if (ctx.name()!=null) | |||||
names.add(ctx.name().getText()); | |||||
return super.visitAtom(ctx); | |||||
} | |||||
} |
@@ -0,0 +1,129 @@ | |||||
import java.io.FileInputStream; | |||||
import java.io.FileNotFoundException; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.lang.StringBuffer; | |||||
// https://svn.apache.org/repos/asf/poi/trunk/src/ooxml/testcases/org/apache/poi/extractor/TestExtractorFactory.java | |||||
import org.apache.poi.POIOLE2TextExtractor; | |||||
import org.apache.poi.POITextExtractor; | |||||
//import org.apache.poi.POIDataSamples; | |||||
//import org.apache.poi.extractor.*; | |||||
import org.apache.poi.extractor.ExtractorFactory; | |||||
import org.apache.poi.hdgf.extractor.VisioTextExtractor; | |||||
import org.apache.poi.hpbf.extractor.PublisherTextExtractor; | |||||
import org.apache.poi.hslf.extractor.PowerPointExtractor; | |||||
import org.apache.poi.hsmf.extractor.OutlookTextExtactor; | |||||
import org.apache.poi.hssf.extractor.EventBasedExcelExtractor; | |||||
import org.apache.poi.hssf.extractor.ExcelExtractor; | |||||
import org.apache.poi.hwpf.extractor.Word6Extractor; | |||||
import org.apache.poi.hwpf.extractor.WordExtractor; | |||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||||
import org.apache.poi.xslf.extractor.XSLFPowerPointExtractor; | |||||
import org.apache.poi.xssf.extractor.XSSFEventBasedExcelExtractor; | |||||
import org.apache.poi.xssf.extractor.XSSFExcelExtractor; | |||||
import org.apache.poi.xwpf.extractor.XWPFWordExtractor; | |||||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; | |||||
import org.apache.poi.poifs.filesystem.OfficeXmlFileException; | |||||
import org.apache.poi.xslf.usermodel.XMLSlideShow; // pptx 2007, http://poi.apache.org/apidocs/org/apache/poi/xslf/ | |||||
import org.apache.poi.xwpf.usermodel.XWPFDocument; // docx 2007, http://poi.apache.org/apidocs/org/apache/poi/xwpf/ | |||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook; // xlsx 2007, http://poi.apache.org/apidocs/org/apache/poi/xssf/ | |||||
class ExtractText | |||||
{ | |||||
public static String file(String path) { | |||||
try { return pptx(new FileInputStream(path)); } catch(Exception e) { } | |||||
try { return docx(new FileInputStream(path)); } catch(Exception e) { } | |||||
try { return xlsx(new FileInputStream(path)); } catch(Exception e) { } | |||||
return ""; | |||||
} | |||||
public static String pptx(InputStream in) throws Exception { | |||||
XSLFPowerPointExtractor o = new XSLFPowerPointExtractor( new XMLSlideShow(in) ); | |||||
o.setSlidesByDefault(true); | |||||
o.setNotesByDefault(true); | |||||
return o.getText(); | |||||
} | |||||
public static String docx(InputStream in) throws Exception { | |||||
XWPFWordExtractor o = new XWPFWordExtractor(new XWPFDocument(in)); | |||||
return o.getText(); | |||||
} | |||||
public static String xlsx(InputStream in) throws Exception { | |||||
XSSFExcelExtractor o = new XSSFExcelExtractor(new XSSFWorkbook(in)); | |||||
return o.getText(); | |||||
} | |||||
public static void main(String argv[]) { | |||||
try { | |||||
InputStream in = null; | |||||
if (argv.length < 1) | |||||
in = System.in; | |||||
else | |||||
in = new FileInputStream(argv[0]); | |||||
StringBuffer output = new StringBuffer(); | |||||
POITextExtractor textExtractor = ExtractorFactory.createExtractor(in); | |||||
if (textExtractor instanceof ExcelExtractor) // xls, excel 97-2003 | |||||
{ | |||||
ExcelExtractor extractor = (ExcelExtractor) textExtractor; | |||||
output.append(extractor.getText()); | |||||
} | |||||
else if (textExtractor instanceof XSSFExcelExtractor) // xlsx, excel 2007 | |||||
{ | |||||
XSSFExcelExtractor extractor = (XSSFExcelExtractor) textExtractor; | |||||
output.append(extractor.getText()); | |||||
} | |||||
else if (textExtractor instanceof Word6Extractor) // doc, word 95 | |||||
{ | |||||
Word6Extractor extractor = (Word6Extractor) textExtractor; | |||||
output.append(extractor.getText()); | |||||
} | |||||
else if (textExtractor instanceof WordExtractor) // doc, word 97-2003 | |||||
{ | |||||
WordExtractor extractor = (WordExtractor) textExtractor; | |||||
output.append(extractor.getText()); | |||||
} | |||||
else if (textExtractor instanceof XWPFWordExtractor) // docx, word 2007 | |||||
{ | |||||
XWPFWordExtractor extractor = (XWPFWordExtractor) textExtractor; | |||||
output.append(extractor.getText()); | |||||
} | |||||
else if (textExtractor instanceof PowerPointExtractor) // ppt, ppt 97-2003 | |||||
{ | |||||
PowerPointExtractor extractor = (PowerPointExtractor) textExtractor; | |||||
output.append(extractor.getText()); | |||||
output.append(extractor.getNotes()); | |||||
} | |||||
else if (textExtractor instanceof XSLFPowerPointExtractor ) // pptx, powerpoint 2007 | |||||
{ | |||||
XSLFPowerPointExtractor extractor = (XSLFPowerPointExtractor) textExtractor; | |||||
extractor.setSlidesByDefault(true); | |||||
extractor.setNotesByDefault(true); | |||||
output.append(extractor.getText()); | |||||
} | |||||
else if (textExtractor instanceof VisioTextExtractor) // vsd, visio | |||||
{ | |||||
VisioTextExtractor extractor = (VisioTextExtractor) textExtractor; | |||||
output.append(extractor.getText()); | |||||
} | |||||
else if (textExtractor instanceof PublisherTextExtractor) // pub, publisher | |||||
{ | |||||
PublisherTextExtractor extractor = (PublisherTextExtractor) textExtractor; | |||||
output.append(extractor.getText()); | |||||
} | |||||
else if (textExtractor instanceof OutlookTextExtactor) // msg, outlook | |||||
{ | |||||
OutlookTextExtactor extractor = (OutlookTextExtactor) textExtractor; | |||||
output.append(extractor.getText()); | |||||
} | |||||
System.out.println(output.toString().replaceAll( "[\n\t\r ]+"," ")); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
// TODO Auto-generated catch block | |||||
//e.printStackTrace(); | |||||
//System.out.println(e); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,169 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.entity; | |||||
import depends.importtypes.Import; | |||||
import depends.relations.IBindingResolver; | |||||
import java.util.*; | |||||
public class FileEntity extends TypeEntity { | |||||
private List<Import> importedNames = new ArrayList<>(); | |||||
private boolean isInProjectScope = false; | |||||
private Collection<Entity> importedRelationEntities = new ArrayList<>(); | |||||
private Collection<Entity> importedFiles = new ArrayList<>(); | |||||
private Collection<Entity> importedTypes = new ArrayList<>(); | |||||
private List<TypeEntity> declaredTypes = new ArrayList<>(); | |||||
private ImportedFileCollector importedFileCollector = null; | |||||
public FileEntity() {} | |||||
public FileEntity(String fullName, int fileId, boolean isInProjectScope) { | |||||
super(GenericName.build(fullName), null, fileId); | |||||
setQualifiedName(fullName); | |||||
this.isInProjectScope = isInProjectScope; | |||||
} | |||||
public FileEntity(String fullName, int fileId) { | |||||
this(fullName, fileId, true); | |||||
} | |||||
public void addImport(Import imported) { | |||||
if (!importedNames.contains(imported)) | |||||
importedNames.add(imported); | |||||
} | |||||
/** | |||||
* To match the imported name by suffix | |||||
* for example: | |||||
* import a.b.ClassX; | |||||
* the b.ClassX, ClassX , a.b.classX should be matched | |||||
* @param lastName | |||||
* @return | |||||
*/ | |||||
public String importedSuffixMatch(String lastName) { | |||||
if (!lastName.startsWith(".")) | |||||
lastName = "." + lastName; | |||||
for (Entity imported : this.importedTypes) { | |||||
String name = imported.getQualifiedName(true); | |||||
if (!name.startsWith(".")) | |||||
name = "." + name; | |||||
if (imported.getQualifiedName(true).endsWith(lastName)) | |||||
return imported.getQualifiedName(true); | |||||
} | |||||
return null; | |||||
} | |||||
@Override | |||||
public String getQualifiedName(boolean overrideFileWithPackage) { | |||||
if (!overrideFileWithPackage) { | |||||
return super.getQualifiedName(); | |||||
} | |||||
if (this.getParent() == null) { | |||||
return ""; | |||||
} | |||||
if (this.getParent() instanceof PackageEntity) | |||||
return this.getParent().getQualifiedName(); | |||||
else | |||||
return super.getQualifiedName(); | |||||
} | |||||
@Override | |||||
public void inferLocalLevelEntities(IBindingResolver bindingResolver) { | |||||
this.importedRelationEntities = bindingResolver.getImportedRelationEntities(importedNames); | |||||
this.importedTypes = bindingResolver.getImportedTypes(importedNames,this); | |||||
this.importedFiles = bindingResolver.getImportedFiles(importedNames); | |||||
super.inferLocalLevelEntities(bindingResolver); | |||||
} | |||||
public boolean isInProjectScope() { | |||||
return isInProjectScope; | |||||
} | |||||
public void setInProjectScope(boolean isInProjectScope) { | |||||
this.isInProjectScope = isInProjectScope; | |||||
} | |||||
public Collection<Entity> getImportedRelationEntities() { | |||||
return importedRelationEntities; | |||||
} | |||||
public Collection<Entity> getImportedFiles() { | |||||
return importedFiles; | |||||
} | |||||
public Collection<Entity> getImportedTypes() { | |||||
return importedTypes; | |||||
} | |||||
public List<TypeEntity> getDeclaredTypes() { | |||||
return this.declaredTypes; | |||||
} | |||||
public void addType(TypeEntity currentTypeEntity) { | |||||
this.declaredTypes.add(currentTypeEntity); | |||||
} | |||||
public Set<FileEntity> getImportedFilesInAllLevel() { | |||||
if (importedFileCollector==null) | |||||
importedFileCollector = new ImportedFileCollector(this); | |||||
return importedFileCollector.getFiles(); | |||||
} | |||||
public List<Import> getImportedNames() { | |||||
return importedNames; | |||||
} | |||||
public void cacheAllExpressions() { | |||||
this.cacheChildExpressions(); | |||||
} | |||||
@Override | |||||
public Entity getByName(String name, HashSet<Entity> searched) { | |||||
Entity entity = super.getByName(name, searched); | |||||
if (entity!=null) return entity; | |||||
for (TypeEntity type:getDeclaredTypes()) { | |||||
if (type.getRawName().getName().equals(name)|| | |||||
suffixMatch(name,type.getQualifiedName())) { | |||||
return type; | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
private boolean suffixMatch(String name, String qualifiedName) { | |||||
if (qualifiedName.contains(".")) { | |||||
if (!name.startsWith(".")) name = "." +name; | |||||
return qualifiedName.endsWith(name); | |||||
} | |||||
else { | |||||
return qualifiedName.equals(name); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,99 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor.java.context; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import org.antlr.v4.runtime.tree.TerminalNode; | |||||
import depends.entity.FunctionEntity; | |||||
import depends.entity.GenericName; | |||||
import depends.entity.VarEntity; | |||||
import depends.entity.repo.IdGenerator; | |||||
import depends.extractor.java.JavaParser.FormalParameterContext; | |||||
import depends.extractor.java.JavaParser.FormalParameterListContext; | |||||
import depends.extractor.java.JavaParser.FormalParametersContext; | |||||
import depends.extractor.java.JavaParser.LastFormalParameterContext; | |||||
import depends.extractor.java.JavaParser.TypeTypeContext; | |||||
import depends.extractor.java.JavaParser.VariableModifierContext; | |||||
public class FormalParameterListContextHelper { | |||||
FormalParameterListContext context; | |||||
private IdGenerator idGenerator; | |||||
private List<String> annotations; | |||||
private FunctionEntity container; | |||||
public FormalParameterListContextHelper(FormalParameterListContext formalParameterListContext,FunctionEntity container, IdGenerator idGenerator) { | |||||
this.context = formalParameterListContext; | |||||
this.container = container; | |||||
annotations = new ArrayList<>(); | |||||
this.idGenerator = idGenerator; | |||||
if (context!=null) | |||||
extractParameterTypeList(); | |||||
} | |||||
public FormalParameterListContextHelper(FormalParametersContext formalParameters,FunctionEntity container, IdGenerator idGenerator) { | |||||
this(formalParameters.formalParameterList(),container,idGenerator); | |||||
} | |||||
public void extractParameterTypeList() { | |||||
if (context != null) { | |||||
if (context.formalParameter() != null) { | |||||
for (FormalParameterContext p : context.formalParameter()) { | |||||
foundParameterDefintion(p.typeType(),p.variableDeclaratorId().IDENTIFIER(),p.variableModifier()); | |||||
} | |||||
if (context.lastFormalParameter()!=null) { | |||||
LastFormalParameterContext p = context.lastFormalParameter(); | |||||
foundParameterDefintion(p.typeType(),p.variableDeclaratorId().IDENTIFIER(),p.variableModifier()); | |||||
} | |||||
} | |||||
} | |||||
return; | |||||
} | |||||
private void foundParameterDefintion(TypeTypeContext typeType, TerminalNode identifier, List<VariableModifierContext> variableModifier) { | |||||
GenericName type = GenericName.build(ClassTypeContextHelper.getClassName(typeType)); | |||||
GenericName varName = GenericName.build(identifier.getText()); | |||||
VarEntity varEntity = new VarEntity(varName,type,container,idGenerator.generateId()); | |||||
container.addParameter(varEntity); | |||||
for ( VariableModifierContext modifier:variableModifier) { | |||||
if (modifier.annotation()!=null) { | |||||
this.annotations.add(QualitiedNameContextHelper.getName(modifier.annotation().qualifiedName())); | |||||
} | |||||
} | |||||
} | |||||
public List<String> getAnnotations() { | |||||
return annotations; | |||||
} | |||||
} |
@@ -0,0 +1,149 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.entity; | |||||
import depends.relations.IBindingResolver; | |||||
import java.util.ArrayList; | |||||
import java.util.Collection; | |||||
import java.util.List; | |||||
public class FunctionEntity extends ContainerEntity{ | |||||
private List<GenericName> returnTypeIdentifiers = new ArrayList<>(); | |||||
Collection<VarEntity> parameters; | |||||
Collection<GenericName> throwTypesIdentifiers = new ArrayList<>(); | |||||
private Collection<Entity> returnTypes = new ArrayList<>(); | |||||
private Collection<Entity> throwTypes = new ArrayList<>(); | |||||
public FunctionEntity() { | |||||
this.parameters = new ArrayList<>(); | |||||
} | |||||
public FunctionEntity(GenericName simpleName, Entity parent, Integer id, GenericName returnType) { | |||||
super(simpleName, parent,id); | |||||
this.returnTypes = new ArrayList<>(); | |||||
returnTypeIdentifiers = new ArrayList<>(); | |||||
this.parameters = new ArrayList<>(); | |||||
throwTypesIdentifiers = new ArrayList<>(); | |||||
addReturnType(returnType); | |||||
} | |||||
public Collection<Entity> getReturnTypes() { | |||||
return returnTypes; | |||||
} | |||||
@Override | |||||
public TypeEntity getType() { | |||||
if (returnTypes.size()>0){ | |||||
Object type = returnTypes.iterator().next(); | |||||
if (type instanceof TypeEntity) | |||||
return (TypeEntity)type; | |||||
} | |||||
return null; | |||||
} | |||||
public void addReturnType(GenericName returnType) { | |||||
if (returnType==null) return; | |||||
this.returnTypeIdentifiers.add(returnType); | |||||
} | |||||
public void addReturnType(TypeEntity returnType) { | |||||
if (returnType==null) return; | |||||
if (!this.returnTypeIdentifiers.contains(returnType.rawName)){ | |||||
this.returnTypeIdentifiers.add(returnType.rawName); | |||||
this.returnTypes.add(returnType); | |||||
} | |||||
} | |||||
public void addThrowTypes(List<GenericName> throwedType) { | |||||
throwTypesIdentifiers.addAll(throwedType); | |||||
} | |||||
@Override | |||||
public void inferLocalLevelEntities(IBindingResolver bindingResolver) { | |||||
for (VarEntity param:parameters) { | |||||
param.fillCandidateTypes(bindingResolver); | |||||
param.inferLocalLevelEntities(bindingResolver); | |||||
} | |||||
if (returnTypes.size()<returnTypeIdentifiers.size()) { | |||||
returnTypes = identiferToEntities(bindingResolver,this.returnTypeIdentifiers); | |||||
for ( GenericName returnTypeName: returnTypeIdentifiers) { | |||||
Collection<Entity> typeEntities = typeParametersToEntities(bindingResolver, returnTypeName); | |||||
this.appendTypeParameters(typeEntities); | |||||
} | |||||
} | |||||
if (throwTypes.size()<throwTypesIdentifiers.size()) | |||||
throwTypes = identiferToEntities(bindingResolver,this.throwTypesIdentifiers); | |||||
super.inferLocalLevelEntities(bindingResolver); | |||||
} | |||||
private Collection<Entity> typeParametersToEntities(IBindingResolver bindingResolver, GenericName name) { | |||||
ArrayList<Entity> r = new ArrayList<>(); | |||||
for (GenericName typeParameter:name.getArguments()) { | |||||
toEntityList(bindingResolver, r,typeParameter); | |||||
} | |||||
return r; | |||||
} | |||||
public Collection<VarEntity> getParameters() { | |||||
return parameters; | |||||
} | |||||
public Collection<Entity> getThrowTypes() { | |||||
return throwTypes; | |||||
} | |||||
@Override | |||||
public Entity lookupVarInVisibleScope(GenericName varName) { | |||||
for (VarEntity param:parameters) { | |||||
if (varName.equals(param.getRawName())) { | |||||
return param; | |||||
} | |||||
} | |||||
return super.lookupVarInVisibleScope(varName); | |||||
} | |||||
public void addParameter(VarEntity var) { | |||||
this.parameters.add(var); | |||||
} | |||||
@Override | |||||
public String getDisplayName() { | |||||
FileEntity f = (FileEntity) this.getAncestorOfType(FileEntity.class); | |||||
return f.getRawName()+"("+this.getQualifiedName()+")"; | |||||
} | |||||
@Override | |||||
public VarEntity lookupVarLocally(GenericName varName) { | |||||
for (VarEntity var:this.parameters) { | |||||
if (var.getRawName().equals(varName)) | |||||
return var; | |||||
} | |||||
return super.lookupVarLocally(varName); | |||||
} | |||||
public void linkReturnToLastExpression() { | |||||
if (expressionList()==null) return; | |||||
for (int i = expressionList().size() - 1; i >= 0; i--) { | |||||
Expression expr = expressionList().get(i); | |||||
if (expr.isStatement()) | |||||
expr.addDeducedTypeFunction(this); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,117 @@ | |||||
package depends.entity; | |||||
import java.io.Serializable; | |||||
import java.util.ArrayList; | |||||
import java.util.Arrays; | |||||
import java.util.List; | |||||
public class GenericName implements Serializable{ | |||||
private static final long serialVersionUID = 1L; | |||||
private char[] name; | |||||
List<GenericName> arguments; | |||||
public GenericName(String name) { | |||||
this.name = name.toCharArray(); | |||||
} | |||||
public GenericName(String name, List<GenericName> arguments) { | |||||
this.name = name.toCharArray(); | |||||
this.arguments = arguments; | |||||
} | |||||
public boolean contains(String rawType) { | |||||
if (new String(name).contains(rawType)) return true; | |||||
return false; | |||||
} | |||||
public String getName() { | |||||
return new String(name); | |||||
} | |||||
public List<GenericName> getArguments() { | |||||
if (arguments==null) return new ArrayList<>(); | |||||
return arguments; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return new String(name) + (getArguments().size()>0?"(" + arguments + ")":""); | |||||
} | |||||
public GenericName replace(String from, String to) { | |||||
name = new String(name).replace(from, to).toCharArray(); | |||||
for (GenericName arg:getArguments()) { | |||||
arg.replace(from, to); | |||||
} | |||||
return this; | |||||
} | |||||
public boolean startsWith(String prefix) { | |||||
if (name==null) return false; | |||||
return new String(name).startsWith(prefix); | |||||
} | |||||
public String uniqName() { | |||||
if (getArguments().size()==0) return new String(name); | |||||
StringBuffer sb = new StringBuffer(); | |||||
sb.append(name); | |||||
if (getArguments().size()>0) { | |||||
for (GenericName arg:getArguments()) { | |||||
sb.append("__").append(arg.uniqName()).append("__"); | |||||
} | |||||
} | |||||
return sb.toString(); | |||||
} | |||||
public GenericName substring(int start) { | |||||
return new GenericName(new String(this.name).substring(start)); | |||||
} | |||||
public boolean isNull() { | |||||
return name==null; | |||||
} | |||||
public static GenericName build(String name) { | |||||
if (name==null) return null; | |||||
return new GenericName(name); | |||||
} | |||||
public static GenericName build(String name, List<GenericName> arguments) { | |||||
return new GenericName(name,arguments); | |||||
} | |||||
public boolean find(GenericName rawType) { | |||||
//if (this.equals(rawType)) return true; | |||||
for (GenericName subType:this.getArguments()) { | |||||
if (subType.equals(rawType)) return true; | |||||
boolean found = subType.find(rawType); | |||||
if (found) return true; | |||||
} | |||||
return false; | |||||
} | |||||
public void appendArguments(List<GenericName> parameters) { | |||||
if (this.arguments==null) this.arguments = new ArrayList<>(); | |||||
this.arguments.addAll(parameters); | |||||
} | |||||
public void appendArguments(GenericName parameter) { | |||||
if (this.arguments==null) this.arguments = new ArrayList<>(); | |||||
this.arguments.add(parameter); | |||||
} | |||||
@Override | |||||
public int hashCode() { | |||||
final int prime = 31; | |||||
int result = 1; | |||||
result = prime * result + ((arguments == null) ? 0 : arguments.hashCode()); | |||||
result = prime * result + Arrays.hashCode(name); | |||||
return result; | |||||
} | |||||
@Override | |||||
public boolean equals(Object obj) { | |||||
if (this == obj) | |||||
return true; | |||||
if (obj == null) | |||||
return false; | |||||
if (getClass() != obj.getClass()) | |||||
return false; | |||||
GenericName other = (GenericName) obj; | |||||
if (this.getArguments() == null) { | |||||
if (other.getArguments() != null) | |||||
return false; | |||||
} else if (!getArguments().equals(other.getArguments())) | |||||
return false; | |||||
if (!Arrays.equals(name, other.name)) | |||||
return false; | |||||
return true; | |||||
} | |||||
} |
@@ -0,0 +1,103 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor.golang; | |||||
import depends.entity.Entity; | |||||
import depends.entity.FileEntity; | |||||
import depends.entity.PackageEntity; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.extractor.UnsolvedBindings; | |||||
import depends.importtypes.Import; | |||||
import depends.relations.ImportLookupStrategy; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.Set; | |||||
public class GoImportLookupStrategy extends ImportLookupStrategy{ | |||||
public GoImportLookupStrategy(EntityRepo repo) { | |||||
super(repo); | |||||
} | |||||
public Entity lookupImportedType(String name, FileEntity fileEntity) { | |||||
//Java Strategy | |||||
String importedString = fileEntity.importedSuffixMatch(name); | |||||
if (importedString==null) return null; | |||||
return repo.getEntity(importedString); | |||||
} | |||||
@Override | |||||
public List<Entity> getImportedRelationEntities(List<Import> importedList) { | |||||
ArrayList<Entity> result = new ArrayList<>(); | |||||
for (Import importedItem:importedList) { | |||||
Entity imported = repo.getEntity(importedItem.getContent()); | |||||
if (imported==null) continue; | |||||
if (imported instanceof PackageEntity) { | |||||
//ignore wildcard import relation | |||||
}else { | |||||
result.add(imported); | |||||
} | |||||
} | |||||
return result; | |||||
} | |||||
@Override | |||||
public List<Entity> getImportedTypes(List<Import> importedList, Set<UnsolvedBindings> unsolvedBindings) { | |||||
ArrayList<Entity> result = new ArrayList<>(); | |||||
for (Import importedItem:importedList) { | |||||
Entity imported = repo.getEntity(importedItem.getContent()); | |||||
if (imported==null) { | |||||
unsolvedBindings.add(new UnsolvedBindings(importedItem.getContent(),null)); | |||||
continue; | |||||
} | |||||
if (imported instanceof PackageEntity) { | |||||
//expand import of package to all classes under the package due to we dis-courage the behavior | |||||
for (Entity child:imported.getChildren()) { | |||||
if (child instanceof FileEntity) { | |||||
child.getChildren().forEach(item->result.add(item)); | |||||
}else { | |||||
result.add(child); | |||||
} | |||||
} | |||||
}else { | |||||
result.add(imported); | |||||
} | |||||
} | |||||
return result; | |||||
} | |||||
@Override | |||||
public List<Entity> getImportedFiles(List<Import> importedList) { | |||||
return new ArrayList<Entity>(); | |||||
} | |||||
@Override | |||||
public boolean supportGlobalNameLookup() { | |||||
return true; | |||||
} | |||||
} |
@@ -0,0 +1,136 @@ | |||||
package depends.extractor.golang; | |||||
import depends.entity.FunctionEntity; | |||||
import depends.entity.GenericName; | |||||
import depends.entity.VarEntity; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.relations.IBindingResolver; | |||||
import org.antlr.v4.runtime.tree.TerminalNode; | |||||
import java.util.ArrayList; | |||||
public class GoListener extends GoParserBaseListener { | |||||
private final EntityRepo entityRepo; | |||||
GoHandlerContext context; | |||||
@Override | |||||
public void enterSourceFile(GoParser.SourceFileContext ctx) { | |||||
super.enterSourceFile(ctx); | |||||
} | |||||
public GoListener(String fileFullPath, EntityRepo entityRepo, IBindingResolver bindingResolver) { | |||||
context = new GoHandlerContext(entityRepo, bindingResolver); | |||||
context.startFile(fileFullPath); | |||||
this.entityRepo = entityRepo; | |||||
} | |||||
@Override | |||||
public void enterFunctionDecl(GoParser.FunctionDeclContext ctx) { | |||||
String funcName = ctx.IDENTIFIER().getText(); | |||||
context.foundMethodDeclarator(funcName,ctx.getStart().getLine()); | |||||
foundFuncSignature(ctx.signature()); | |||||
super.enterFunctionDecl(ctx); | |||||
} | |||||
@Override | |||||
public void exitFunctionDecl(GoParser.FunctionDeclContext ctx) { | |||||
context.exitLastedEntity(); | |||||
super.exitFunctionDecl(ctx); | |||||
} | |||||
@Override | |||||
public void enterPackageClause(GoParser.PackageClauseContext ctx) { | |||||
context.foundPackageDeclaration(ctx.IDENTIFIER().getText()); | |||||
super.enterPackageClause(ctx); | |||||
} | |||||
private void foundFuncSignature(GoParser.SignatureContext signature) { | |||||
FunctionEntity func = (FunctionEntity) context.lastContainer(); | |||||
if (signature.parameters()!=null) { | |||||
for (GoParser.ParameterDeclContext param : signature.parameters().parameterDecl()) { | |||||
if (param.identifierList() != null) { | |||||
TypeDefHelper typeDefHelper = new TypeDefHelper(param.type_()); | |||||
for (TerminalNode id : param.identifierList().IDENTIFIER()) { | |||||
VarEntity varEntity = new VarEntity(GenericName.build(id.getText()), | |||||
GenericName.build(typeDefHelper.getTypeRefName()), context.lastContainer(), | |||||
entityRepo.generateId()); | |||||
func.addParameter(varEntity); | |||||
} | |||||
} else/* with ... parameters*/ { | |||||
} | |||||
} | |||||
} | |||||
if (signature.result()!=null){ | |||||
if(signature.result().parameters()!=null){ | |||||
for (GoParser.ParameterDeclContext paramDecl:signature.result().parameters().parameterDecl()){ | |||||
TypeDefHelper typeDefHelper = new TypeDefHelper(paramDecl.type_()); | |||||
if (typeDefHelper.isTypeRef()) { | |||||
func.addReturnType(GenericName.build(typeDefHelper.getTypeRefName())); | |||||
}else{ | |||||
System.err.println("TODO: unsupport return type"); | |||||
} | |||||
} | |||||
} | |||||
if (signature.result().type_()!=null){ | |||||
TypeDefHelper typeDefHelper = new TypeDefHelper(signature.result().type_()); | |||||
if (typeDefHelper.isTypeRef()) { | |||||
func.addReturnType(GenericName.build(typeDefHelper.getTypeRefName())); | |||||
}else{ | |||||
System.err.println("TODO: unsupport return type"); | |||||
} | |||||
} | |||||
System.err.println(signature.result().getText()); | |||||
} | |||||
} | |||||
@Override | |||||
public void enterTypeSpec(GoParser.TypeSpecContext ctx) { | |||||
TypeSpecHelper specHelper = new TypeSpecHelper(ctx); | |||||
if (specHelper.getTypeDefHelper().isTypeRef()){ | |||||
context.foundNewAlias(specHelper.getIdentifier(),specHelper.getTypeDefHelper().getTypeRefName()); | |||||
}else if (specHelper.getTypeDefHelper().isStruct()){ | |||||
context.foundNewType(specHelper.getIdentifier(),ctx.getStart().getLine()); | |||||
} | |||||
super.enterTypeSpec(ctx); | |||||
} | |||||
@Override | |||||
public void exitTypeSpec(GoParser.TypeSpecContext ctx) { | |||||
TypeSpecHelper specHelper = new TypeSpecHelper(ctx); | |||||
if (specHelper.getTypeDefHelper().isStruct()){ | |||||
context.exitLastedEntity(); | |||||
} | |||||
super.exitTypeSpec(ctx); | |||||
} | |||||
// fieldDecl | |||||
// : ({noTerminatorBetween(2)}? identifierList type_ | anonymousField) string_? | |||||
// ; | |||||
@Override | |||||
public void enterFieldDecl(GoParser.FieldDeclContext ctx) { | |||||
FieldDeclHelper fieldDeclHelper = new FieldDeclHelper(ctx); | |||||
String typeName = fieldDeclHelper.getTypeName(); | |||||
if (fieldDeclHelper.getIdentifiers()!=null){ | |||||
for (String id:fieldDeclHelper.getIdentifiers()){ | |||||
if (typeName==null){ | |||||
System.err.println("TODO: unsupport fieldDecl without type"); | |||||
} | |||||
context.foundVarDefinition(id, GenericName.build(typeName),new ArrayList<>(),ctx.getStart().getLine()); | |||||
} | |||||
} | |||||
if (fieldDeclHelper.getAnonymousField()!=null){ | |||||
System.err.println("TODO: unsupport anonymousField"); | |||||
} | |||||
if (fieldDeclHelper.getString()!=null){ | |||||
System.err.println("TODO: unsupport field with string " + fieldDeclHelper.getString()); | |||||
} | |||||
super.enterFieldDecl(ctx); | |||||
} | |||||
public void done() { | |||||
} | |||||
} |
@@ -0,0 +1,132 @@ | |||||
package depends.extractor.golang; | |||||
import org.antlr.v4.runtime.*; | |||||
import java.util.List; | |||||
/** | |||||
* All parser methods that used in grammar (p, prev, notLineTerminator, etc.) | |||||
* should start with lower case char similar to parser rules. | |||||
*/ | |||||
public abstract class GoParserBase extends Parser | |||||
{ | |||||
protected GoParserBase(TokenStream input) { | |||||
super(input); | |||||
} | |||||
/** | |||||
* Returns {@code true} iff on the current index of the parser's | |||||
* token stream a token exists on the {@code HIDDEN} channel which | |||||
* either is a line terminator, or is a multi line comment that | |||||
* contains a line terminator. | |||||
* | |||||
* @return {@code true} iff on the current index of the parser's | |||||
* token stream a token exists on the {@code HIDDEN} channel which | |||||
* either is a line terminator, or is a multi line comment that | |||||
* contains a line terminator. | |||||
*/ | |||||
protected boolean lineTerminatorAhead() { | |||||
// Get the token ahead of the current index. | |||||
int possibleIndexEosToken = this.getCurrentToken().getTokenIndex() - 1; | |||||
if (possibleIndexEosToken == -1) | |||||
{ | |||||
return true; | |||||
} | |||||
Token ahead = _input.get(possibleIndexEosToken); | |||||
if (ahead.getChannel() != Lexer.HIDDEN) { | |||||
// We're only interested in tokens on the HIDDEN channel. | |||||
return false; | |||||
} | |||||
if (ahead.getType() == GoLexer.TERMINATOR) { | |||||
// There is definitely a line terminator ahead. | |||||
return true; | |||||
} | |||||
if (ahead.getType() == GoLexer.WS) { | |||||
// Get the token ahead of the current whitespaces. | |||||
possibleIndexEosToken = this.getCurrentToken().getTokenIndex() - 2; | |||||
ahead = _input.get(possibleIndexEosToken); | |||||
} | |||||
// Get the token's text and type. | |||||
String text = ahead.getText(); | |||||
int type = ahead.getType(); | |||||
// Check if the token is, or contains a line terminator. | |||||
return (type == GoLexer.COMMENT && (text.contains("\r") || text.contains("\n"))) || | |||||
(type == GoLexer.TERMINATOR); | |||||
} | |||||
/** | |||||
* Returns {@code true} if no line terminator exists between the specified | |||||
* token offset and the prior one on the {@code HIDDEN} channel. | |||||
* | |||||
* @return {@code true} if no line terminator exists between the specified | |||||
* token offset and the prior one on the {@code HIDDEN} channel. | |||||
*/ | |||||
protected boolean noTerminatorBetween(int tokenOffset) { | |||||
BufferedTokenStream stream = (BufferedTokenStream)_input; | |||||
List<Token> tokens = stream.getHiddenTokensToLeft(stream.LT(tokenOffset).getTokenIndex()); | |||||
if (tokens == null) { | |||||
return true; | |||||
} | |||||
for (Token token : tokens) { | |||||
if (token.getText().contains("\n")) | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
/** | |||||
* Returns {@code true} if no line terminator exists after any encounterd | |||||
* parameters beyond the specified token offset and the next on the | |||||
* {@code HIDDEN} channel. | |||||
* | |||||
* @return {@code true} if no line terminator exists after any encounterd | |||||
* parameters beyond the specified token offset and the next on the | |||||
* {@code HIDDEN} channel. | |||||
*/ | |||||
protected boolean noTerminatorAfterParams(int tokenOffset) { | |||||
BufferedTokenStream stream = (BufferedTokenStream)_input; | |||||
int leftParams = 1; | |||||
int rightParams = 0; | |||||
int valueType; | |||||
if (stream.LT(tokenOffset).getType() == GoLexer.L_PAREN) { | |||||
// Scan past parameters | |||||
while (leftParams != rightParams) { | |||||
tokenOffset++; | |||||
valueType = stream.LT(tokenOffset).getType(); | |||||
if (valueType == GoLexer.L_PAREN){ | |||||
leftParams++; | |||||
} | |||||
else if (valueType == GoLexer.R_PAREN) { | |||||
rightParams++; | |||||
} | |||||
} | |||||
tokenOffset++; | |||||
return noTerminatorBetween(tokenOffset); | |||||
} | |||||
return true; | |||||
} | |||||
protected boolean checkPreviousTokenText(String text) | |||||
{ | |||||
BufferedTokenStream stream = (BufferedTokenStream)_input; | |||||
String prevTokenText = stream.LT(1).getText(); | |||||
if (prevTokenText == null) | |||||
return false; | |||||
return prevTokenText.equals(text); | |||||
} | |||||
} |
@@ -0,0 +1,333 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor; | |||||
import depends.entity.*; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.entity.repo.IdGenerator; | |||||
import depends.importtypes.Import; | |||||
import depends.relations.IBindingResolver; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.Stack; | |||||
import java.util.stream.Collectors; | |||||
public abstract class HandlerContext { | |||||
protected EntityRepo entityRepo; | |||||
protected IdGenerator idGenerator; | |||||
protected FileEntity currentFileEntity; | |||||
protected IBindingResolver bindingResolver; | |||||
public HandlerContext(EntityRepo entityRepo, IBindingResolver bindingResolver) { | |||||
this.entityRepo = entityRepo; | |||||
this.idGenerator = entityRepo; | |||||
entityStack = new Stack<Entity>(); | |||||
this.bindingResolver = bindingResolver; | |||||
} | |||||
public FileEntity startFile(String fileName) { | |||||
currentFileEntity = new FileEntity(fileName, idGenerator.generateId(),true); | |||||
pushToStack(currentFileEntity); | |||||
addToRepo(currentFileEntity); | |||||
return currentFileEntity; | |||||
} | |||||
public TypeEntity foundNewType(GenericName name, Integer startLine) { | |||||
TypeEntity currentTypeEntity = new TypeEntity(name, this.latestValidContainer(), | |||||
idGenerator.generateId()); | |||||
currentTypeEntity.setLine(startLine); | |||||
pushToStack(currentTypeEntity); | |||||
addToRepo(currentTypeEntity); | |||||
currentFileEntity.addType(currentTypeEntity); | |||||
return currentTypeEntity; | |||||
} | |||||
/** | |||||
* Tell the context object that a new type founded. | |||||
* @param name | |||||
* @param startLine | |||||
* @return | |||||
*/ | |||||
public TypeEntity foundNewType(String name, Integer startLine) { | |||||
return foundNewType(GenericName.build(name),startLine); | |||||
} | |||||
public AliasEntity foundNewAlias(String aliasName, String originalName) { | |||||
if (aliasName.equals(originalName)) return null; //it is a tricky, we treat same name no different. | |||||
//indeed it is not perfect -> the right match should depends on no-bare format like "struct a" instead of "a" | |||||
AliasEntity currentTypeEntity = new AliasEntity(GenericName.build(aliasName), this.latestValidContainer(), | |||||
idGenerator.generateId(),GenericName.build(originalName) ); | |||||
addToRepo(currentTypeEntity); | |||||
return currentTypeEntity; | |||||
} | |||||
public AliasEntity foundNewAlias(GenericName aliasName, Entity referToEntity) { | |||||
AliasEntity currentTypeEntity = new AliasEntity(aliasName, this.latestValidContainer(), | |||||
idGenerator.generateId(),aliasName); | |||||
currentTypeEntity.setReferToEntity(referToEntity); | |||||
addToRepo(currentTypeEntity); | |||||
return currentTypeEntity; | |||||
} | |||||
/** | |||||
* Tell the context that a new method was found. | |||||
* Do not forget to tell the context leave the method when you finish | |||||
* the process of the method | |||||
* @param methodName | |||||
* @param returnType - if no return type information avaliable, keep it as null; | |||||
* @param throwedType - if no throwed type information avaliable, keep it as empty list; | |||||
* @return the new function enity | |||||
*/ | |||||
public FunctionEntity foundMethodDeclarator(String methodName, String returnType, List<String> throwedType, Integer startLine) { | |||||
FunctionEntity functionEntity = new FunctionEntity(GenericName.build(methodName), this.latestValidContainer(), | |||||
idGenerator.generateId(),GenericName.build(returnType)); | |||||
functionEntity.setLine(startLine); | |||||
addToRepo(functionEntity); | |||||
this.typeOrFileContainer().addFunction(functionEntity); | |||||
pushToStack(functionEntity); | |||||
functionEntity.addThrowTypes(throwedType.stream().map(item->GenericName.build(item)).collect(Collectors.toList())); | |||||
return functionEntity; | |||||
} | |||||
public FunctionEntity foundMethodDeclarator(String methodName, Integer startLine) { | |||||
FunctionEntity functionEntity = new FunctionEntity(GenericName.build(methodName), this.latestValidContainer(), | |||||
idGenerator.generateId(),null); | |||||
functionEntity.setLine(startLine); | |||||
addToRepo(functionEntity); | |||||
this.typeOrFileContainer().addFunction(functionEntity); | |||||
pushToStack(functionEntity); | |||||
return functionEntity; | |||||
} | |||||
public FunctionEntity foundMethodDeclarator(ContainerEntity containerEntity, String methodName, Integer startLine) { | |||||
FunctionEntity functionEntity = new FunctionEntity(GenericName.build(methodName), containerEntity, | |||||
idGenerator.generateId(),null); | |||||
functionEntity.setLine(startLine); | |||||
addToRepo(functionEntity); | |||||
containerEntity.addFunction(functionEntity); | |||||
pushToStack(functionEntity); | |||||
functionEntity.addThrowTypes(new ArrayList<>()); | |||||
return functionEntity; | |||||
} | |||||
public void foundNewImport(Import imported) { | |||||
currentFileEntity.addImport(imported); | |||||
} | |||||
public TypeEntity currentType() { | |||||
for (int i = entityStack.size() - 1; i >= 0; i--) { | |||||
Entity t = entityStack.get(i); | |||||
if (t instanceof TypeEntity) | |||||
return (TypeEntity) t; | |||||
} | |||||
return null; | |||||
} | |||||
public ContainerEntity typeOrFileContainer() { | |||||
for (int i = entityStack.size() - 1; i >= 0; i--) { | |||||
Entity t = entityStack.get(i); | |||||
if (t instanceof TypeEntity) | |||||
return (ContainerEntity) t; | |||||
if (t instanceof FileEntity) { | |||||
return (ContainerEntity)t; | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
public FunctionEntity currentFunction() { | |||||
for (int i = entityStack.size() - 1; i >= 0; i--) { | |||||
Entity t = entityStack.get(i); | |||||
if (t instanceof FunctionEntity) | |||||
return (FunctionEntity) t; | |||||
} | |||||
return null; | |||||
} | |||||
public FileEntity currentFile() { | |||||
return currentFileEntity; | |||||
} | |||||
public ContainerEntity globalScope() { | |||||
Entity global = entityRepo.getEntity(EntityRepo.GLOBAL_SCOPE_NAME); | |||||
if (global==null) { | |||||
global = new PackageEntity(EntityRepo.GLOBAL_SCOPE_NAME,idGenerator.generateId()); | |||||
addToRepo(global); | |||||
} | |||||
return (ContainerEntity)global; | |||||
} | |||||
public Entity latestValidContainer() { | |||||
for (int i = entityStack.size() - 1; i >= 0; i--) { | |||||
Entity t = entityStack.get(i); | |||||
if (t instanceof FunctionEntity) | |||||
return t; | |||||
if (t instanceof TypeEntity) | |||||
return t; | |||||
if (t instanceof FileEntity) | |||||
return t; | |||||
} | |||||
return null; | |||||
} | |||||
public ContainerEntity lastContainer() { | |||||
for (int i = entityStack.size() - 1; i >= 0; i--) { | |||||
Entity t = entityStack.get(i); | |||||
if (t instanceof ContainerEntity) | |||||
return (ContainerEntity) t; | |||||
} | |||||
return null; | |||||
} | |||||
public void foundImplements(GenericName typeName) { | |||||
currentType().addImplements(typeName); | |||||
} | |||||
public void foundExtends(String className) { | |||||
foundExtends(GenericName.build(className)); | |||||
} | |||||
public void foundExtends(GenericName typeName) { | |||||
if (currentType()==null) { | |||||
System.out.println("error: type do not exist"); | |||||
return ; | |||||
} | |||||
currentType().addExtends(typeName); | |||||
} | |||||
public void foundMixin(String name) { | |||||
foundMixin(GenericName.build(name)); | |||||
} | |||||
public void foundMixin(GenericName name) { | |||||
lastContainer().addMixin(name); | |||||
} | |||||
public void foundTypeParametes(GenericName typeName) { | |||||
lastContainer().addTypeParameter(typeName); | |||||
} | |||||
public List<VarEntity> foundVarDefinitions(List<String> varNames, String type, List<GenericName> typeArguments, Integer line) { | |||||
return varNames.stream().map(item->foundVarDefinition(item,GenericName.build(type),typeArguments,line)).collect(Collectors.toList()); | |||||
} | |||||
public VarEntity foundVarDefinition(ContainerEntity container,String varName,Integer line) { | |||||
if (container==null) { | |||||
System.out.println("fallback to file container for var " + varName + " in file "+ currentFile().getRawName()); | |||||
container = currentFile(); | |||||
} | |||||
VarEntity var = getVarInLocalFile(container,GenericName.build(varName)); | |||||
if (var!=null) return var; | |||||
var = new VarEntity(GenericName.build(varName), null, container, idGenerator.generateId()); | |||||
var.setLine(line); | |||||
container.addVar(var); | |||||
addToRepo(var); | |||||
return var; | |||||
} | |||||
public VarEntity foundGlobalVarDefinition(ContainerEntity container,String varName,Integer line) { | |||||
if (container==null) { | |||||
System.out.println("fallback to file container for var " + varName + " in file "+ currentFile().getRawName()); | |||||
container = currentFile(); | |||||
} | |||||
VarEntity var = getVarInLocalFile(container,GenericName.build(varName)); | |||||
if (var!=null) return var; | |||||
var = new VarEntity(GenericName.build(varName), null, container, idGenerator.generateId()); | |||||
container.addVar(var); | |||||
var.setLine(line); | |||||
var.setQualifiedName(var.getRawName().toString()); | |||||
addToRepo(var); | |||||
return var; | |||||
} | |||||
public VarEntity foundVarDefinition(String varName, GenericName type, List<GenericName> typeArguments,Integer line) { | |||||
VarEntity var = new VarEntity(GenericName.build(varName), type, lastContainer(), idGenerator.generateId()); | |||||
var.setLine(line); | |||||
var.addTypeParameter(typeArguments); | |||||
lastContainer().addVar(var); | |||||
addToRepo(var); | |||||
return var; | |||||
} | |||||
public VarEntity addMethodParameter(String paramName) { | |||||
if (currentFunction()==null) return null; | |||||
VarEntity varEntity = new VarEntity(GenericName.build(paramName),null,currentFunction(),idGenerator.generateId()); | |||||
currentFunction().addParameter(varEntity); | |||||
addToRepo(varEntity); | |||||
return varEntity; | |||||
} | |||||
public VarEntity foundEnumConstDefinition(String varName,Integer line) { | |||||
GenericName type = lastContainer().getRawName(); | |||||
return foundVarDefinition(varName,type,new ArrayList<>(),line); | |||||
} | |||||
protected Stack<Entity> entityStack = new Stack<Entity>(); | |||||
protected void pushToStack(Entity entity) { | |||||
entityStack.push(entity); | |||||
} | |||||
public void exitLastedEntity() { | |||||
//we never pop up the lastest one (FileEntity) | |||||
if (entityStack.size()>1) | |||||
entityStack.pop(); | |||||
} | |||||
private VarEntity getVarInLocalFile(ContainerEntity container, GenericName varName) { | |||||
Entity entity = bindingResolver.resolveName(container, varName, false); | |||||
if (entity ==null ) return null; | |||||
Entity fileEntity = entity.getAncestorOfType(FileEntity.class); | |||||
if (fileEntity ==null ){ | |||||
//may not exist in fileEntity, for example global vars | |||||
}else{ | |||||
if (!fileEntity.equals(currentFileEntity)) return null; | |||||
if (entity instanceof VarEntity) return (VarEntity)entity; | |||||
} | |||||
return null; | |||||
} | |||||
public Entity foundEntityWithName(GenericName rawName) { | |||||
return bindingResolver.resolveName(lastContainer(), rawName, true); | |||||
} | |||||
public void addToRepo(Entity entity) { | |||||
entityRepo.add(entity); | |||||
} | |||||
} |
@@ -0,0 +1,135 @@ | |||||
package depends.entity.repo; | |||||
import depends.entity.Entity; | |||||
import depends.entity.FileEntity; | |||||
import depends.entity.GenericName; | |||||
import depends.entity.MultiDeclareEntities; | |||||
import multilang.depends.util.file.FileUtil; | |||||
import java.util.*; | |||||
import java.util.Map.Entry; | |||||
public class InMemoryEntityRepo extends SimpleIdGenerator implements EntityRepo { | |||||
public class EntityMapIterator implements Iterator<Entity>{ | |||||
private Iterator<Entry<Integer, Entity>> entryIterator; | |||||
public EntityMapIterator(Set<Entry<Integer, Entity>> entries) { | |||||
this.entryIterator = entries.iterator(); | |||||
} | |||||
@Override | |||||
public boolean hasNext() { | |||||
return entryIterator.hasNext(); | |||||
} | |||||
@Override | |||||
public Entity next() { | |||||
return entryIterator.next().getValue(); | |||||
} | |||||
} | |||||
private Map<String, Entity> allEntieisByName; | |||||
private Map<Integer, Entity> allEntitiesById; | |||||
private List<Entity> allFileEntitiesByOrder; | |||||
public InMemoryEntityRepo() { | |||||
allEntieisByName = new TreeMap<>(); | |||||
allEntitiesById = new TreeMap<>(); | |||||
allFileEntitiesByOrder = new LinkedList<>(); | |||||
} | |||||
@Override | |||||
public Entity getEntity(String entityName) { | |||||
return allEntieisByName.get(entityName); | |||||
} | |||||
@Override | |||||
public Entity getEntity(Integer entityId) { | |||||
return allEntitiesById.get(entityId); | |||||
} | |||||
@Override | |||||
public void add(Entity entity) { | |||||
allEntitiesById.put(entity.getId(), entity); | |||||
String name = entity.getRawName().uniqName(); | |||||
if (entity.getQualifiedName() != null && !(entity.getQualifiedName().isEmpty())) { | |||||
name = entity.getQualifiedName(); | |||||
} | |||||
if (allEntieisByName.containsKey(name)) { | |||||
Entity existedEntity = allEntieisByName.get(name); | |||||
if (existedEntity instanceof MultiDeclareEntities) { | |||||
((MultiDeclareEntities) existedEntity).add(entity); | |||||
} else { | |||||
MultiDeclareEntities eMultiDeclare = new MultiDeclareEntities(existedEntity, this.generateId()); | |||||
eMultiDeclare.add(entity); | |||||
allEntieisByName.put(name, eMultiDeclare); | |||||
} | |||||
} else { | |||||
allEntieisByName.put(name, entity); | |||||
} | |||||
if (entity.getParent() != null) | |||||
Entity.setParent(entity, entity.getParent()); | |||||
} | |||||
@Override | |||||
public Iterator<Entity> entityIterator() { | |||||
return new EntityMapIterator(allEntitiesById.entrySet()); | |||||
} | |||||
@Override | |||||
public void update(Entity entity) { | |||||
} | |||||
@Override | |||||
public Entity getEntity(GenericName rawName) { | |||||
return this.getEntity(rawName.uniqName()); | |||||
} | |||||
@Override | |||||
public Collection<Entity> getFileEntities() { | |||||
return allFileEntitiesByOrder; | |||||
} | |||||
@Override | |||||
public Iterator<Entity> sortedFileIterator() { | |||||
return allFileEntitiesByOrder.iterator(); | |||||
} | |||||
@Override | |||||
public void clear() { | |||||
allEntieisByName.clear(); | |||||
allEntitiesById.clear(); | |||||
allFileEntitiesByOrder.clear(); | |||||
} | |||||
@Override | |||||
public FileEntity getFileEntity(String fileFullPath) { | |||||
fileFullPath = FileUtil.uniqFilePath(fileFullPath); | |||||
Entity entity = this.getEntity(fileFullPath); | |||||
if (entity ==null) return null; | |||||
if (entity instanceof FileEntity) return (FileEntity) entity; | |||||
if (entity instanceof MultiDeclareEntities){ | |||||
MultiDeclareEntities multiDeclare = (MultiDeclareEntities) entity; | |||||
for (Entity theEntity: multiDeclare.getEntities()){ | |||||
if (theEntity instanceof FileEntity){ | |||||
return (FileEntity) theEntity; | |||||
} | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
@Override | |||||
public void completeFile(String fileFullPath) { | |||||
FileEntity fileEntity = getFileEntity(fileFullPath); | |||||
// in case of parse error(throw exception), the file entity may not exists | |||||
if (fileEntity!=null) { | |||||
fileEntity.cacheAllExpressions(); | |||||
allFileEntitiesByOrder.add(fileEntity); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,109 @@ | |||||
package depends.extractor.cpp; | |||||
import static org.junit.Assert.assertEquals; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import org.junit.Before; | |||||
import org.junit.Test; | |||||
import depends.deptypes.DependencyType; | |||||
import depends.entity.AliasEntity; | |||||
import multilang.depends.util.file.FileUtil; | |||||
public class IncludeRelationTest extends CppParserTest{ | |||||
@Before | |||||
public void setUp() { | |||||
super.init(); | |||||
} | |||||
@Test | |||||
public void test_includefiles_should_be_imported_relations() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/cpp-code-examples/includesTest/EntryFile.cpp", | |||||
"./src/test/resources/cpp-code-examples/includesTest/LocalHeader.h", | |||||
"./src/test/resources/cpp-code-examples/includesTest/IndirectIncluded.h", | |||||
"./src/test/resources/cpp-code-examples/includesTest/RelativeInclude.h", | |||||
"./src/test/resources/cpp-code-examples/includesTest/path/Header.h", | |||||
}; | |||||
for (String src:srcs) { | |||||
CppFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
File f = new File(srcs[0]); | |||||
assertEquals(3, entityRepo.getEntity(f.getCanonicalPath()).getRelations().size()); | |||||
} | |||||
@Test | |||||
public void test_could_found_files_in_include_path() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/cpp-code-examples/includesTest/EntryFileIncludePathTest.cpp", | |||||
"./src/test/resources/cpp-code-examples/includesTest/path/HeadersWithoutPath.h", | |||||
}; | |||||
List<String> includePaths = new ArrayList<>(); | |||||
includePaths.add("./src/test/resources/cpp-code-examples/includesTest/path/"); | |||||
for (String src:srcs) { | |||||
CppFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
File f = new File(srcs[0]); | |||||
assertEquals(1, entityRepo.getEntity(f.getCanonicalPath()).getRelations().size()); | |||||
} | |||||
@Test | |||||
public void test_type_t_should_be_treat_as_structure() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/cpp-code-examples/typedeftest2.cpp", | |||||
}; | |||||
for (String src:srcs) { | |||||
CppFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
assertEquals("abc",((AliasEntity) entityRepo.getEntity("abc_t")).getOriginType().getRawName().uniqName()); | |||||
} | |||||
@Test | |||||
public void test_call_should_only_in_relations_with_include() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/cpp-code-examples/includeTest2/f0.cpp", | |||||
"./src/test/resources/cpp-code-examples/includeTest2/f_with_include.cpp", | |||||
"./src/test/resources/cpp-code-examples/includeTest2/f_without_include.cpp", | |||||
}; | |||||
for (String src:srcs) { | |||||
CppFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
this.assertContainsRelation(this.entityRepo.getEntity("foo"), DependencyType.CALL, "bar"); | |||||
this.assertNotContainsRelation(this.entityRepo.getEntity("foo2"), DependencyType.CALL, "bar"); | |||||
} | |||||
@Test | |||||
public void should_find_include_relation_in_conditional_macro_block() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/cpp-code-examples/includeTest3/inc_macro_test.c", | |||||
"./src/test/resources/cpp-code-examples/includeTest3/fx.h", | |||||
"./src/test/resources/cpp-code-examples/includeTest3/fy.h", | |||||
}; | |||||
for (String src:srcs) { | |||||
CppFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
this.assertContainsRelation(this.entityRepo.getEntity(FileUtil.uniqFilePath(srcs[0])), DependencyType.IMPORT, FileUtil.uniqFilePath(srcs[1])); | |||||
this.assertContainsRelation(this.entityRepo.getEntity(FileUtil.uniqFilePath(srcs[0])), DependencyType.IMPORT, FileUtil.uniqFilePath(srcs[2])); | |||||
} | |||||
} |
@@ -0,0 +1,88 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.format.json; | |||||
import java.util.ArrayList; | |||||
import java.util.Collection; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import depends.format.FileAttributes; | |||||
import depends.matrix.core.DependencyDetail; | |||||
import depends.matrix.core.DependencyMatrix; | |||||
import depends.matrix.core.DependencyPair; | |||||
import depends.matrix.core.DependencyValue; | |||||
public class JDataBuilder { | |||||
public JDepObject build(DependencyMatrix dependencyMatrix, FileAttributes attribute) { | |||||
ArrayList<String> files = dependencyMatrix.getNodes(); | |||||
Collection<DependencyPair> dependencyPairs = dependencyMatrix.getDependencyPairs(); | |||||
ArrayList<JCellObject> cellObjects = buildCellObjects(dependencyPairs); // transform finalRes into cellObjects | |||||
JDepObject depObject = new JDepObject(); | |||||
depObject.setVariables(files); | |||||
depObject.setName(attribute.getAttributeName()); | |||||
depObject.setSchemaVersion(attribute.getSchemaVersion()); | |||||
depObject.setCells(cellObjects); | |||||
return depObject; | |||||
} | |||||
private ArrayList<JCellObject> buildCellObjects(Collection<DependencyPair> dependencyPairs) { | |||||
ArrayList<JCellObject> cellObjects = new ArrayList<JCellObject>(); | |||||
for (DependencyPair dependencyPair : dependencyPairs) { | |||||
Map<String, Float> valueObject = buildValueObject(dependencyPair.getDependencies()); | |||||
List<DetailItem> details = buildDetails(dependencyPair.getDependencies()); | |||||
JCellObject cellObject = new JCellObject(); | |||||
cellObject.setSrc(dependencyPair.getFrom()); | |||||
cellObject.setDest(dependencyPair.getTo()); | |||||
cellObject.setValues(valueObject); | |||||
cellObject.setDetails(details); | |||||
cellObjects.add(cellObject); | |||||
} | |||||
return cellObjects; | |||||
} | |||||
private List<DetailItem> buildDetails(Collection<DependencyValue> dependencies) { | |||||
List<DetailItem> r = new ArrayList<>(); | |||||
for (DependencyValue dependency : dependencies) { | |||||
for (DependencyDetail detail:dependency.getDetails()) { | |||||
r.add(new DetailItem(detail.getSrc(),detail.getDest(),dependency.getType())); | |||||
} | |||||
} | |||||
if (r.size()==0) return null; | |||||
return r; | |||||
} | |||||
private Map<String, Float> buildValueObject(Collection<DependencyValue> dependencies) { | |||||
Map<String, Float> valueObject = new HashMap<String, Float>(); | |||||
for (DependencyValue dependency : dependencies) { | |||||
valueObject.put(dependency.getType(), (float) dependency.getWeight()); | |||||
} | |||||
return valueObject; | |||||
} | |||||
} |
@@ -0,0 +1,281 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor.ruby.jruby; | |||||
import depends.entity.*; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.extractor.ParserCreator; | |||||
import depends.extractor.IncludedFileLocator; | |||||
import depends.extractor.ruby.RubyHandlerContext; | |||||
import depends.relations.IBindingResolver; | |||||
import org.jrubyparser.ast.*; | |||||
import org.jrubyparser.util.NoopVisitor; | |||||
import java.util.ArrayList; | |||||
import java.util.Collection; | |||||
public class JRubyVisitor extends NoopVisitor { | |||||
private RubyHandlerContext context; | |||||
RubyParserHelper helper = RubyParserHelper.getInst(); | |||||
private ExpressionUsage expressionUsage; | |||||
public JRubyVisitor(String fileFullPath, EntityRepo entityRepo, IncludedFileLocator includedFileLocator, | |||||
IBindingResolver bindingResolver, ParserCreator parserCreator) { | |||||
this.context = new RubyHandlerContext(entityRepo, includedFileLocator, bindingResolver, parserCreator); | |||||
expressionUsage = new ExpressionUsage(context, entityRepo, helper, bindingResolver); | |||||
context.startFile(fileFullPath); | |||||
} | |||||
@Override | |||||
public Object visitAliasNode(AliasNode node) { | |||||
context.foundNewAlias(node.getNewNameString(), node.getOldNameString()); | |||||
return super.visitAliasNode(node); | |||||
} | |||||
@Override | |||||
public Object visitModuleNode(ModuleNode node) { | |||||
String name = helper.getName(node.getCPath()); | |||||
context.foundNamespace(name,node.getPosition().getStartLine()+1); | |||||
super.visitModuleNode(node); | |||||
context.exitLastedEntity(); | |||||
return null; | |||||
} | |||||
@Override | |||||
public Object visitClassNode(ClassNode node) { | |||||
TypeEntity type = context.foundNewType(helper.getName(node.getCPath()),node.getPosition().getStartLine()+1); | |||||
Node superNode = node.getSuper(); | |||||
if (superNode instanceof ConstNode || | |||||
superNode instanceof SymbolNode || | |||||
superNode instanceof Colon2ConstNode || | |||||
superNode instanceof Colon3Node) { | |||||
String superName = helper.getName(superNode); | |||||
context.foundExtends(superName); | |||||
}else{ | |||||
if (superNode != null) { | |||||
System.err.println("cannot support the super node style" + superNode.toString()); | |||||
} | |||||
} | |||||
super.visitClassNode(node); | |||||
context.exitLastedEntity(); | |||||
return null; | |||||
} | |||||
@Override | |||||
public Object visitRootNode(RootNode node) { | |||||
return super.visitRootNode(node); | |||||
} | |||||
@Override | |||||
public Object visitFCallNode(FCallNode node) { | |||||
String fname = helper.getName(node); | |||||
Collection<String> params = getParams(node); | |||||
context.processSpecialFuncCall(fname, params, node.getPosition().getStartLine()+1); | |||||
return super.visitFCallNode(node); | |||||
} | |||||
private Collection<String> getParams(IArgumentNode node) { | |||||
Node args = node.getArgs(); | |||||
Collection<String> params = new ArrayList<>(); | |||||
if (args instanceof ArrayNode) { | |||||
ArrayNode argArray = (ArrayNode) args; | |||||
for (Node arg : argArray.childNodes()) { | |||||
if (arg instanceof StrNode) { | |||||
params.add(((StrNode) arg).getValue()); | |||||
} else if (arg instanceof ConstNode) { | |||||
params.add(((ConstNode) arg).getName()); | |||||
} | |||||
} | |||||
} | |||||
return params; | |||||
} | |||||
@Override | |||||
public Object visitCallNode(CallNode node) { | |||||
String fname = helper.getName(node); | |||||
Collection<String> params = getParams(node); | |||||
addCallToReceiverVar(node, fname); | |||||
context.processSpecialFuncCall(fname, params, node.getPosition().getStartLine()+1); | |||||
return super.visitCallNode(node); | |||||
} | |||||
private void addCallToReceiverVar(CallNode node, String fname) { | |||||
if (helper.isCommonOperator(fname))return; | |||||
Node varNode = node.getReceiver(); | |||||
GenericName varName = GenericName.build(helper.getName(varNode)); | |||||
if (varName==null) return; | |||||
Entity var = context.foundEntityWithName(varName); | |||||
if (var != null && var instanceof VarEntity) { | |||||
VarEntity varEntity = (VarEntity) var; | |||||
varEntity.addFunctionCall(GenericName.build(fname)); | |||||
} | |||||
} | |||||
@Override | |||||
public Object visitUnaryCallNode(UnaryCallNode node) { | |||||
String fname = helper.getName(node); | |||||
Collection<String> params = new ArrayList<>(); | |||||
context.processSpecialFuncCall(fname, params, node.getPosition().getStartLine()+1); | |||||
return super.visitUnaryCallNode(node); | |||||
} | |||||
/** | |||||
* VCallNode is just a function call without parameter | |||||
*/ | |||||
@Override | |||||
public Object visitVCallNode(VCallNode node) { | |||||
return super.visitVCallNode(node); | |||||
} | |||||
@Override | |||||
public Object visitDefnNode(DefnNode node) { | |||||
FunctionEntity method = context.foundMethodDeclarator(node.getName(),node.getPosition().getStartLine()+1); | |||||
method.setLine(node.getPosition().getStartLine()+1); | |||||
super.visitDefnNode(node); | |||||
context.exitLastedEntity(); | |||||
return null; | |||||
} | |||||
@Override | |||||
public Object visitDefsNode(DefsNode node) { | |||||
boolean handled = false; | |||||
Node varNode = node.getReceiver(); | |||||
if (varNode instanceof SelfNode) { | |||||
//will be handled by context.foundMethodDeclarator(node.getName(), null, new ArrayList<>()); | |||||
} else if (varNode instanceof ConstNode) { | |||||
String className = ((INameNode) varNode).getName(); | |||||
Entity entity = context.foundEntityWithName(GenericName.build(className)); | |||||
if (entity != null && entity instanceof ContainerEntity) { | |||||
FunctionEntity method = context.foundMethodDeclarator(((ContainerEntity) entity), node.getName(),node.getPosition().getStartLine()+1); | |||||
method.setLine(node.getPosition().getStartLine()+1); | |||||
handled = true; | |||||
} | |||||
} else if (varNode instanceof INameNode) { | |||||
String varName = ((INameNode) varNode).getName(); | |||||
Entity var = context.foundEntityWithName(GenericName.build(varName)); | |||||
if (var != null && var instanceof ContainerEntity) { | |||||
FunctionEntity method = context.foundMethodDeclarator(((ContainerEntity) var), node.getName(),node.getPosition().getStartLine()+1); | |||||
method.setLine(node.getPosition().getStartLine()+1); | |||||
handled = true; | |||||
} | |||||
} | |||||
if (!handled) { | |||||
// fallback to add it to last container | |||||
FunctionEntity method = context.foundMethodDeclarator(node.getName(),node.getPosition().getStartLine()+1); | |||||
method.setLine(node.getPosition().getStartLine()+1); | |||||
} | |||||
super.visitDefsNode(node); | |||||
context.exitLastedEntity(); | |||||
return null; | |||||
} | |||||
@Override | |||||
public Object visitGlobalVarNode(GlobalVarNode node) { | |||||
context.foundVarDefinition(context.globalScope(), node.getName(),node.getPosition().getStartLine()+1); | |||||
return super.visitGlobalVarNode(node); | |||||
} | |||||
@Override | |||||
public Object visitInstVarNode(InstVarNode node) { | |||||
context.foundVarDefinition(context.currentType(), node.getName(),node.getPosition().getStartLine()+1); | |||||
return super.visitInstVarNode(node); | |||||
} | |||||
@Override | |||||
public Object visitClassVarAsgnNode(ClassVarAsgnNode node) { | |||||
context.foundVarDefinition(helper.getScopeOfVar(node,context), node.getName(),node.getPosition().getStartLine()+1); | |||||
return super.visitClassVarAsgnNode(node); | |||||
} | |||||
@Override | |||||
public Object visitClassVarDeclNode(ClassVarDeclNode node) { | |||||
context.foundVarDefinition(context.currentType(), node.getName(),node.getPosition().getStartLine()+1); | |||||
return super.visitClassVarDeclNode(node); | |||||
} | |||||
@Override | |||||
public Object visitClassVarNode(ClassVarNode node) { | |||||
context.foundVarDefinition(context.currentType(), node.getName(),node.getPosition().getStartLine()+1); | |||||
return super.visitClassVarNode(node); | |||||
} | |||||
@Override | |||||
public Object visitLocalVarNode(LocalVarNode node) { | |||||
return super.visitLocalVarNode(node); | |||||
} | |||||
@Override | |||||
public Object visitDVarNode(DVarNode node) { | |||||
context.foundVarDefinition(context.lastContainer(), node.getName(),node.getPosition().getStartLine()+1); | |||||
return super.visitDVarNode(node); | |||||
} | |||||
@Override | |||||
public Object visitDAsgnNode(DAsgnNode node) { | |||||
context.foundVarDefinition(helper.getScopeOfVar(node,context), node.getName(),node.getPosition().getStartLine()+1); | |||||
expressionUsage.foundExpression(node.getValue()); | |||||
return super.visitDAsgnNode(node); | |||||
} | |||||
@Override | |||||
public Object visitGlobalAsgnNode(GlobalAsgnNode node) { | |||||
context.foundVarDefinition(helper.getScopeOfVar(node,context), node.getName(),node.getPosition().getStartLine()+1); | |||||
return super.visitGlobalAsgnNode(node); | |||||
} | |||||
@Override | |||||
public Object visitInstAsgnNode(InstAsgnNode node) { | |||||
context.foundVarDefinition(helper.getScopeOfVar(node,context), node.getName(),node.getPosition().getStartLine()+1); | |||||
return super.visitInstAsgnNode(node); | |||||
} | |||||
@Override | |||||
public Object visitArgumentNode(ArgumentNode node) { | |||||
String paramName = node.getName(); | |||||
context.addMethodParameter(paramName); | |||||
return super.visitArgumentNode(node); | |||||
} | |||||
@Override | |||||
public Object visitLocalAsgnNode(LocalAsgnNode node) { | |||||
context.foundVarDefinition(helper.getScopeOfVar(node,context), node.getName(),node.getPosition().getStartLine()+1); | |||||
return super.visitLocalAsgnNode(node); | |||||
} | |||||
@Override | |||||
protected Object visit(Node node) { | |||||
expressionUsage.foundExpression(node); | |||||
return super.visit(node); | |||||
} | |||||
} |
@@ -0,0 +1,84 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor.java; | |||||
import depends.entity.repo.BuiltInType; | |||||
public class JavaBuiltInType extends BuiltInType{ | |||||
@Override | |||||
protected String[] getBuiltInTypeName() { | |||||
return new String[]{ | |||||
"void","int","double","char","byte","boolean","long","short","float", | |||||
"BigDecimal","Integer","Double","Char","Byte","Boolean","Long","Short","Float", | |||||
"String","Object","Class","Exception","StringBuilder", | |||||
"Appendable","AutoCloseable","Cloneable","Comparable","Iterable","Readable", | |||||
"Runnable","Thread.UncaughtExceptionHandler","Boolean","Byte","Character","Character.Subset", | |||||
"Character.UnicodeBlock","ClassLoader","ClassValue","Compiler","Double","Enum", | |||||
"InheritableThreadLocal","Math","Number","Package","Process", | |||||
"ProcessBuilder","ProcessBuilder.Redirect","Runtime","RuntimePermission", | |||||
"SecurityManager","StackTraceElement","StrictMath","StringBuffer", | |||||
"System","Thread","ThreadGroup","ThreadLocal","Throwable","Void","ProcessBuilder.Redirect.Type", | |||||
"Thread.State","ArithmeticException","ArrayIndexOutOfBoundsException", | |||||
"ArrayStoreException","ClassCastException","ClassNotFoundException","CloneNotSupportedException", | |||||
"EnumConstantNotPresentException","Exception","IllegalAccessException","IllegalArgumentException", | |||||
"IllegalMonitorStateException","IllegalStateException","IllegalThreadStateException", | |||||
"IndexOutOfBoundsException","InstantiationException","InterruptedException", | |||||
"NegativeArraySizeException","NoSuchFieldException","NoSuchMethodException","NullPointerException", | |||||
"NumberFormatException","ReflectiveOperationException","RuntimeException","SecurityException", | |||||
"StringIndexOutOfBoundsException","TypeNotPresentException","UnsupportedOperationException","AbstractMethodError", | |||||
"AssertionError","BootstrapMethodError","ClassCircularityError","ClassFormatError","Error","ExceptionInInitializerError", | |||||
"IllegalAccessError","IncompatibleClassChangeError","InstantiationError","InternalError","LinkageError","NoClassDefFoundError" | |||||
,"NoSuchFieldError","NoSuchMethodError","OutOfMemoryError","StackOverflowError","ThreadDeath","UnknownError", | |||||
"UnsatisfiedLinkError","UnsupportedClassVersionError","VerifyError","VirtualMachineError","Deprecated","Override", | |||||
"SafeVarargs","SuppressWarnings", | |||||
"Collection","Comparator","Deque","Enumeration","EventListener","Formattable","Iterator","List", | |||||
"ListIterator","Map","Map.Entry","NavigableMap","NavigableSet","Observer","Queue","RandomAccess", | |||||
"Set","SortedMap","SortedSet","AbstractCollection","AbstractList","AbstractMap","AbstractMap.SimpleEntry", | |||||
"AbstractMap.SimpleImmutableEntry","AbstractQueue","AbstractSequentialList","AbstractSet","ArrayDeque", | |||||
"ArrayList","Arrays","BitSet","Calendar","Collections","Currency","Date","Dictionary","EnumMap","EnumSet", | |||||
"EventListenerProxy","EventObject","FormattableFlags","Formatter","GregorianCalendar","HashMap","HashSet", | |||||
"Hashtable","IdentityHashMap","LinkedHashMap","LinkedHashSet","LinkedList","ListResourceBundle","Locale", | |||||
"Locale.Builder","Objects","Observable","PriorityQueue","Properties","PropertyPermission", | |||||
"PropertyResourceBundle","Random","ResourceBundle","ResourceBundle.Control","Scanner", | |||||
"ServiceLoader","SimpleTimeZone","Stack","StringTokenizer","Timer","TimerTask","TimeZone", | |||||
"TreeMap","TreeSet","UUID","Vector","WeakHashMap","Formatter.BigDecimalLayoutForm", | |||||
"Locale.Category","ConcurrentModificationException","DuplicateFormatFlagsException", | |||||
"EmptyStackException","FormatFlagsConversionMismatchException","FormatterClosedException", | |||||
"IllegalFormatCodePointException","IllegalFormatConversionException","IllegalFormatException", | |||||
"IllegalFormatFlagsException","IllegalFormatPrecisionException","IllegalFormatWidthException", | |||||
"IllformedLocaleException","InputMismatchException","InvalidPropertiesFormatException","MissingFormatArgumentException", | |||||
"MissingFormatWidthException","MissingResourceException","NoSuchElementException","TooManyListenersException", | |||||
"UnknownFormatConversionException","UnknownFormatFlagsException","ServiceConfigurationError", | |||||
"<Built-in>" | |||||
}; | |||||
} | |||||
@Override | |||||
protected String[] getBuiltInTypePrefix() { | |||||
return new String[]{ | |||||
"java.","javax.","com.sun." | |||||
}; | |||||
} | |||||
} |
@@ -0,0 +1,105 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor.java; | |||||
import depends.entity.Entity; | |||||
import depends.entity.FileEntity; | |||||
import depends.entity.PackageEntity; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.extractor.UnsolvedBindings; | |||||
import depends.importtypes.Import; | |||||
import depends.relations.ImportLookupStrategy; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.Set; | |||||
public class JavaImportLookupStrategy extends ImportLookupStrategy{ | |||||
public JavaImportLookupStrategy(EntityRepo repo) { | |||||
super(repo); | |||||
} | |||||
@Override | |||||
public Entity lookupImportedType(String name, FileEntity fileEntity) { | |||||
//Java Strategy | |||||
String importedString = fileEntity.importedSuffixMatch(name); | |||||
if (importedString==null) return null; | |||||
return repo.getEntity(importedString); | |||||
} | |||||
@Override | |||||
public List<Entity> getImportedRelationEntities(List<Import> importedList) { | |||||
ArrayList<Entity> result = new ArrayList<>(); | |||||
for (Import importedItem:importedList) { | |||||
Entity imported = repo.getEntity(importedItem.getContent()); | |||||
if (imported==null) continue; | |||||
if (imported instanceof PackageEntity) { | |||||
//ignore wildcard import relation | |||||
}else { | |||||
result.add(imported); | |||||
} | |||||
} | |||||
return result; | |||||
} | |||||
@Override | |||||
public List<Entity> getImportedTypes(List<Import> importedList,Set<UnsolvedBindings> unsolvedBindings) { | |||||
ArrayList<Entity> result = new ArrayList<>(); | |||||
for (Import importedItem:importedList) { | |||||
Entity imported = repo.getEntity(importedItem.getContent()); | |||||
if (imported==null) { | |||||
unsolvedBindings.add(new UnsolvedBindings(importedItem.getContent(),null)); | |||||
continue; | |||||
} | |||||
if (imported instanceof PackageEntity) { | |||||
//expand import of package to all classes under the package due to we dis-courage the behavior | |||||
for (Entity child:imported.getChildren()) { | |||||
if (child instanceof FileEntity) { | |||||
child.getChildren().forEach(item->result.add(item)); | |||||
}else { | |||||
result.add(child); | |||||
} | |||||
} | |||||
}else { | |||||
result.add(imported); | |||||
} | |||||
} | |||||
return result; | |||||
} | |||||
@Override | |||||
public List<Entity> getImportedFiles(List<Import> importedList) { | |||||
return new ArrayList<Entity>(); | |||||
} | |||||
@Override | |||||
public boolean supportGlobalNameLookup() { | |||||
return true; | |||||
} | |||||
} |
@@ -0,0 +1,337 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor.java; | |||||
import depends.entity.*; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.extractor.java.JavaParser.*; | |||||
import depends.extractor.java.context.*; | |||||
import depends.importtypes.ExactMatchImport; | |||||
import depends.relations.IBindingResolver; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
public class JavaListener extends JavaParserBaseListener { | |||||
private final JavaHandlerContext context; | |||||
private final AnnotationProcessor annotationProcessor; | |||||
private final ExpressionUsage expressionUsage; | |||||
private final EntityRepo entityRepo; | |||||
public JavaListener(String fileFullPath, EntityRepo entityRepo, IBindingResolver bindingResolver) { | |||||
this.context = new JavaHandlerContext(entityRepo, bindingResolver); | |||||
this.entityRepo = entityRepo; | |||||
annotationProcessor = new AnnotationProcessor(); | |||||
expressionUsage = new ExpressionUsage(context,entityRepo); | |||||
context.startFile(fileFullPath); | |||||
} | |||||
//////////////////////// | |||||
// Package | |||||
@Override | |||||
public void enterPackageDeclaration(PackageDeclarationContext ctx) { | |||||
context.foundNewPackage(QualitiedNameContextHelper.getName(ctx.qualifiedName())); | |||||
super.enterPackageDeclaration(ctx); | |||||
} | |||||
//////////////////////// | |||||
// Import | |||||
@Override | |||||
public void enterImportDeclaration(ImportDeclarationContext ctx) { | |||||
context.foundNewImport(new ExactMatchImport(ctx.qualifiedName().getText())); | |||||
super.enterImportDeclaration(ctx); | |||||
} | |||||
/////////////////////// | |||||
// Class or Interface | |||||
// classDeclaration | enumDeclaration | interfaceDeclaration | | |||||
/////////////////////// annotationTypeDeclaration | |||||
@Override | |||||
public void enterClassDeclaration(ClassDeclarationContext ctx) { | |||||
if (ctx.IDENTIFIER()==null) return; | |||||
context.foundNewType(GenericName.build(ctx.IDENTIFIER().getText()), ctx.getStart().getLine()); | |||||
// implements | |||||
if (ctx.typeList() != null) { | |||||
for (int i = 0; i < ctx.typeList().typeType().size(); i++) { | |||||
context.foundImplements(GenericName.build(ClassTypeContextHelper.getClassName(ctx.typeList().typeType().get(i)))); | |||||
} | |||||
} | |||||
// extends relation | |||||
if (ctx.typeType() != null) { | |||||
context.foundExtends(GenericName.build(ClassTypeContextHelper.getClassName(ctx.typeType()))); | |||||
} | |||||
if (ctx.typeParameters() != null) { | |||||
foundTypeParametersUse(ctx.typeParameters()); | |||||
} | |||||
annotationProcessor.processAnnotationModifier(ctx, TypeDeclarationContext.class ,"classOrInterfaceModifier.annotation",context.lastContainer()); | |||||
super.enterClassDeclaration(ctx); | |||||
} | |||||
@Override | |||||
public void exitClassDeclaration(ClassDeclarationContext ctx) { | |||||
exitLastEntity(); | |||||
super.exitClassDeclaration(ctx); | |||||
} | |||||
@Override | |||||
public void enterEnumDeclaration(EnumDeclarationContext ctx) { | |||||
context.foundNewType(GenericName.build(ctx.IDENTIFIER().getText()), ctx.getStart().getLine()); | |||||
annotationProcessor.processAnnotationModifier(ctx, TypeDeclarationContext.class ,"classOrInterfaceModifier.annotation",context.lastContainer()); | |||||
super.enterEnumDeclaration(ctx); | |||||
} | |||||
@Override | |||||
public void enterAnnotationTypeDeclaration(AnnotationTypeDeclarationContext ctx) { | |||||
context.foundNewType(GenericName.build(ctx.IDENTIFIER().getText()), ctx.getStart().getLine()); | |||||
annotationProcessor.processAnnotationModifier(ctx, TypeDeclarationContext.class ,"classOrInterfaceModifier.annotation",context.lastContainer()); | |||||
super.enterAnnotationTypeDeclaration(ctx); | |||||
} | |||||
@Override | |||||
public void exitEnumDeclaration(EnumDeclarationContext ctx) { | |||||
exitLastEntity(); | |||||
super.exitEnumDeclaration(ctx); | |||||
} | |||||
/** | |||||
* interfaceDeclaration : INTERFACE IDENTIFIER typeParameters? (EXTENDS | |||||
* typeList)? interfaceBody ; | |||||
*/ | |||||
@Override | |||||
public void enterInterfaceDeclaration(InterfaceDeclarationContext ctx) { | |||||
context.foundNewType(GenericName.build(ctx.IDENTIFIER().getText()), ctx.getStart().getLine()); | |||||
// type parameters | |||||
if (ctx.typeParameters() != null) { | |||||
foundTypeParametersUse(ctx.typeParameters()); | |||||
} | |||||
// extends relation | |||||
if (ctx.typeList() != null) { | |||||
for (int i = 0; i < ctx.typeList().typeType().size(); i++) { | |||||
context.foundExtends(ClassTypeContextHelper.getClassName(ctx.typeList().typeType().get(i))); | |||||
} | |||||
} | |||||
annotationProcessor.processAnnotationModifier(ctx, TypeDeclarationContext.class ,"classOrInterfaceModifier.annotation",context.lastContainer()); | |||||
super.enterInterfaceDeclaration(ctx); | |||||
} | |||||
@Override | |||||
public void exitInterfaceDeclaration(InterfaceDeclarationContext ctx) { | |||||
exitLastEntity(); | |||||
super.exitInterfaceDeclaration(ctx); | |||||
} | |||||
@Override | |||||
public void exitAnnotationTypeDeclaration(AnnotationTypeDeclarationContext ctx) { | |||||
exitLastEntity(); | |||||
super.exitAnnotationTypeDeclaration(ctx); | |||||
} | |||||
///////////////////////// | |||||
// Method | |||||
@Override | |||||
public void enterMethodDeclaration(MethodDeclarationContext ctx) { | |||||
List<String> throwedType = QualitiedNameContextHelper.getNames(ctx.qualifiedNameList()); | |||||
String methodName = ctx.IDENTIFIER().getText(); | |||||
String returnedType = ClassTypeContextHelper.getClassName(ctx.typeTypeOrVoid()); | |||||
FunctionEntity method = context.foundMethodDeclarator(methodName, returnedType, throwedType,ctx.getStart().getLine()); | |||||
new FormalParameterListContextHelper(ctx.formalParameters(), method, entityRepo); | |||||
if (ctx.typeParameters() != null) { | |||||
List<GenericName> parameters = TypeParameterContextHelper.getTypeParameters(ctx.typeParameters()); | |||||
method.addTypeParameter(parameters); | |||||
} | |||||
annotationProcessor.processAnnotationModifier(ctx, ClassBodyDeclarationContext.class,"modifier.classOrInterfaceModifier.annotation",context.lastContainer()); | |||||
super.enterMethodDeclaration(ctx); | |||||
} | |||||
@Override | |||||
public void exitMethodDeclaration(MethodDeclarationContext ctx) { | |||||
exitLastEntity(); | |||||
super.exitMethodDeclaration(ctx); | |||||
} | |||||
private void exitLastEntity() { | |||||
context.exitLastedEntity(); | |||||
} | |||||
// interfaceMethodDeclaration | |||||
// : interfaceMethodModifier* (typeTypeOrVoid | typeParameters annotation* typeTypeOrVoid) | |||||
// IDENTIFIER formalParameters ('[' ']')* (THROWS qualifiedNameList)? methodBody | |||||
@Override | |||||
public void enterInterfaceMethodDeclaration(InterfaceMethodDeclarationContext ctx) { | |||||
List<String> throwedType = QualitiedNameContextHelper.getNames(ctx.qualifiedNameList()); | |||||
FunctionEntity method = context.foundMethodDeclarator(ctx.IDENTIFIER().getText(), | |||||
ClassTypeContextHelper.getClassName(ctx.typeTypeOrVoid()), throwedType,ctx.getStart().getLine()); | |||||
new FormalParameterListContextHelper(ctx.formalParameters(), method, entityRepo); | |||||
if (ctx.typeParameters() != null) { | |||||
foundTypeParametersUse(ctx.typeParameters()); | |||||
} | |||||
annotationProcessor.processAnnotationModifier(ctx, InterfaceBodyDeclarationContext.class,"modifier.classOrInterfaceModifier.annotation",context.lastContainer()); | |||||
super.enterInterfaceMethodDeclaration(ctx); | |||||
} | |||||
@Override | |||||
public void exitInterfaceMethodDeclaration(InterfaceMethodDeclarationContext ctx) { | |||||
exitLastEntity(); | |||||
super.exitInterfaceMethodDeclaration(ctx); | |||||
} | |||||
@Override | |||||
public void enterConstructorDeclaration(ConstructorDeclarationContext ctx) { | |||||
List<String> throwedType = QualitiedNameContextHelper.getNames(ctx.qualifiedNameList()); | |||||
FunctionEntity method = context.foundMethodDeclarator(ctx.IDENTIFIER().getText(), ctx.IDENTIFIER().getText(), | |||||
throwedType,ctx.getStart().getLine()); | |||||
new FormalParameterListContextHelper(ctx.formalParameters(), method, entityRepo); | |||||
method.addReturnType(context.currentType()); | |||||
annotationProcessor.processAnnotationModifier(ctx, ClassBodyDeclarationContext.class,"modifier.classOrInterfaceModifier.annotation",context.lastContainer()); | |||||
super.enterConstructorDeclaration(ctx); | |||||
} | |||||
@Override | |||||
public void exitConstructorDeclaration(ConstructorDeclarationContext ctx) { | |||||
exitLastEntity(); | |||||
super.exitConstructorDeclaration(ctx); | |||||
} | |||||
///////////////////////////////////////////////////////// | |||||
// Field | |||||
@Override | |||||
public void enterFieldDeclaration(FieldDeclarationContext ctx) { | |||||
List<String> varNames = VariableDeclaratorsContextHelper.getVariables(ctx.variableDeclarators()); | |||||
String type = ClassTypeContextHelper.getClassName(ctx.typeType()); | |||||
List<GenericName> typeArguments = ClassTypeContextHelper.getTypeArguments(ctx.typeType()); | |||||
List<VarEntity> vars = context.foundVarDefinitions(varNames, type,typeArguments,ctx.getStart().getLine()); | |||||
annotationProcessor.processAnnotationModifier(ctx, ClassBodyDeclarationContext.class,"modifier.classOrInterfaceModifier.annotation",vars); | |||||
super.enterFieldDeclaration(ctx); | |||||
} | |||||
@Override | |||||
public void enterConstDeclaration(ConstDeclarationContext ctx) { | |||||
List<GenericName> typeArguments = ClassTypeContextHelper.getTypeArguments(ctx.typeType()); | |||||
List<VarEntity> vars = context.foundVarDefinitions(VariableDeclaratorsContextHelper.getVariables(ctx.constantDeclarator()), | |||||
ClassTypeContextHelper.getClassName(ctx.typeType()),typeArguments, ctx.getStart().getLine()); | |||||
annotationProcessor.processAnnotationModifier(ctx, InterfaceBodyDeclarationContext.class,"modifier.classOrInterfaceModifier.annotation",vars); | |||||
super.enterConstDeclaration(ctx); | |||||
} | |||||
@Override | |||||
public void enterEnumConstant(EnumConstantContext ctx) { | |||||
if (ctx.IDENTIFIER() != null) { | |||||
context.foundEnumConstDefinition(ctx.IDENTIFIER().getText(),ctx.getStart().getLine()); | |||||
} | |||||
super.enterEnumConstant(ctx); | |||||
} | |||||
@Override | |||||
public void enterAnnotationMethodRest(AnnotationMethodRestContext ctx) { | |||||
context.foundMethodDeclarator(ctx.IDENTIFIER().getText(), ClassTypeContextHelper.getClassName(ctx.typeType()), | |||||
new ArrayList<>(),ctx.getStart().getLine()); | |||||
super.enterAnnotationMethodRest(ctx); | |||||
} | |||||
@Override | |||||
public void exitAnnotationMethodRest(AnnotationMethodRestContext ctx) { | |||||
exitLastEntity(); | |||||
super.exitAnnotationMethodRest(ctx); | |||||
} | |||||
@Override | |||||
public void enterAnnotationConstantRest(AnnotationConstantRestContext ctx) { | |||||
// TODO: no variable type defined in annotation const? | |||||
context.foundVarDefinitions(VariableDeclaratorsContextHelper.getVariables(ctx.variableDeclarators()), "", new ArrayList<>(), ctx.getStart().getLine()); | |||||
super.enterAnnotationConstantRest(ctx); | |||||
} | |||||
/////////////////////////////////////////// | |||||
// variables | |||||
// TODO: all modifier have not processed yet. | |||||
@Override | |||||
public void enterLocalVariableDeclaration(LocalVariableDeclarationContext ctx) { | |||||
List<GenericName> typeArguments = ClassTypeContextHelper.getTypeArguments(ctx.typeType()); | |||||
context.foundVarDefinitions(VariableDeclaratorsContextHelper.getVariables((ctx.variableDeclarators())), | |||||
ClassTypeContextHelper.getClassName(ctx.typeType()), typeArguments, ctx.getStart().getLine()); | |||||
super.enterLocalVariableDeclaration(ctx); | |||||
} | |||||
public void enterEnhancedForControl(EnhancedForControlContext ctx) { | |||||
List<GenericName> typeArguments = ClassTypeContextHelper.getTypeArguments(ctx.typeType()); | |||||
context.foundVarDefinitions(VariableDeclaratorsContextHelper.getVariable((ctx.variableDeclaratorId())), | |||||
ClassTypeContextHelper.getClassName(ctx.typeType()), typeArguments, ctx.getStart().getLine()); | |||||
super.enterEnhancedForControl(ctx); | |||||
} | |||||
// resource | |||||
// : variableModifier* classOrInterfaceType variableDeclaratorId '=' expression | |||||
// ; | |||||
@Override | |||||
public void enterResource(ResourceContext ctx) { | |||||
List<GenericName> typeArguments = ClassTypeContextHelper.getTypeArguments(ctx.classOrInterfaceType()); | |||||
context.foundVarDefinition(ctx.variableDeclaratorId().IDENTIFIER().getText(), | |||||
GenericName.build(IdentifierContextHelper.getName(ctx.classOrInterfaceType().IDENTIFIER())), typeArguments,ctx.getStart().getLine()); | |||||
super.enterResource(ctx); | |||||
} | |||||
@Override | |||||
public void enterExpression(ExpressionContext ctx) { | |||||
Expression expr = expressionUsage.foundExpression(ctx); | |||||
expr.setLine(ctx.getStart().getLine()); | |||||
super.enterExpression(ctx); | |||||
} | |||||
///////////////////////////////////////////// | |||||
// Block | |||||
@Override | |||||
public void enterBlock(BlockContext ctx) { | |||||
// TODO support block in java | |||||
super.enterBlock(ctx); | |||||
} | |||||
@Override | |||||
public void exitBlock(BlockContext ctx) { | |||||
// TODO support block in java | |||||
super.exitBlock(ctx); | |||||
} | |||||
/* type parameters <T> <T1,T2>, <> treat as USE */ | |||||
private void foundTypeParametersUse(TypeParametersContext typeParameters) { | |||||
for (int i = 0; i < typeParameters.typeParameter().size(); i++) { | |||||
TypeParameterContext typeParam = typeParameters.typeParameter(i); | |||||
if (typeParam.typeBound() != null) { | |||||
for (int j = 0; j < typeParam.typeBound().typeType().size(); j++) { | |||||
context.foundTypeParametes(GenericName.build(ClassTypeContextHelper.getClassName(typeParam.typeBound().typeType(j)))); | |||||
} | |||||
} | |||||
context.currentType().addTypeParameter(GenericName.build(typeParam.IDENTIFIER().getText())); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,100 @@ | |||||
package depends.extractor.java; | |||||
import depends.deptypes.DependencyType; | |||||
import depends.entity.ContainerEntity; | |||||
import depends.entity.Entity; | |||||
import depends.entity.FunctionEntity; | |||||
import depends.entity.TypeEntity; | |||||
import org.junit.Before; | |||||
import org.junit.Test; | |||||
import java.io.IOException; | |||||
import static org.junit.Assert.assertEquals; | |||||
public class JavaVarResolveTest extends JavaParserTest{ | |||||
@Before | |||||
public void setUp() { | |||||
super.init(); | |||||
} | |||||
@Test | |||||
public void test_field_var_should_be_parsed() throws IOException { | |||||
String src = "./src/test/resources/java-code-examples/FieldVar.java"; | |||||
JavaFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
resolveAllBindings(); | |||||
Entity classEntity = entityRepo.getEntity("FieldVar"); | |||||
assertEquals(3,((TypeEntity)classEntity).getVars().size()); | |||||
} | |||||
@Test | |||||
public void test_local_var_should_be_parsed() throws IOException { | |||||
String src = "./src/test/resources/java-code-examples/LocalVar.java"; | |||||
JavaFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
resolveAllBindings(); | |||||
assertEquals(1,((TypeEntity)entityRepo.getEntity("LocalVar")).getVars().size()); | |||||
assertEquals(1,((FunctionEntity)entityRepo.getEntity("LocalVar.foo")).getVars().size()); | |||||
} | |||||
@Test | |||||
public void test_local_var_type_could_be_inferred() throws IOException { | |||||
String src = "./src/test/resources/java-code-examples/LocalVarInferExample.java"; | |||||
JavaFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
resolveAllBindings(); | |||||
ContainerEntity e = (ContainerEntity) entityRepo.getEntity("LocalVarInferExample.setExample"); | |||||
this.assertContainsRelation(e, DependencyType.CONTAIN, "MyInteger"); | |||||
} | |||||
@Test | |||||
public void test_field_access_could_be_inferred() throws IOException { | |||||
String src = "./src/test/resources/java-code-examples/ComplexExpressionExample.java"; | |||||
JavaFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
resolveAllBindings(); | |||||
Entity e = entityRepo.getEntity("test.ComplexExpressionExample.setExample"); | |||||
this.assertContainsRelation(e, DependencyType.PARAMETER, "test.ClassA"); | |||||
this.assertContainsRelation(e, DependencyType.CREATE, "test.ClassA"); | |||||
this.assertContainsRelation(e, DependencyType.CALL, "test.ClassA"); | |||||
this.assertContainsRelation(e, DependencyType.CAST, "test.ClassA"); | |||||
this.assertContainsRelation(e, DependencyType.CALL, "test.ClassA.foo"); | |||||
this.assertContainsRelation(e, DependencyType.CALL, "test.ClassA"); | |||||
this.assertContainsRelation(e, DependencyType.USE, "test.ComplexExpressionExample.setExample.a3"); | |||||
this.assertContainsRelation(e, DependencyType.USE, "test.ClassX.m"); | |||||
this.assertContainsRelation(e, DependencyType.USE, "test.ComplexExpressionExample.setExample.a2"); | |||||
this.assertContainsRelation(e, DependencyType.USE, "test.ClassA.x"); | |||||
this.assertContainsRelation(e, DependencyType.USE, "test.ComplexExpressionExample.setExample.a"); | |||||
} | |||||
@Test | |||||
public void test_long_static_function_should_be_inferred() throws IOException { | |||||
String src = "./src/test/resources/java-code-examples/LongExpressionWithAbsolutePath.java"; | |||||
JavaFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
resolveAllBindings(); | |||||
assertEquals(5,entityRepo.getEntity("x.LongExpressionWithAbsolutePath.setExample").getRelations().size()); | |||||
} | |||||
@Test | |||||
public void test_call_should_be_referred() throws IOException { | |||||
String src = "./src/test/resources/java-code-examples/ExpressionCallTest.java"; | |||||
JavaFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
resolveAllBindings(); | |||||
assertEquals(10,entityRepo.getEntity("ValidateAll.validate").getRelations().size()); | |||||
} | |||||
@Test | |||||
public void test_could_detect_type_argument_in_field() throws IOException { | |||||
String src = "./src/test/resources/java-code-examples/TypeArgument.java"; | |||||
JavaFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
resolveAllBindings(); | |||||
this.assertContainsRelation(entityRepo.getEntity("JDepObject.cells"),DependencyType.PARAMETER, "JCellObject"); | |||||
} | |||||
} |
@@ -0,0 +1,261 @@ | |||||
package com.educoder.bridge.service; | |||||
import com.alibaba.fastjson.JSONObject; | |||||
import com.educoder.bridge.model.SSHInfo; | |||||
import com.educoder.bridge.model.SSHSession; | |||||
import com.educoder.bridge.utils.Base64Util; | |||||
import com.jcraft.jsch.ChannelShell; | |||||
import com.jcraft.jsch.JSch; | |||||
import com.jcraft.jsch.Session; | |||||
import com.jcraft.jsch.UserInfo; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.springframework.stereotype.Service; | |||||
import org.springframework.web.socket.TextMessage; | |||||
import org.springframework.web.socket.WebSocketSession; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.io.OutputStream; | |||||
import java.util.Arrays; | |||||
import java.util.List; | |||||
import java.util.Optional; | |||||
import java.util.concurrent.CopyOnWriteArrayList; | |||||
import java.util.concurrent.ExecutorService; | |||||
import java.util.concurrent.Executors; | |||||
@Service | |||||
public class JchService { | |||||
private static List<SSHSession> sshSessionQueue = new CopyOnWriteArrayList<>(); | |||||
private ExecutorService executorService = Executors.newCachedThreadPool(); | |||||
private Logger logger = LoggerFactory.getLogger(getClass()); | |||||
com.jcraft.jsch.Logger jschLogger = new com.jcraft.jsch.Logger() { | |||||
@Override | |||||
public boolean isEnabled(int arg0) { | |||||
return true; | |||||
} | |||||
@Override | |||||
public void log(int arg0, String arg1) { | |||||
if (logger.isTraceEnabled()) { | |||||
logger.trace("JSch Log [Level " + arg0 + "]: " + arg1); | |||||
} | |||||
} | |||||
}; | |||||
/** | |||||
* 在webSocket连接时,初始化一个ssh连接 | |||||
* | |||||
* @param webSocketSession webSocket连接 | |||||
*/ | |||||
public void add(WebSocketSession webSocketSession) { | |||||
SSHSession sshSession = new SSHSession(); | |||||
sshSession.setWebSocketSession(webSocketSession); | |||||
sshSessionQueue.add(sshSession); | |||||
} | |||||
/** | |||||
* 处理客户端发过来的数据 | |||||
* @param buffer 数据 | |||||
* @param webSocketSession webSocket连接 | |||||
*/ | |||||
public void recv(String buffer, WebSocketSession webSocketSession) { | |||||
SSHSession sshSession = null; | |||||
try { | |||||
logger.debug("webSocketSessionID: {}, 信息: {}", webSocketSession.getId(), buffer); | |||||
JSONObject info = JSONObject.parseObject(buffer); | |||||
String tp = info.getString("tp"); | |||||
sshSession = findByWebSocketSession(webSocketSession); | |||||
//初始化连接 | |||||
if ("init".equals(tp)) { | |||||
// {"tp":"init","data":{"host":"127.0.0.1","port":"41080","username":"root","password":"123123"}} | |||||
SSHInfo sshInfo = info.getObject("data", SSHInfo.class); | |||||
sshSession.setSSHInfo(sshInfo); | |||||
if (sshSession != null) { | |||||
SSHSession finalSSHSession = sshSession; | |||||
// 新开一个线程建立连接,连接开启之后以一直监听来自客户端的输入 | |||||
executorService.execute(() -> { | |||||
connectTossh(finalSSHSession); | |||||
}); | |||||
} | |||||
} else if ("client".equals(tp)) { | |||||
String data = info.getString("data"); | |||||
// 将网页输入的数据传送给后端服务器 | |||||
if (sshSession != null) { | |||||
transTossh(sshSession.getOutputStream(), data); | |||||
} | |||||
} | |||||
} catch (Exception e) { | |||||
logger.error("转发命令到ssh出错: {}", e); | |||||
close(sshSession); | |||||
} | |||||
} | |||||
/** | |||||
* 将数据传送给服务端作为SSH的输入 | |||||
* | |||||
* @param outputStream | |||||
* @param data | |||||
* @throws IOException | |||||
*/ | |||||
private void transTossh(OutputStream outputStream, String data) throws IOException { | |||||
if (outputStream != null) { | |||||
outputStream.write(data.getBytes()); | |||||
outputStream.flush(); | |||||
} | |||||
} | |||||
/** | |||||
* 连接ssh | |||||
* | |||||
* @param sshSession ssh连接需要的信息 | |||||
*/ | |||||
private void connectTossh(SSHSession sshSession){ | |||||
Session jschSession = null; | |||||
SSHInfo SSHInfo = sshSession.getSSHInfo(); | |||||
try { | |||||
JSch jsch = new JSch(); | |||||
JSch.setLogger(jschLogger); | |||||
//启动线程 | |||||
java.util.Properties config = new java.util.Properties(); | |||||
config.put("StrictHostKeyChecking", "no"); | |||||
jschSession = jsch.getSession(SSHInfo.getUsername(), SSHInfo.getHost(), SSHInfo.getPort()); | |||||
jschSession.setConfig(config); | |||||
jschSession.setPassword(SSHInfo.getPassword()); | |||||
jschSession.setUserInfo(new UserInfo() { | |||||
@Override | |||||
public String getPassphrase() { | |||||
return null; | |||||
} | |||||
@Override | |||||
public String getPassword() { | |||||
return null; | |||||
} | |||||
@Override | |||||
public boolean promptPassword(String s) { | |||||
return false; | |||||
} | |||||
@Override | |||||
public boolean promptPassphrase(String s) { | |||||
return false; | |||||
} | |||||
@Override | |||||
public boolean promptYesNo(String s) { | |||||
return true; | |||||
} // Accept all server keys | |||||
@Override | |||||
public void showMessage(String s) { | |||||
} | |||||
}); | |||||
jschSession.connect(); | |||||
ChannelShell channel = (ChannelShell) jschSession.openChannel("shell"); | |||||
channel.setPtyType("xterm"); | |||||
channel.connect(); | |||||
sshSession.setChannel(channel); | |||||
InputStream inputStream = channel.getInputStream(); | |||||
sshSession.setOutputStream(channel.getOutputStream()); | |||||
sshSession.setSSHInfo(SSHInfo); | |||||
logger.debug("主机: {} 连接成功!", SSHInfo.getHost()); | |||||
// 循环读取,jsch的输入为服务器执行命令之后的返回数据 | |||||
byte[] buf = new byte[1024]; | |||||
while (true) { | |||||
int length = inputStream.read(buf); | |||||
if (length < 0) { | |||||
close(sshSession); | |||||
throw new Exception("读取出错,数据长度:" + length); | |||||
} | |||||
sendMsg(sshSession.getWebSocketSession(), Arrays.copyOfRange(buf, 0, length)); | |||||
} | |||||
} catch (Exception e) { | |||||
logger.error("ssh连接出错, e: {}", e); | |||||
} finally { | |||||
logger.info("连接关闭, {}", SSHInfo.getHost()); | |||||
if (jschSession != null) { | |||||
jschSession.disconnect(); | |||||
} | |||||
close(sshSession); | |||||
} | |||||
} | |||||
/** | |||||
* 发送数据回websocket | |||||
* | |||||
* @param webSocketSession webSocket连接 | |||||
* @param buffer 数据 | |||||
* @throws IOException | |||||
*/ | |||||
public void sendMsg(WebSocketSession webSocketSession, byte[] buffer) throws IOException { | |||||
logger.debug("服务端返回的数据: {}", new String(buffer, "UTF-8")); | |||||
webSocketSession.sendMessage(new TextMessage(Base64Util.encodeBytes(buffer))); | |||||
} | |||||
/** | |||||
* 通过webSocket连接在队列中找到对应的SSH连接 | |||||
* | |||||
* @param webSocketSession webSocket连接 | |||||
*/ | |||||
public SSHSession findByWebSocketSession(WebSocketSession webSocketSession) { | |||||
Optional<SSHSession> optional = sshSessionQueue.stream().filter(webscoketObj -> webscoketObj.getWebSocketSession() == webSocketSession).findFirst(); | |||||
if (optional.isPresent()) { | |||||
return optional.get(); | |||||
} | |||||
return null; | |||||
} | |||||
/** | |||||
* 关闭ssh和websocket连接 | |||||
* | |||||
* @param sshSession ssh连接 | |||||
*/ | |||||
private void close(SSHSession sshSession) { | |||||
if (sshSession != null) { | |||||
sshSession.getChannel().disconnect(); | |||||
try { | |||||
sshSession.getWebSocketSession().close(); | |||||
sshSession.getOutputStream().close(); | |||||
} catch (IOException e) { | |||||
logger.error("连接关闭失败!e: {}", e); | |||||
} | |||||
sshSessionQueue.remove(sshSession); | |||||
} | |||||
} | |||||
/** | |||||
* 通过webSocketSession关闭ssh与webSocket连接 | |||||
* | |||||
* @param webSocketSession | |||||
*/ | |||||
public void closeByWebSocket(WebSocketSession webSocketSession) { | |||||
close(findByWebSocketSession(webSocketSession)); | |||||
} | |||||
} |
@@ -0,0 +1,197 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends; | |||||
import depends.addons.DV8MappingFileBuilder; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.extractor.AbstractLangProcessor; | |||||
import depends.extractor.LangProcessorRegistration; | |||||
import depends.extractor.UnsolvedBindings; | |||||
import depends.format.DependencyDumper; | |||||
import depends.format.detail.UnsolvedSymbolDumper; | |||||
import depends.generator.DependencyGenerator; | |||||
import depends.generator.FileDependencyGenerator; | |||||
import depends.generator.FunctionDependencyGenerator; | |||||
import depends.generator.StructureDependencyGenerator; | |||||
import depends.matrix.core.DependencyMatrix; | |||||
import depends.matrix.transform.MatrixLevelReducer; | |||||
import depends.relations.BindingResolver; | |||||
import depends.relations.IBindingResolver; | |||||
import depends.relations.RelationCounter; | |||||
import edu.emory.mathcs.backport.java.util.Arrays; | |||||
import multilang.depends.util.file.FileUtil; | |||||
import multilang.depends.util.file.FolderCollector; | |||||
import multilang.depends.util.file.TemporaryFile; | |||||
import multilang.depends.util.file.path.*; | |||||
import multilang.depends.util.file.strip.LeadingNameStripper; | |||||
import net.sf.ehcache.CacheManager; | |||||
import org.codehaus.plexus.util.StringUtils; | |||||
import picocli.CommandLine; | |||||
import picocli.CommandLine.PicocliException; | |||||
import java.io.File; | |||||
import java.util.List; | |||||
import java.util.Set; | |||||
/** | |||||
* The entry pooint of depends | |||||
*/ | |||||
public class Main { | |||||
public static void main(String[] args) { | |||||
try { | |||||
LangRegister langRegister = new LangRegister(); | |||||
langRegister.register(); | |||||
DependsCommand appArgs = CommandLine.populateCommand(new DependsCommand(), args); | |||||
if (appArgs.help) { | |||||
CommandLine.usage(new DependsCommand(), System.out); | |||||
System.exit(0); | |||||
} | |||||
executeCommand(appArgs); | |||||
} catch (Exception e) { | |||||
if (e instanceof PicocliException) { | |||||
CommandLine.usage(new DependsCommand(), System.out); | |||||
} else if (e instanceof ParameterException){ | |||||
System.err.println(e.getMessage()); | |||||
}else { | |||||
System.err.println("Exception encountered. If it is a design error, please report issue to us." ); | |||||
e.printStackTrace(); | |||||
} | |||||
System.exit(0); | |||||
} | |||||
} | |||||
@SuppressWarnings("unchecked") | |||||
private static void executeCommand(DependsCommand args) throws ParameterException { | |||||
String lang = args.getLang(); | |||||
String inputDir = args.getSrc(); | |||||
String[] includeDir = args.getIncludes(); | |||||
String outputName = args.getOutputName(); | |||||
String outputDir = args.getOutputDir(); | |||||
String[] outputFormat = args.getFormat(); | |||||
inputDir = FileUtil.uniqFilePath(inputDir); | |||||
if (args.isAutoInclude()) { | |||||
includeDir = appendAllFoldersToIncludePath(inputDir, includeDir); | |||||
} | |||||
AbstractLangProcessor langProcessor = LangProcessorRegistration.getRegistry().getProcessorOf(lang); | |||||
if (langProcessor == null) { | |||||
System.err.println("Not support this language: " + lang); | |||||
return; | |||||
} | |||||
IBindingResolver bindingResolver = new BindingResolver(langProcessor, args.isOutputExternalDependencies(), args.isDuckTypingDeduce()); | |||||
long startTime = System.currentTimeMillis(); | |||||
//step1: build data | |||||
EntityRepo entityRepo = langProcessor.buildDependencies(inputDir, includeDir, bindingResolver); | |||||
new RelationCounter(entityRepo,langProcessor, bindingResolver).computeRelations(); | |||||
System.out.println("Dependency done...."); | |||||
//step2: generate dependencies matrix | |||||
DependencyGenerator dependencyGenerator = getDependencyGenerator(args, inputDir); | |||||
DependencyMatrix matrix = dependencyGenerator.identifyDependencies(entityRepo,args.getTypeFilter()); | |||||
//step3: output | |||||
if (args.getGranularity().startsWith("L")) { | |||||
matrix = new MatrixLevelReducer(matrix,args.getGranularity().substring(1)).shrinkToLevel(); | |||||
} | |||||
DependencyDumper output = new DependencyDumper(matrix); | |||||
output.outputResult(outputName,outputDir,outputFormat); | |||||
if (args.isOutputExternalDependencies()) { | |||||
Set<UnsolvedBindings> unsolved = langProcessor.getExternalDependencies(); | |||||
UnsolvedSymbolDumper unsolvedSymbolDumper = new UnsolvedSymbolDumper(unsolved,args.getOutputName(),args.getOutputDir(), | |||||
new LeadingNameStripper(args.isStripLeadingPath(),inputDir,args.getStrippedPaths())); | |||||
unsolvedSymbolDumper.output(); | |||||
} | |||||
long endTime = System.currentTimeMillis(); | |||||
TemporaryFile.getInstance().delete(); | |||||
CacheManager.create().shutdown(); | |||||
System.out.println("Consumed time: " + (float) ((endTime - startTime) / 1000.00) + " s, or " | |||||
+ (float) ((endTime - startTime) / 60000.00) + " min."); | |||||
if ( args.isDv8map()) { | |||||
DV8MappingFileBuilder dv8MapfileBuilder = new DV8MappingFileBuilder(langProcessor.supportedRelations()); | |||||
dv8MapfileBuilder.create(outputDir+ File.separator+"depends-dv8map.mapping"); | |||||
} | |||||
} | |||||
private static String[] appendAllFoldersToIncludePath(String inputDir, String[] includeDir) { | |||||
FolderCollector includePathCollector = new FolderCollector(); | |||||
List<String> additionalIncludePaths = includePathCollector.getFolders(inputDir); | |||||
additionalIncludePaths.addAll(Arrays.asList(includeDir)); | |||||
includeDir = additionalIncludePaths.toArray(new String[] {}); | |||||
return includeDir; | |||||
} | |||||
private static DependencyGenerator getDependencyGenerator(DependsCommand app, String inputDir) throws ParameterException { | |||||
FilenameWritter filenameWritter = new EmptyFilenameWritter(); | |||||
if (!StringUtils.isEmpty(app.getNamePathPattern())) { | |||||
if (app.getNamePathPattern().equals("dot")|| | |||||
app.getNamePathPattern().equals(".")) { | |||||
filenameWritter = new DotPathFilenameWritter(); | |||||
}else if (app.getNamePathPattern().equals("unix")|| | |||||
app.getNamePathPattern().equals("/")) { | |||||
filenameWritter = new UnixPathFilenameWritter(); | |||||
}else if (app.getNamePathPattern().equals("windows")|| | |||||
app.getNamePathPattern().equals("\\")) { | |||||
filenameWritter = new WindowsPathFilenameWritter(); | |||||
}else{ | |||||
throw new ParameterException("Unknown name pattern paremater:" + app.getNamePathPattern()); | |||||
} | |||||
} | |||||
/* by default use file dependency generator */ | |||||
DependencyGenerator dependencyGenerator = new FileDependencyGenerator(); | |||||
if (!StringUtils.isEmpty(app.getGranularity())) { | |||||
/* method parameter means use method generator */ | |||||
if (app.getGranularity().equals("method")) | |||||
dependencyGenerator = new FunctionDependencyGenerator(); | |||||
else if (app.getGranularity().equals("structure")) | |||||
dependencyGenerator = new StructureDependencyGenerator(); | |||||
else if (app.getGranularity().equals("file")) | |||||
/*no action*/; | |||||
else if (app.getGranularity().startsWith("L")) | |||||
/*no action*/; | |||||
else | |||||
throw new ParameterException("Unknown granularity parameter:" + app.getGranularity()); | |||||
} | |||||
if (app.isStripLeadingPath() || | |||||
app.getStrippedPaths().length>0) { | |||||
dependencyGenerator.setLeadingStripper(new LeadingNameStripper(app.isStripLeadingPath(), inputDir, app.getStrippedPaths())); | |||||
} | |||||
if (app.isDetail()) { | |||||
dependencyGenerator.setGenerateDetail(true); | |||||
} | |||||
dependencyGenerator.setFilenameRewritter(filenameWritter); | |||||
return dependencyGenerator; | |||||
} | |||||
} |
@@ -0,0 +1,131 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.matrix.transform; | |||||
import java.util.ArrayList; | |||||
import java.util.Comparator; | |||||
import java.util.HashMap; | |||||
import depends.matrix.core.DependencyMatrix; | |||||
import depends.matrix.core.DependencyPair; | |||||
import depends.matrix.core.DependencyValue; | |||||
public class MatrixLevelReducer { | |||||
private DependencyMatrix origin; | |||||
private int level; | |||||
HashMap<String, Integer> nodesMap = new HashMap<>(); | |||||
public MatrixLevelReducer(DependencyMatrix matrix, String levelString) { | |||||
this.origin = matrix; | |||||
this.level = stringToPositiveInt(levelString); | |||||
} | |||||
public DependencyMatrix shrinkToLevel() { | |||||
if (level < 0) | |||||
return origin; | |||||
ArrayList<String> reMappedNodes = new ArrayList<>(); | |||||
for (String node : origin.getNodes()) { | |||||
String newNode = calcuateNodeAtLevel(node, level); | |||||
if (!reMappedNodes.contains(newNode)) { | |||||
reMappedNodes.add(newNode); | |||||
} | |||||
} | |||||
// sort nodes by name | |||||
reMappedNodes.sort(new Comparator<String>() { | |||||
@Override | |||||
public int compare(String o1, String o2) { | |||||
return o1.compareTo(o2); | |||||
} | |||||
}); | |||||
DependencyMatrix ordered = new DependencyMatrix(); | |||||
for (int id=0;id<reMappedNodes.size();id++) { | |||||
nodesMap.put(reMappedNodes.get(id), id); | |||||
ordered.addNode(reMappedNodes.get(id), id); | |||||
} | |||||
// add dependencies | |||||
for (DependencyPair dependencyPair : origin.getDependencyPairs()) { | |||||
for (DependencyValue dep : dependencyPair.getDependencies()) { | |||||
ordered.addDependency(dep.getType(), translateToNewId(dependencyPair.getFrom()), | |||||
translateToNewId(dependencyPair.getTo()), dep.getWeight(), dep.getDetails()); | |||||
} | |||||
} | |||||
return ordered; | |||||
} | |||||
public static String calcuateNodeAtLevel(String node, int level) { | |||||
String splitterRegex = "\\."; | |||||
String splitter = "."; | |||||
String windowsSplitter = "\\"; | |||||
String unixSplitter = "/"; | |||||
if (node.contains(windowsSplitter)) { | |||||
splitter = windowsSplitter; | |||||
splitterRegex = windowsSplitter+windowsSplitter; | |||||
}else if (node.contains(unixSplitter)) { | |||||
splitter = unixSplitter; | |||||
splitterRegex = unixSplitter; | |||||
} | |||||
String prefix = ""; | |||||
if (node.startsWith(splitter)) { | |||||
prefix = splitter; | |||||
} | |||||
String[] segments = node.split(splitterRegex); | |||||
StringBuffer sb = new StringBuffer(); | |||||
int count = 0; | |||||
for (int i = 0; i < segments.length; i++) { | |||||
if (count == level) | |||||
break; | |||||
if (segments[i].length() > 0) { | |||||
if (sb.length()>0) | |||||
sb.append(splitter); | |||||
sb.append(segments[i]); | |||||
count++; | |||||
} | |||||
} | |||||
return prefix + sb.toString(); | |||||
} | |||||
private Integer translateToNewId(Integer id) { | |||||
String newNode = calcuateNodeAtLevel(origin.getNodeName(id), level); | |||||
return nodesMap.get(newNode); | |||||
} | |||||
private int stringToPositiveInt(String level) { | |||||
int result = -1; | |||||
try { | |||||
result = Integer.parseInt(level); | |||||
} catch (Exception e) { | |||||
result = -1; | |||||
} | |||||
if (result <= 0) { | |||||
result = -1; | |||||
} | |||||
return result; | |||||
} | |||||
} |
@@ -0,0 +1,109 @@ | |||||
package depends.extractor; | |||||
import depends.entity.ContainerEntity; | |||||
import depends.entity.Entity; | |||||
import depends.entity.FunctionEntity; | |||||
import depends.entity.VarEntity; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.relations.BindingResolver; | |||||
import depends.relations.IBindingResolver; | |||||
import depends.relations.Relation; | |||||
import depends.relations.RelationCounter; | |||||
import multilang.depends.util.file.TemporaryFile; | |||||
import java.util.ArrayList; | |||||
import java.util.Collection; | |||||
import java.util.Set; | |||||
import static org.junit.Assert.fail; | |||||
public abstract class ParserTest { | |||||
protected EntityRepo entityRepo ; | |||||
protected IBindingResolver bindingResolver; | |||||
protected AbstractLangProcessor langProcessor; | |||||
protected void init(){ | |||||
entityRepo = langProcessor.getEntityRepo(); | |||||
bindingResolver = new BindingResolver(langProcessor,true,false); | |||||
langProcessor.bindingResolver = bindingResolver; | |||||
TemporaryFile.reset(); | |||||
} | |||||
protected void init(boolean duckTypingDeduce){ | |||||
entityRepo = langProcessor.getEntityRepo(); | |||||
bindingResolver = new BindingResolver(langProcessor,false,duckTypingDeduce); | |||||
langProcessor.bindingResolver = bindingResolver; | |||||
TemporaryFile.reset(); | |||||
} | |||||
public Set<UnsolvedBindings> resolveAllBindings() { | |||||
Set<UnsolvedBindings> result = bindingResolver.resolveAllBindings(langProcessor.isEagerExpressionResolve()); | |||||
new RelationCounter(entityRepo,langProcessor, bindingResolver).computeRelations(); | |||||
return result; | |||||
} | |||||
protected Set<UnsolvedBindings> resolveAllBindings(boolean callAsImpl) { | |||||
Set<UnsolvedBindings> result = bindingResolver.resolveAllBindings(langProcessor.isEagerExpressionResolve()); | |||||
new RelationCounter(entityRepo,langProcessor, bindingResolver).computeRelations(); | |||||
return result; | |||||
} | |||||
protected void assertNotContainsRelation(Entity inEntity, String dependencyType, String dependedEntityFullName) { | |||||
for (Relation r:inEntity.getRelations()) { | |||||
if (r.getType().equals(dependencyType)) { | |||||
if (r.getEntity().getQualifiedName().equals(dependedEntityFullName)) { | |||||
fail("found unexpected relation: type = " + dependencyType + " to entity " + dependedEntityFullName); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
protected void assertContainsRelation(Entity inEntity, String dependencyType, String dependedEntityFullName) { | |||||
Relation relation = null; | |||||
for (Relation r:inEntity.getRelations()) { | |||||
if (r.getType().equals(dependencyType)) { | |||||
relation = r; | |||||
if (r.getEntity()==null) continue; | |||||
if (r.getEntity().getQualifiedName().equals(dependedEntityFullName)) | |||||
return; | |||||
} | |||||
} | |||||
if (relation==null) { | |||||
fail("cannot found relation type of "+ dependencyType); | |||||
}else { | |||||
fail("cannot found relation type of " + dependencyType + " to entity " + dependedEntityFullName); | |||||
} | |||||
} | |||||
protected void assertContainsVarWithRawName(Entity entity, String name) { | |||||
ContainerEntity container = (ContainerEntity)entity; | |||||
ArrayList<VarEntity> vars = container.getVars(); | |||||
for (VarEntity var:vars) { | |||||
if (var.getRawName().uniqName().equals(name)) { | |||||
return; | |||||
} | |||||
} | |||||
fail("cannot found var with rawname " + name); | |||||
} | |||||
protected void assertContainsParametersWithRawName(FunctionEntity function, String name) { | |||||
Collection<VarEntity> vars = function.getParameters(); | |||||
for (VarEntity var:vars) { | |||||
if (var.getRawName().uniqName().equals(name)) { | |||||
return; | |||||
} | |||||
} | |||||
fail("cannot found parameter with rawname " + name); | |||||
} | |||||
protected void assertContainReturnType(FunctionEntity function, String name) { | |||||
Collection<Entity> types = function.getReturnTypes(); | |||||
for (Entity type:types) { | |||||
if (type.getRawName().uniqName().equals(name)) { | |||||
return; | |||||
} | |||||
} | |||||
fail("cannot found return type with rawname " + name); | |||||
} | |||||
} |
@@ -0,0 +1,165 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor.pom; | |||||
import depends.entity.Expression; | |||||
import depends.entity.GenericName; | |||||
import depends.entity.VarEntity; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.extractor.FileParser; | |||||
import depends.extractor.xml.XMLParser.ElementContext; | |||||
import depends.extractor.xml.XMLParserBaseListener; | |||||
import depends.relations.IBindingResolver; | |||||
import org.antlr.v4.runtime.ParserRuleContext; | |||||
import java.io.IOException; | |||||
import java.util.List; | |||||
import java.util.Stack; | |||||
public class PomListener extends XMLParserBaseListener { | |||||
private PomHandlerContext context; | |||||
private EntityRepo entityRepo; | |||||
PomArtifactEntity currentEntity; | |||||
private VarEntity currentVar; | |||||
Expression currentExpression; | |||||
private PomParent pomParent; | |||||
private PomProcessor parseCreator; | |||||
private List<String> includePaths; | |||||
private IBindingResolver IBindingResolver; | |||||
private Stack<PomCoords> pomCoords= new Stack<>(); | |||||
public PomListener(String fileFullPath, EntityRepo entityRepo, List<String> includePaths, PomProcessor parseCreator, | |||||
IBindingResolver bindingResolver) { | |||||
this.context = new PomHandlerContext(entityRepo); | |||||
this.entityRepo = entityRepo; | |||||
this.parseCreator = parseCreator; | |||||
this.includePaths = includePaths; | |||||
this.IBindingResolver = bindingResolver; | |||||
context.startFile(fileFullPath); | |||||
} | |||||
@Override | |||||
public void enterElement(ElementContext ctx) { | |||||
String name = ctx.Name(0).getText(); | |||||
if (name.equals("project")) { | |||||
pomCoords.push(new PomCoords()); | |||||
currentEntity = new PomArtifactEntity("", context.currentFile(), entityRepo.generateId()); | |||||
} else if (name.equals("plugin")) { | |||||
pomCoords.push(new PomCoords()); | |||||
currentExpression = new Expression(entityRepo.generateId()); | |||||
currentExpression.setRawType(""); | |||||
} else if (name.equals("dependency")) { | |||||
pomCoords.push(new PomCoords()); | |||||
currentVar = new VarEntity(GenericName.build(""), GenericName.build(""), | |||||
currentEntity, entityRepo.generateId()); | |||||
} else if (name.equals("parent")) { | |||||
pomCoords.push(new PomCoords()); | |||||
pomParent = new PomParent(""); | |||||
} | |||||
// Add attribute | |||||
else if (name.equals("groupId")) { | |||||
peekPomCoords().groupId = getContentValueOf(ctx); | |||||
} else if (name.equals("artifactId")) { | |||||
peekPomCoords().artifactId = getContentValueOf(ctx); | |||||
} else if (name.equals("version")) { | |||||
peekPomCoords().version = getContentValueOf(ctx); | |||||
} else if ("properties".equals(getParentElementName(ctx))) { | |||||
if (ctx.content() != null) { | |||||
currentEntity.addProperty(name, getContentValueOf(ctx)); | |||||
} | |||||
} | |||||
super.enterElement(ctx); | |||||
} | |||||
private PomCoords peekPomCoords() { | |||||
return pomCoords.peek(); | |||||
} | |||||
private String getContentValueOf(ElementContext ctx) { | |||||
String text = ctx.content().getText(); | |||||
if (text == null) | |||||
return ""; | |||||
if (text.contains("${")) | |||||
text = currentEntity.replaceProperty(text); | |||||
return text; | |||||
} | |||||
@Override | |||||
public void exitElement(ElementContext ctx) { | |||||
String name = ctx.Name(0).getText(); | |||||
if (name.equals("project")) { | |||||
if (pomParent != null) { | |||||
peekPomCoords().fillFromIfNull(pomParent); | |||||
} | |||||
currentEntity.setRawName(peekPomCoords().getGenericNamePath()); | |||||
currentEntity.setQualifiedName(currentEntity.getRawName().uniqName()); | |||||
entityRepo.add(currentEntity); | |||||
pomCoords.pop(); | |||||
} else if (name.equals("plugin")) { | |||||
peekPomCoords().sureFillVersion(includePaths); | |||||
currentExpression.setRawType(peekPomCoords().getGenericNamePath()); | |||||
currentEntity.addExpression(ctx, currentExpression); | |||||
pomCoords.pop(); | |||||
} else if (name.equals("dependency")) { | |||||
peekPomCoords().sureFillVersion(includePaths); | |||||
currentVar.setRawType(peekPomCoords().getGenericNamePath()); | |||||
//TODO: Depends currently has a limitation: var name cannot be same as var type | |||||
//To be fixed in future | |||||
currentVar.setRawName(new GenericName("_" + currentVar.getRawType().getName())); | |||||
currentEntity.addVar(currentVar); | |||||
pomCoords.pop(); | |||||
} else if (name.equals("parent")) { | |||||
pomParent.buildFrom(peekPomCoords()); | |||||
context.currentFile().addImport(pomParent); | |||||
String parentFileName = new PomLocator(includePaths, pomParent).getLocation(); | |||||
if (parentFileName != null) { | |||||
FileParser importedParser = parseCreator.createFileParser(); | |||||
try { | |||||
importedParser.parse(parentFileName); | |||||
} catch (IOException e) { | |||||
System.err.println("error occurred during parse "+ parentFileName); | |||||
} | |||||
} | |||||
pomCoords.pop(); | |||||
context.currentFile().inferLocalLevelEntities(IBindingResolver); | |||||
} | |||||
super.exitElement(ctx); | |||||
} | |||||
private String getParentElementName(ParserRuleContext node) { | |||||
node = node.getParent(); | |||||
if (node == null) | |||||
return "project"; | |||||
node = node.getParent(); | |||||
if (!(node instanceof ElementContext)) | |||||
return "project"; | |||||
ElementContext p = (ElementContext) node; | |||||
String name = p.Name().get(0).getText(); | |||||
return name; | |||||
} | |||||
} |
@@ -0,0 +1,126 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor.cpp.cdt; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.util.ArrayList; | |||||
import java.util.HashSet; | |||||
import java.util.List; | |||||
import org.eclipse.cdt.core.dom.ast.IASTFileLocation; | |||||
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement; | |||||
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement; | |||||
import org.eclipse.cdt.internal.core.parser.scanner.ScannerUtility; | |||||
import multilang.depends.util.file.FileTraversal; | |||||
import multilang.depends.util.file.FileTraversal.IFileVisitor; | |||||
import multilang.depends.util.file.FileUtil; | |||||
public class PreprocessorHandler { | |||||
private List<String> includePaths; | |||||
private String inputSrcPath; | |||||
private HashSet<String> allFiles = new HashSet<>(); | |||||
public PreprocessorHandler(String inputSrcPath, List<String> includePaths){ | |||||
this.inputSrcPath = inputSrcPath; | |||||
this.includePaths = includePaths; | |||||
buildAllFiles(); | |||||
} | |||||
class AllFileVisitor implements IFileVisitor{ | |||||
@Override | |||||
public void visit(File file) { | |||||
try { | |||||
allFiles.add(file.getCanonicalPath()); | |||||
} catch (IOException e) { | |||||
} | |||||
} | |||||
} | |||||
private void buildAllFiles() { | |||||
allFiles = new HashSet<>(); | |||||
AllFileVisitor v = new AllFileVisitor(); | |||||
if (inputSrcPath!=null) { | |||||
FileTraversal ft = new FileTraversal(v,false,true); | |||||
ft.travers(inputSrcPath); | |||||
} | |||||
for (String includePath:includePaths) { | |||||
FileTraversal ft = new FileTraversal(v,false,true); | |||||
ft.travers(includePath); | |||||
} | |||||
} | |||||
private boolean existFile(String checkPath) { | |||||
checkPath = FileUtil.uniformPath(checkPath); | |||||
return allFiles.contains(checkPath); | |||||
} | |||||
public List<String> getDirectIncludedFiles(IASTPreprocessorStatement[] statements, String fileLocation) { | |||||
ArrayList<String> includedFullPathNames = new ArrayList<>(); | |||||
for (int statementIndex=0;statementIndex<statements.length;statementIndex++) { | |||||
if (statements[statementIndex] instanceof IASTPreprocessorIncludeStatement) | |||||
{ | |||||
IASTPreprocessorIncludeStatement incl = (IASTPreprocessorIncludeStatement)(statements[statementIndex]); | |||||
if (!incl.getFileLocation().getFileName().equals(fileLocation)) | |||||
continue; | |||||
String path = resolveInclude(incl); | |||||
if (!existFile(path)) { | |||||
continue; | |||||
} | |||||
if (FileUtil.isDirectory(path)) { | |||||
continue; | |||||
} | |||||
includedFullPathNames.add(path); | |||||
} | |||||
} | |||||
return includedFullPathNames; | |||||
} | |||||
private String resolveInclude(IASTPreprocessorIncludeStatement incl) { | |||||
String path = incl.toString(); | |||||
int pos = path.indexOf(' '); | |||||
path = path.substring(pos+1).trim(); | |||||
if (path.startsWith("\"") || path.startsWith("<")){ | |||||
path = path.substring(1); | |||||
path = path.substring(0,path.length()-1); | |||||
} | |||||
//First search in local directory | |||||
IASTFileLocation location = incl.getFileLocation(); | |||||
String locationDir = FileUtil.getLocatedDir(location.getFileName()); | |||||
ArrayList<String> searchPath = new ArrayList<>(); | |||||
searchPath.add(locationDir); | |||||
searchPath.addAll(includePaths); | |||||
for (String includePath:searchPath) { | |||||
String checkPath = ScannerUtility.createReconciledPath(includePath,path); | |||||
if (existFile(checkPath)) { | |||||
return FileUtil.uniqFilePath(checkPath); | |||||
} | |||||
} | |||||
return ""; | |||||
} | |||||
public List<String> getIncludePaths() { | |||||
return includePaths; | |||||
} | |||||
} |
@@ -0,0 +1,96 @@ | |||||
package depends.extractor.python; | |||||
import depends.entity.FunctionCall; | |||||
import depends.entity.FunctionEntity; | |||||
import depends.entity.GenericName; | |||||
import depends.entity.TypeEntity; | |||||
import depends.entity.repo.BuiltInType; | |||||
import depends.relations.FunctionMatcher; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
public class PythonBuiltInType extends BuiltInType { | |||||
public static String[] BUILT_IN_FUNCTIONS = { "abs", "delattr", "hash", "memoryview", "set", "all", "dict", "help", | |||||
"min", "setattr", "any", "dir", "hex", "next", "slice", "exit", "ascii", "divmod", "id", "object", "sorted", | |||||
"bin", "enumerate", "input", "oct", "staticmethod", "bool", "eval", "int", "open", "str", "breakpoint", | |||||
"exec", "isinstance", "ord", "sum", "bytearray", "filter", "issubclass", "pow", "super", "bytes", "float", | |||||
"iter", "print", "tuple", "callable", "format", "len", "property", "type", "chr", "frozenset", "list", | |||||
"range", "vars", "classmethod", "getattr", "locals", "repr", "zip", "compile", "globals", "map", "reversed", | |||||
"__import__", "complex", "hasattr", "max", "round" }; | |||||
/** | |||||
* methods of built-in String | |||||
*/ | |||||
public static String[] BUILT_IN_STRING_METHODS = { "capitalize", "center", "casefold", "count", "endswith", | |||||
"expandtabs", "encode", "find", "format", "index", "isalnum", "isalpha", "isdecimal", "isdigit", | |||||
"isidentifier", "islower", "isnumeric", "isprintable", "isspace", "istitle", "isupper", "join", "ljust", | |||||
"rjust", "lower", "upper", "swapcase", "lstrip", "rstrip", "strip", "partition", "maketrans", "rpartition", | |||||
"translate", "replace", "rfind", "rindex", "split", "rsplit", "splitlines", "startswith", "title", "zfill", | |||||
"format_map" }; | |||||
/** | |||||
* methods of built-in List | |||||
*/ | |||||
public static String[] BUILT_IN_LIST_METHODS = { "index", "append", "extend", "insert", "remove", "count", "pop", | |||||
"reverse", "sort", "copy", "clear" }; | |||||
/** | |||||
* methods of built-in Tuple | |||||
*/ | |||||
public static String[] BUILT_IN_TUPLE_METHODS = { "index", "count" }; | |||||
/** | |||||
* methods of built-in Dict | |||||
*/ | |||||
public static String[] BUILT_IN_DICT_METHODS = { "clear", "copy", "fromkeys", "get", "items", "keys", "popitem", | |||||
"setdefault", "pop", "values", "update", }; | |||||
/** | |||||
* methods of built-in Set | |||||
*/ | |||||
public static String[] BUILT_IN_SET_METHODS = { "remove", "add", "copy", "clear", "difference", "difference_update", | |||||
"discard", "intersection", "intersection_update", "isdisjoint", "issubset", "pop", "symmetric_difference", | |||||
"symmetric_difference_update", "union", "update" }; | |||||
/** | |||||
* methods of built-in File | |||||
*/ | |||||
public static String[] BUILT_IN_FILE_METHOD = { "close", "flush", "fileno", "isatty", "next", "read", "readline", | |||||
"readlines", "seek", "tell", "truncate", "write", "writelines" }; | |||||
List<TypeEntity> buildInTypes = new ArrayList<>(); | |||||
public PythonBuiltInType() { | |||||
addBuildInType(BUILT_IN_FILE_METHOD); | |||||
addBuildInType(BUILT_IN_SET_METHODS); | |||||
addBuildInType(BUILT_IN_DICT_METHODS); | |||||
addBuildInType(BUILT_IN_TUPLE_METHODS); | |||||
addBuildInType(BUILT_IN_LIST_METHODS); | |||||
addBuildInType(BUILT_IN_STRING_METHODS); | |||||
} | |||||
private void addBuildInType(String[] methods) { | |||||
TypeEntity type = new TypeEntity(); | |||||
for (String method:methods) { | |||||
FunctionEntity func = new FunctionEntity(GenericName.build(method),type,-1,GenericName.build("")); | |||||
type.addFunction(func); | |||||
} | |||||
buildInTypes.add(type); | |||||
} | |||||
@Override | |||||
public boolean isBuildInTypeMethods(List<FunctionCall> functionCalls) { | |||||
for (TypeEntity type:buildInTypes) { | |||||
FunctionMatcher functionMatcher = new FunctionMatcher(type.getFunctions()); | |||||
if (functionMatcher.containsAll(functionCalls)) { | |||||
return true; | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
} |
@@ -0,0 +1,371 @@ | |||||
package depends.extractor.python.union; | |||||
import depends.entity.*; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.extractor.python.NameAliasImport; | |||||
import depends.extractor.python.PythonHandlerContext; | |||||
import depends.extractor.python.PythonParser.*; | |||||
import depends.extractor.python.PythonParserBaseListener; | |||||
import depends.extractor.IncludedFileLocator; | |||||
import depends.importtypes.FileImport; | |||||
import depends.relations.IBindingResolver; | |||||
import multilang.depends.util.file.FileUtil; | |||||
import org.antlr.v4.runtime.ParserRuleContext; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
public class PythonCodeListener extends PythonParserBaseListener{ | |||||
private final PythonHandlerContext context; | |||||
private final ExpressionUsage expressionUsage; | |||||
private final EntityRepo entityRepo; | |||||
private final IncludedFileLocator includeFileLocator; | |||||
private final PythonProcessor pythonProcessor; | |||||
private final IBindingResolver bindingResolver; | |||||
public PythonCodeListener(String fileFullPath, EntityRepo entityRepo, IBindingResolver bindingResolver, | |||||
IncludedFileLocator includeFileLocator, PythonProcessor pythonProcessor) { | |||||
this.context = new PythonHandlerContext(entityRepo, bindingResolver); | |||||
this.expressionUsage = new ExpressionUsage(context, entityRepo, bindingResolver); | |||||
FileEntity fileEntity = context.startFile(fileFullPath); | |||||
this.entityRepo = entityRepo; | |||||
this.includeFileLocator = includeFileLocator; | |||||
this.bindingResolver = bindingResolver; | |||||
this.pythonProcessor = pythonProcessor; | |||||
String dir = FileUtil.uniqFilePath(FileUtil.getLocatedDir(fileFullPath)); | |||||
if (entityRepo.getEntity(dir) == null) { | |||||
PackageEntity pacakgeEntity = new PackageEntity(dir, entityRepo.generateId()); | |||||
entityRepo.add(pacakgeEntity); | |||||
} | |||||
PackageEntity packageEntity = (PackageEntity) entityRepo.getEntity(dir); | |||||
String moduleName = fileEntity.getRawName().uniqName().substring(packageEntity.getRawName().uniqName().length() + 1); | |||||
if (moduleName.endsWith(".py")) | |||||
moduleName.substring(0, moduleName.length() - ".py".length()); | |||||
Entity.setParent(fileEntity, packageEntity); | |||||
packageEntity.addChild(FileUtil.getShortFileName(fileEntity.getRawName().uniqName()).replace(".py", ""), fileEntity); | |||||
} | |||||
@Override | |||||
public void enterImport_stmt(Import_stmtContext ctx) { | |||||
String moduleName = null; | |||||
for(Dotted_as_nameContext dotted_as_name:ctx.dotted_as_names().dotted_as_name()){ | |||||
moduleName = getName(dotted_as_name.dotted_name()); | |||||
String aliasName = moduleName; | |||||
if (dotted_as_name.name()!=null) { | |||||
aliasName = dotted_as_name.name().getText(); | |||||
} | |||||
List<String> fullNames = foundImportedModuleOrPackage(0,moduleName); | |||||
for (String fullName:fullNames) { | |||||
if (FileUtil.existFile(fullName) && !(FileUtil.isDirectory(fullName))) { | |||||
context.foundNewImport(new FileImport(fullName)); | |||||
} | |||||
context.foundNewImport(new NameAliasImport(fullName, entityRepo.getEntity(fullName), aliasName)); | |||||
} | |||||
} | |||||
super.enterImport_stmt(ctx); | |||||
} | |||||
@Override | |||||
public void enterFrom_stmt(From_stmtContext ctx) { | |||||
String moduleName = null; | |||||
if (ctx.dotted_name() != null) { | |||||
moduleName = ctx.dotted_name().getText(); | |||||
} | |||||
int prefixDotCount = getDotCounter(ctx); | |||||
List<String> fullNames = foundImportedModuleOrPackage(prefixDotCount, moduleName); | |||||
for (String fullName:fullNames) { | |||||
if (ctx.import_as_names() == null) {// import * | |||||
ContainerEntity moduleEntity = (ContainerEntity) (entityRepo.getEntity(fullName)); | |||||
if (moduleEntity != null) { | |||||
for (Entity child:moduleEntity.getChildren()) { | |||||
context.foundNewImport(new NameAliasImport(fullName, child, child.getRawName().uniqName())); | |||||
context.foundNewAlias(child.getRawName(), child); | |||||
} | |||||
if (moduleEntity instanceof PackageEntity) { | |||||
for (Entity file : moduleEntity.getChildren()) { | |||||
if (file instanceof FileEntity) { | |||||
String fileName = file.getRawName().uniqName().substring(fullName.length()); | |||||
context.foundNewImport(new NameAliasImport(file.getRawName().uniqName(), file, fileName)); | |||||
context.foundNewAlias(GenericName.build(FileUtil.getShortFileName(fileName).replace(".py", "")), file); | |||||
}else { | |||||
context.foundNewImport(new NameAliasImport(file.getRawName().uniqName(), file, file.getRawName().uniqName())); | |||||
context.foundNewAlias(GenericName.build(FileUtil.getShortFileName(file.getRawName().uniqName())), file); | |||||
} | |||||
} | |||||
} | |||||
if (moduleEntity instanceof FileEntity) { | |||||
String fileName = moduleEntity.getRawName().uniqName().substring(fullName.length()); | |||||
context.foundNewImport(new NameAliasImport(moduleEntity.getRawName().uniqName(), moduleEntity, fileName)); | |||||
} | |||||
} | |||||
} else { | |||||
for (Import_as_nameContext item : ctx.import_as_names().import_as_name()) { | |||||
String name = item.name(0).getText(); | |||||
String alias = name; | |||||
if (item.name().size() > 1) | |||||
alias = item.name(1).getText(); | |||||
if (FileUtil.isDirectory(fullName)) { | |||||
String fileName = fullName + File.separator + name + ".py"; | |||||
if (FileUtil.existFile(fileName) && !(FileUtil.isDirectory(fileName))) { | |||||
context.foundNewImport(new FileImport(fileName)); | |||||
} | |||||
} | |||||
if (FileUtil.existFile(fullName) && !(FileUtil.isDirectory(fullName))) { | |||||
context.foundNewImport(new FileImport(fullName)); | |||||
} | |||||
Entity itemEntity = bindingResolver.resolveName(entityRepo.getEntity(fullName), GenericName.build(name), true); | |||||
if (itemEntity != null) { | |||||
context.foundNewAlias(GenericName.build(alias), itemEntity); | |||||
context.foundNewImport(new NameAliasImport(itemEntity.getQualifiedName(), itemEntity, alias)); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
super.enterFrom_stmt(ctx); | |||||
} | |||||
private int getDotCounter(From_stmtContext ctx) { | |||||
int total = 0; | |||||
if (ctx.DOT()!=null){ | |||||
total = ctx.DOT().size(); | |||||
} | |||||
if (ctx.ELLIPSIS()!=null) { | |||||
total += ctx.ELLIPSIS().size()*3; | |||||
} | |||||
return total; | |||||
} | |||||
private List<String> foundImportedModuleOrPackage(int prefixDotCount, String originalName) { | |||||
String dir = FileUtil.getLocatedDir(context.currentFile().getRawName().uniqName()); | |||||
String preFix = ""; | |||||
for (int i = 0; i < prefixDotCount - 1; i++) { | |||||
preFix = preFix + ".." + File.separator; | |||||
} | |||||
dir = dir + File.separator + preFix; | |||||
String fullName = null; | |||||
if (originalName != null) { | |||||
String importedName = originalName.replace(".", File.separator); | |||||
fullName = includeFileLocator.uniqFileName(dir, importedName); | |||||
if (fullName == null) { | |||||
fullName = includeFileLocator.uniqFileName(dir, importedName + ".py"); | |||||
} | |||||
} else { | |||||
fullName = FileUtil.uniqFilePath(dir); | |||||
} | |||||
if (fullName != null) { | |||||
if (FileUtil.isDirectory(fullName)) { | |||||
if (!FileUtil.uniqFilePath(fullName).equals(FileUtil.uniqFilePath(dir))) { | |||||
File d = new File(fullName); | |||||
File[] files = d.listFiles(); | |||||
for (File file : files) { | |||||
if (!file.isDirectory()) { | |||||
if (file.getAbsolutePath().endsWith(".py")) { | |||||
visitIncludedFile(FileUtil.uniqFilePath(file.getAbsolutePath())); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} else { | |||||
visitIncludedFile(fullName); | |||||
} | |||||
} | |||||
ArrayList<String> r = new ArrayList<>(); | |||||
if (fullName==null) return r; | |||||
r.add(fullName); | |||||
if (FileUtil.existFile(fullName+File.separator + "__init__.py")) { | |||||
r.add( fullName+File.separator +"__init__.py"); | |||||
} | |||||
return r; | |||||
} | |||||
private void visitIncludedFile(String fullName) { | |||||
PythonFileParser importedParser = new PythonFileParser(entityRepo, includeFileLocator, bindingResolver, | |||||
pythonProcessor); | |||||
try { | |||||
importedParser.parse(fullName); | |||||
} catch (IOException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
} | |||||
@Override | |||||
public void enterFuncdef(FuncdefContext ctx) { | |||||
String functionName ="<empty>"; | |||||
String name = getName(ctx.name()); | |||||
if (name!=null) { | |||||
functionName = name; | |||||
} | |||||
FunctionEntity method = context.foundMethodDeclarator(functionName,ctx.getStart().getLine()); | |||||
if (ctx.typedargslist()!=null) { | |||||
List<String> parameters = getParameterList(ctx.typedargslist().def_parameters()); | |||||
for (String param : parameters) { | |||||
VarEntity paramEntity = context.addMethodParameter(param); | |||||
if (param.equals("self")) { | |||||
paramEntity.setType(context.currentType()); | |||||
} | |||||
} | |||||
} | |||||
super.enterFuncdef(ctx); | |||||
} | |||||
@Override | |||||
public void exitFuncdef(FuncdefContext ctx) { | |||||
context.exitLastedEntity(); | |||||
super.exitFuncdef(ctx); | |||||
} | |||||
@Override | |||||
public void enterClassdef(ClassdefContext ctx) { | |||||
String name = getName(ctx.name()); | |||||
TypeEntity type = context.foundNewType(name, ctx.getStart().getLine()); | |||||
List<String> baseClasses = getArgList(ctx.arglist()); | |||||
baseClasses.forEach(base -> type.addExtends(GenericName.build(base))); | |||||
super.enterClassdef(ctx); | |||||
} | |||||
@Override | |||||
public void exitClassdef(ClassdefContext ctx) { | |||||
context.exitLastedEntity(); | |||||
super.exitClassdef(ctx); | |||||
} | |||||
private List<String> getParameterList(List<Def_parametersContext> def_parameters) { | |||||
List<String> result = new ArrayList<>(); | |||||
for (Def_parametersContext params:def_parameters) { | |||||
for (Def_parameterContext param:params.def_parameter()) { | |||||
String p = getName( param.named_parameter().name()); | |||||
result.add(p); | |||||
} | |||||
} | |||||
return result; | |||||
} | |||||
private String getName(NameContext name) { | |||||
return name.getText(); | |||||
} | |||||
private String getName(Dotted_nameContext dotted_name) { | |||||
return dotted_name.getText(); | |||||
} | |||||
private String getDecoratedName(Class_or_func_def_stmtContext ctx) { | |||||
if (ctx.classdef()!=null) { | |||||
return getName(ctx.classdef().name()); | |||||
}else if (ctx.funcdef()!=null) { | |||||
return getName(ctx.funcdef().name()); | |||||
} | |||||
return null; | |||||
} | |||||
private List<String> getArgList(ArglistContext arglist) { | |||||
List<String> r = new ArrayList<>(); | |||||
if (arglist==null) return r; | |||||
if (arglist.argument() == null) return r; | |||||
if (arglist.argument().isEmpty()) return r; | |||||
arglist.argument().forEach(arg->r.add(arg.getText())); | |||||
return r; | |||||
} | |||||
/** | |||||
* class_or_func_def_stmt: decorator+ (classdef | funcdef); | |||||
*/ | |||||
@Override | |||||
public void exitClass_or_func_def_stmt(Class_or_func_def_stmtContext ctx) { | |||||
String decoratedName = getDecoratedName(ctx); | |||||
if (decoratedName!=null) { | |||||
Entity entity = context.foundEntityWithName(GenericName.build(decoratedName)); | |||||
entity.setLine(ctx.getStart().getLine()); | |||||
if (entity instanceof DecoratedEntity) { | |||||
for (DecoratorContext decorator: ctx.decorator()) { | |||||
String decoratorName = getName(decorator.dotted_name()); | |||||
((DecoratedEntity) entity).addAnnotation(GenericName.build(decoratorName)); | |||||
} | |||||
} | |||||
} | |||||
super.exitClass_or_func_def_stmt(ctx); | |||||
} | |||||
@Override | |||||
public void enterGlobal_stmt(Global_stmtContext ctx) { | |||||
for (NameContext name:ctx.name()){ | |||||
VarEntity var = context.foundGlobalVarDefinition(context.currentFile(), name.getText(),ctx.getStart().getLine()); | |||||
} | |||||
super.enterGlobal_stmt(ctx); | |||||
} | |||||
@Override | |||||
public void enterEveryRule(ParserRuleContext ctx) { | |||||
expressionUsage.foundExpression(ctx); | |||||
super.enterEveryRule(ctx); | |||||
} | |||||
@Override | |||||
public void enterExpr_stmt(Expr_stmtContext ctx) { | |||||
expressionUsage.startExpr(); | |||||
super.enterExpr_stmt(ctx); | |||||
} | |||||
@Override | |||||
public void exitExpr_stmt(Expr_stmtContext ctx) { | |||||
expressionUsage.stopExpr(); | |||||
super.exitExpr_stmt(ctx); | |||||
} | |||||
@Override | |||||
public void enterDel_stmt(Del_stmtContext ctx) { | |||||
expressionUsage.startExpr(); | |||||
super.enterDel_stmt(ctx); | |||||
} | |||||
@Override | |||||
public void exitDel_stmt(Del_stmtContext ctx) { | |||||
expressionUsage.stopExpr(); | |||||
super.exitDel_stmt(ctx); | |||||
} | |||||
@Override | |||||
public void enterReturn_stmt(Return_stmtContext ctx) { | |||||
expressionUsage.startExpr(); | |||||
super.enterReturn_stmt(ctx); | |||||
} | |||||
@Override | |||||
public void exitReturn_stmt(Return_stmtContext ctx) { | |||||
expressionUsage.stopExpr(); | |||||
super.exitReturn_stmt(ctx); | |||||
} | |||||
@Override | |||||
public void enterRaise_stmt(Raise_stmtContext ctx) { | |||||
expressionUsage.startExpr(); | |||||
super.enterRaise_stmt(ctx); | |||||
} | |||||
@Override | |||||
public void exitRaise_stmt(Raise_stmtContext ctx) { | |||||
expressionUsage.stopExpr(); | |||||
super.exitRaise_stmt(ctx); | |||||
} | |||||
@Override | |||||
public void enterYield_stmt(Yield_stmtContext ctx) { | |||||
expressionUsage.startExpr(); | |||||
super.enterYield_stmt(ctx); | |||||
} | |||||
@Override | |||||
public void exitYield_stmt(Yield_stmtContext ctx) { | |||||
expressionUsage.stopExpr(); | |||||
super.exitYield_stmt(ctx); | |||||
} | |||||
@Override | |||||
public void enterAssert_stmt(Assert_stmtContext ctx) { | |||||
expressionUsage.startExpr(); | |||||
super.enterAssert_stmt(ctx); | |||||
} | |||||
@Override | |||||
public void exitAssert_stmt(Assert_stmtContext ctx) { | |||||
expressionUsage.stopExpr(); | |||||
super.exitAssert_stmt(ctx); | |||||
} | |||||
} |
@@ -0,0 +1,284 @@ | |||||
package depends.extractor.python; | |||||
import depends.deptypes.DependencyType; | |||||
import depends.entity.Entity; | |||||
import depends.entity.FileEntity; | |||||
import depends.entity.FunctionEntity; | |||||
import depends.entity.MultiDeclareEntities; | |||||
import depends.extractor.python.union.PythonFileParser; | |||||
import multilang.depends.util.file.FileUtil; | |||||
import org.junit.Before; | |||||
import org.junit.Test; | |||||
import java.io.IOException; | |||||
public class PythonImportTest extends PythonParserTest { | |||||
@Before | |||||
public void setUp() { | |||||
super.init(); | |||||
} | |||||
@Test | |||||
public void should_parse_module_in_same_package() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/imported_a.py", | |||||
"./src/test/resources/python-code-examples/importing.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
PythonFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
Entity file = entityRepo.getEntity(FileUtil.uniqFilePath(srcs[1])); | |||||
this.assertContainsRelation(file, DependencyType.IMPORT,FileUtil.uniqFilePath(srcs[0])); | |||||
} | |||||
@Test | |||||
public void should_parse_module_in_same_package_order_robust() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/importing.py", | |||||
"./src/test/resources/python-code-examples/imported_a.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
PythonFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
Entity file = entityRepo.getEntity(FileUtil.uniqFilePath(srcs[0])); | |||||
this.assertContainsRelation(file, DependencyType.IMPORT,FileUtil.uniqFilePath(srcs[1])); | |||||
this.assertContainsRelation(file, DependencyType.CALL,withPackageName(srcs[0],"foo")); | |||||
} | |||||
@Test | |||||
public void should_parse_module_in_same_package_with_alias() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/importing_with_alias.py", | |||||
"./src/test/resources/python-code-examples/imported_a.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
PythonFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
Entity file = entityRepo.getEntity(FileUtil.uniqFilePath(srcs[0])); | |||||
this.assertContainsRelation(file, DependencyType.IMPORT,FileUtil.uniqFilePath(srcs[1])); | |||||
this.assertContainsRelation(file, DependencyType.CALL,withPackageName(srcs[0],"foo")); | |||||
} | |||||
@Test | |||||
public void should_parse_module_in_from_importing() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/from_importing.py", | |||||
"./src/test/resources/python-code-examples/imported_a.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
PythonFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
Entity file = entityRepo.getEntity(FileUtil.uniqFilePath(srcs[0])); | |||||
this.assertContainsRelation(file, DependencyType.CALL,withPackageName(srcs[0],"foo")); | |||||
this.assertContainsRelation(file, DependencyType.IMPORT,FileUtil.uniqFilePath(srcs[1])); | |||||
} | |||||
@Test | |||||
public void should_parse_module_in_from_importing_star() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/from_importing_star.py", | |||||
"./src/test/resources/python-code-examples/imported_a.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
PythonFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
Entity file = entityRepo.getEntity(FileUtil.uniqFilePath(srcs[0])); | |||||
this.assertContainsRelation(file, DependencyType.CALL,withPackageName(srcs[0],"foo")); | |||||
this.assertContainsRelation(file, DependencyType.IMPORT,FileUtil.uniqFilePath(srcs[1])); | |||||
} | |||||
@Test | |||||
public void should_parse_import_with_multi_dots() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/from_importing_multidot.py", | |||||
"./src/test/resources/python-code-examples/pkg/imported.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
PythonFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
Entity file = entityRepo.getEntity(FileUtil.uniqFilePath(srcs[0])); | |||||
this.assertContainsRelation(file, DependencyType.IMPORT,FileUtil.uniqFilePath(srcs[1])); | |||||
this.assertContainsRelation(file, DependencyType.CALL,withPackageName(srcs[1],"foo")); | |||||
} | |||||
@Test | |||||
public void should_parse_import_with_prefix_dots() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/import_with_dir/importing.py", | |||||
"./src/test/resources/python-code-examples/import_with_dir/imported_a.py", | |||||
"./src/test/resources/python-code-examples/import_with_dir/subdir/importing.py", | |||||
"./src/test/resources/python-code-examples/import_with_dir/subdir/importing2.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
PythonFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
this.assertContainsRelation(entityRepo.getEntity(FileUtil.uniqFilePath(srcs[0])), DependencyType.IMPORT,FileUtil.uniqFilePath(srcs[1])); | |||||
this.assertContainsRelation(entityRepo.getEntity(FileUtil.uniqFilePath(srcs[2])), DependencyType.IMPORT,FileUtil.uniqFilePath(srcs[1])); | |||||
this.assertContainsRelation(entityRepo.getEntity(FileUtil.uniqFilePath(srcs[3])), DependencyType.IMPORT,FileUtil.uniqFilePath(srcs[1])); | |||||
} | |||||
@Test | |||||
public void should_parse_import_with_prefix_dots2() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/import_with_dir/subdir/importing2.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
PythonFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
} | |||||
@Test | |||||
public void should_import_from_package__init__file() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/import_from_init/importing.py", | |||||
"./src/test/resources/python-code-examples/import_from_init/pkg/__init__.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
PythonFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
this.assertContainsRelation(entityRepo.getEntity(FileUtil.uniqFilePath(srcs[0])), DependencyType.IMPORT,FileUtil.uniqFilePath(srcs[1])); | |||||
} | |||||
@Test | |||||
public void should_not_bypass_import_in_same_dir() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/import_of_same_dir/pkg/importing.py", | |||||
"./src/test/resources/python-code-examples/import_of_same_dir/pkg/a.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
PythonFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
this.assertContainsRelation(entityRepo.getEntity(FileUtil.uniqFilePath(srcs[0])), DependencyType.IMPORT,FileUtil.uniqFilePath(srcs[1])); | |||||
} | |||||
@Test | |||||
public void should_resolve_symbols_of_imported_in_same_dir() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/import_of_same_dir/pkg/importing.py", | |||||
"./src/test/resources/python-code-examples/import_of_same_dir/pkg/a.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
PythonFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
FunctionEntity func = (FunctionEntity) entityRepo.getEntity(withPackageName(srcs[0],"test")); | |||||
this.assertContainsRelation(func, DependencyType.CALL, withPackageName(srcs[1],"foo")); | |||||
} | |||||
@Test | |||||
public void should_resolve_symbols_of_ducktyping() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/duck_typing/forest.py", | |||||
"./src/test/resources/python-code-examples/duck_typing/animals.py", | |||||
"./src/test/resources/python-code-examples/duck_typing/controller.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
PythonFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
MultiDeclareEntities funcs = (MultiDeclareEntities) entityRepo.getEntity(withPackageName(srcs[1],"in_the_forest")); | |||||
Entity func = funcs.getEntities().get(0); | |||||
this.assertContainsRelation(func, DependencyType.CALL, withPackageName(srcs[1],"Duck.quack")); | |||||
this.assertContainsRelation(func, DependencyType.CALL, withPackageName(srcs[1],"Doge.quack")); | |||||
this.assertContainsRelation(func, DependencyType.CALL, withPackageName(srcs[1],"Bird.quack")); | |||||
} | |||||
@Test | |||||
public void should_resolve_symbols_of_ducktyping2() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/duck_typing/animals.py", | |||||
"./src/test/resources/python-code-examples/duck_typing/forest.py", | |||||
"./src/test/resources/python-code-examples/duck_typing/controller.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
PythonFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
MultiDeclareEntities funcs = (MultiDeclareEntities) entityRepo.getEntity(withPackageName(srcs[1],"in_the_forest")); | |||||
Entity func = funcs.getEntities().get(0); | |||||
this.assertContainsRelation(func, DependencyType.CALL, withPackageName(srcs[0],"Duck.quack")); | |||||
this.assertContainsRelation(func, DependencyType.CALL, withPackageName(srcs[0],"Bird.quack")); | |||||
this.assertContainsRelation(func, DependencyType.CALL, withPackageName(srcs[0],"Doge.quack")); | |||||
} | |||||
@Test | |||||
public void should_resolve_imported_symbols() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/import_from_init/use_imported.py", | |||||
"./src/test/resources/python-code-examples/import_from_init/pkg/__init__.py", | |||||
"./src/test/resources/python-code-examples/import_from_init/pkg/core.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
PythonFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
FunctionEntity func = (FunctionEntity) entityRepo.getEntity(withPackageName(srcs[0],"bar")); | |||||
this.assertContainsRelation(func, DependencyType.CALL, withPackageName(srcs[2],"C")); | |||||
} | |||||
@Test | |||||
public void should_resolve_imported_vars() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/import_from_var/use_imported.py", | |||||
"./src/test/resources/python-code-examples/import_from_var/pkg/core.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
PythonFileParser parser = createParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
FileEntity f = (FileEntity) entityRepo.getEntity(FileUtil.uniqFilePath(srcs[0])); | |||||
this.assertContainsRelation(f, DependencyType.CALL, withPackageName(srcs[1],"Core.foo")); | |||||
} | |||||
} |
@@ -0,0 +1,186 @@ | |||||
package depends.extractor.python.union; | |||||
import org.antlr.v4.runtime.CharStream; | |||||
import org.antlr.v4.runtime.CommonToken; | |||||
import org.antlr.v4.runtime.Lexer; | |||||
import org.antlr.v4.runtime.Token; | |||||
import depends.extractor.python.PythonLexer; | |||||
import java.util.Stack; | |||||
public abstract class PythonLexerBase extends Lexer { | |||||
public static int TabSize = 8; | |||||
// The amount of opened braces, brackets and parenthesis. | |||||
private int _opened; | |||||
// The stack that keeps track of the indentation level. | |||||
private Stack<Integer> _indents = new Stack<>(); | |||||
// A circular buffer where extra tokens are pushed on (see the NEWLINE and WS lexer rules). | |||||
private int _firstTokensInd; | |||||
private int _lastTokenInd; | |||||
private Token[] _buffer = new Token[32]; | |||||
private Token _lastToken; | |||||
protected PythonLexerBase(CharStream input) { | |||||
super(input); | |||||
} | |||||
@Override | |||||
public void emit(Token token) { | |||||
super.setToken(token); | |||||
if (_buffer[_firstTokensInd] != null) | |||||
{ | |||||
_lastTokenInd = IncTokenInd(_lastTokenInd); | |||||
if (_lastTokenInd == _firstTokensInd) | |||||
{ | |||||
// Enlarge buffer | |||||
Token[] newArray = new Token[_buffer.length * 2]; | |||||
int destInd = newArray.length - (_buffer.length - _firstTokensInd); | |||||
System.arraycopy(_buffer, 0, newArray, 0, _firstTokensInd); | |||||
System.arraycopy(_buffer, _firstTokensInd, newArray, destInd, _buffer.length - _firstTokensInd); | |||||
_firstTokensInd = destInd; | |||||
_buffer = newArray; | |||||
} | |||||
} | |||||
_buffer[_lastTokenInd] = token; | |||||
_lastToken = token; | |||||
} | |||||
@Override | |||||
public Token nextToken() { | |||||
// Check if the end-of-file is ahead and there are still some DEDENTS expected. | |||||
if (_input.LA(1) == EOF && _indents.size() > 0) | |||||
{ | |||||
if (_buffer[_lastTokenInd] == null || _buffer[_lastTokenInd].getType() != PythonLexer.LINE_BREAK) | |||||
{ | |||||
// First emit an extra line break that serves as the end of the statement. | |||||
emit(PythonLexer.LINE_BREAK); | |||||
} | |||||
// Now emit as much DEDENT tokens as needed. | |||||
while (_indents.size() != 0) | |||||
{ | |||||
emit(PythonLexer.DEDENT); | |||||
_indents.pop(); | |||||
} | |||||
} | |||||
Token next = super.nextToken(); | |||||
if (_buffer[_firstTokensInd] == null) | |||||
{ | |||||
return next; | |||||
} | |||||
Token result = _buffer[_firstTokensInd]; | |||||
_buffer[_firstTokensInd] = null; | |||||
if (_firstTokensInd != _lastTokenInd) | |||||
{ | |||||
_firstTokensInd = IncTokenInd(_firstTokensInd); | |||||
} | |||||
return result; | |||||
} | |||||
protected void HandleNewLine() { | |||||
emit(PythonLexer.NEWLINE, HIDDEN, getText()); | |||||
char next = (char) _input.LA(1); | |||||
// Process whitespaces in HandleSpaces | |||||
if (next != ' ' && next != '\t' && IsNotNewLineOrComment(next)) | |||||
{ | |||||
ProcessNewLine(0); | |||||
} | |||||
} | |||||
protected void HandleSpaces() { | |||||
char next = (char) _input.LA(1); | |||||
if ((_lastToken == null || _lastToken.getType() == PythonLexer.NEWLINE) && IsNotNewLineOrComment(next)) | |||||
{ | |||||
// Calculates the indentation of the provided spaces, taking the | |||||
// following rules into account: | |||||
// | |||||
// "Tabs are replaced (from left to right) by one to eight spaces | |||||
// such that the total number of characters up to and including | |||||
// the replacement is a multiple of eight [...]" | |||||
// | |||||
// -- https://docs.python.org/3.1/reference/lexical_analysis.html#indentation | |||||
int indent = 0; | |||||
String text = getText(); | |||||
for (int i = 0; i < text.length(); i++) { | |||||
indent += text.charAt(i) == '\t' ? TabSize - indent % TabSize : 1; | |||||
} | |||||
ProcessNewLine(indent); | |||||
} | |||||
emit(PythonLexer.WS, HIDDEN, getText()); | |||||
} | |||||
protected void IncIndentLevel() { | |||||
_opened++; | |||||
} | |||||
protected void DecIndentLevel() { | |||||
if (_opened > 0) { | |||||
--_opened; | |||||
} | |||||
} | |||||
private boolean IsNotNewLineOrComment(char next) { | |||||
return _opened == 0 && next != '\r' && next != '\n' && next != '\f' && next != '#'; | |||||
} | |||||
private void ProcessNewLine(int indent) { | |||||
emit(PythonLexer.LINE_BREAK); | |||||
int previous = _indents.size() == 0 ? 0 : _indents.peek(); | |||||
if (indent > previous) | |||||
{ | |||||
_indents.push(indent); | |||||
emit(PythonLexer.INDENT); | |||||
} | |||||
else | |||||
{ | |||||
// Possibly emit more than 1 DEDENT token. | |||||
while (_indents.size() != 0 && _indents.peek() > indent) | |||||
{ | |||||
emit(PythonLexer.DEDENT); | |||||
_indents.pop(); | |||||
} | |||||
} | |||||
} | |||||
private int IncTokenInd(int ind) { | |||||
return (ind + 1) % _buffer.length; | |||||
} | |||||
private void emit(int tokenType) { | |||||
emit(tokenType, DEFAULT_TOKEN_CHANNEL, ""); | |||||
} | |||||
private void emit(int tokenType, int channel, String text) { | |||||
int charIndex = getCharIndex(); | |||||
CommonToken token = new CommonToken(_tokenFactorySourcePair, tokenType, channel, charIndex - text.length(), charIndex); | |||||
token.setLine(getLine()); | |||||
token.setCharPositionInLine(getCharPositionInLine()); | |||||
token.setText(text); | |||||
emit(token); | |||||
} | |||||
} | |||||
@@ -0,0 +1,118 @@ | |||||
package depends.extractor.python; | |||||
import depends.entity.CandidateTypes; | |||||
import depends.entity.FunctionEntity; | |||||
import depends.entity.TypeEntity; | |||||
import depends.entity.VarEntity; | |||||
import depends.extractor.FileParser; | |||||
import org.junit.Before; | |||||
import org.junit.Ignore; | |||||
import org.junit.Test; | |||||
import java.io.IOException; | |||||
import static org.junit.Assert.*; | |||||
public class PythonParameterTypeDedudceTest extends PythonParserTest { | |||||
@Before | |||||
public void setUp() { | |||||
super.init(); | |||||
} | |||||
@Test | |||||
public void test_deduce_type_of_parameter() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/deducetype_parameter.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
FileParser parser = createFileParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
String name = withPackageName(srcs[0],"test"); | |||||
FunctionEntity function = (FunctionEntity)( entityRepo.getEntity(name)); | |||||
VarEntity var = function.lookupVarLocally("t1"); | |||||
TypeEntity type = var.getType(); | |||||
assertTrue(type instanceof CandidateTypes); | |||||
assertEquals(2,((CandidateTypes)type).getCandidateTypes().size()); | |||||
} | |||||
@Test | |||||
public void test_deduce_type_of_builtIn() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/deducetype_builtin.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
FileParser parser = createFileParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
String name = withPackageName(srcs[0],"test"); | |||||
FunctionEntity function = (FunctionEntity)( entityRepo.getEntity(name)); | |||||
VarEntity var = function.lookupVarLocally("t1"); | |||||
TypeEntity type = var.getType(); | |||||
assertTrue(type == null); | |||||
} | |||||
@Test | |||||
public void test_deduce_type_of_builtIn_cannot_override() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/deducetype_builtin.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
FileParser parser = createFileParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
String name = withPackageName(srcs[0],"test2"); | |||||
FunctionEntity function = (FunctionEntity)( entityRepo.getEntity(name)); | |||||
VarEntity var = function.lookupVarLocally("t1"); | |||||
TypeEntity type = var.getType(); | |||||
assertTrue(type instanceof CandidateTypes); | |||||
assertEquals(1,((CandidateTypes)type).getCandidateTypes().size()); | |||||
} | |||||
@Ignore | |||||
public void test_deduce_type_of_non_param_var() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/deducetype_nonparam.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
FileParser parser = createFileParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
String name = withPackageName(srcs[0],"test"); | |||||
FunctionEntity function = (FunctionEntity)( entityRepo.getEntity(name)); | |||||
VarEntity var = function.lookupVarLocally("t2"); | |||||
TypeEntity type = var.getType(); | |||||
assertTrue(type instanceof CandidateTypes); | |||||
assertEquals(2,((CandidateTypes)type).getCandidateTypes().size()); | |||||
} | |||||
@Test | |||||
public void test_expression_count_should_be_() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/python-code-examples/expression_reload_issue_test.py", | |||||
}; | |||||
for (String src:srcs) { | |||||
FileParser parser = createFileParser(); | |||||
parser.parse(src); | |||||
} | |||||
resolveAllBindings(); | |||||
String name = withPackageName(srcs[0],"test_expression"); | |||||
FunctionEntity function = (FunctionEntity)( entityRepo.getEntity(name)); | |||||
assertNotNull(function); | |||||
} | |||||
} | |||||
@@ -0,0 +1,246 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.relations; | |||||
import depends.deptypes.DependencyType; | |||||
import depends.entity.*; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.extractor.AbstractLangProcessor; | |||||
import java.util.Collection; | |||||
import java.util.List; | |||||
import java.util.stream.Collectors; | |||||
public class RelationCounter { | |||||
private Collection<Entity> entities; | |||||
private IBindingResolver bindingResolver; | |||||
private EntityRepo repo; | |||||
private boolean callAsImpl; | |||||
private AbstractLangProcessor langProcessor; | |||||
public RelationCounter(EntityRepo repo, AbstractLangProcessor langProcessor, IBindingResolver bindingResolver) { | |||||
this.entities = repo.getFileEntities(); | |||||
this.bindingResolver = bindingResolver; | |||||
this.repo = repo; | |||||
this.callAsImpl = langProcessor.supportCallAsImpl(); | |||||
this.langProcessor = langProcessor; | |||||
} | |||||
public void computeRelations() { | |||||
entities.forEach(entity-> | |||||
computeRelationOf(entity)); | |||||
} | |||||
private void computeRelationOf(Entity entity) { | |||||
if (!entity.inScope()) | |||||
return; | |||||
if (entity instanceof FileEntity) { | |||||
computeImports((FileEntity)entity); | |||||
} | |||||
else if (entity instanceof FunctionEntity) { | |||||
computeFunctionRelations((FunctionEntity)entity); | |||||
} | |||||
else if (entity instanceof TypeEntity) { | |||||
computeTypeRelations((TypeEntity)entity); | |||||
} | |||||
if (entity instanceof ContainerEntity) { | |||||
computeContainerRelations((ContainerEntity)entity); | |||||
} | |||||
entity.getChildren().forEach(child->computeRelationOf(child)); | |||||
} | |||||
private void computeContainerRelations(ContainerEntity entity) { | |||||
for (VarEntity var:entity.getVars()) { | |||||
if (var.getType()!=null) | |||||
entity.addRelation(buildRelation(entity,DependencyType.CONTAIN,var.getType(),var.getLocation())); | |||||
for (Entity type:var.getResolvedTypeParameters()) { | |||||
var.addRelation(buildRelation(var, DependencyType.PARAMETER,type,type.getLocation())); | |||||
} | |||||
} | |||||
for (Entity type:entity.getResolvedAnnotations()) { | |||||
entity.addRelation(buildRelation(entity,DependencyType.ANNOTATION,type)); | |||||
} | |||||
for (Entity type:entity.getResolvedTypeParameters()) { | |||||
entity.addRelation(buildRelation(entity,DependencyType.USE,type)); | |||||
} | |||||
for (ContainerEntity mixin:entity.getResolvedMixins()) { | |||||
entity.addRelation(buildRelation(entity,DependencyType.MIXIN,mixin)); | |||||
} | |||||
entity.reloadExpression(repo); | |||||
if (!bindingResolver.isEagerExpressionResolve()) | |||||
{ | |||||
entity.resolveExpressions(bindingResolver); | |||||
} | |||||
for (Expression expression:entity.expressionList()){ | |||||
if (expression.isStatement()) { | |||||
continue; | |||||
} | |||||
Entity referredEntity = expression.getReferredEntity(); | |||||
addRelationFromExpression(entity, expression, referredEntity); | |||||
} | |||||
entity.clearExpressions(); | |||||
} | |||||
private void addRelationFromExpression(ContainerEntity entity, Expression expression, Entity referredEntity) { | |||||
if (referredEntity==null) { | |||||
return; | |||||
} | |||||
if (referredEntity.getId()<0){ | |||||
return; | |||||
} | |||||
if (referredEntity instanceof MultiDeclareEntities) { | |||||
for (Entity e:((MultiDeclareEntities)referredEntity).getEntities()) { | |||||
addRelationFromExpression(entity,expression,e); | |||||
} | |||||
return; | |||||
} | |||||
boolean matched = false; | |||||
if (expression.isCall()) { | |||||
/* if it is a FunctionEntityProto, add Relation to all Impl Entities*/ | |||||
if (callAsImpl && referredEntity instanceof FunctionEntityProto) { | |||||
if (entity.getAncestorOfType(FileEntity.class).getId().equals(referredEntity.getAncestorOfType(FileEntity.class).getId())){ | |||||
entity.addRelation(buildRelation(entity,DependencyType.CALL,referredEntity,expression.getLocation())); | |||||
}else { | |||||
Entity multiDeclare = repo.getEntity(referredEntity.getQualifiedName()); | |||||
if (multiDeclare instanceof MultiDeclareEntities) { | |||||
MultiDeclareEntities m = (MultiDeclareEntities) multiDeclare; | |||||
List<Entity> entities = m.getEntities().stream().filter(item -> (item instanceof FunctionEntityImpl)) | |||||
.collect(Collectors.toList()); | |||||
for (Entity e : entities) { | |||||
entity.addRelation(expression, buildRelation(entity, DependencyType.IMPLLINK, e, expression.getLocation())); | |||||
matched = true; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
entity.addRelation(buildRelation(entity,DependencyType.CALL,referredEntity,expression.getLocation())); | |||||
matched = true; | |||||
} | |||||
if (expression.isCreate()) { | |||||
entity.addRelation(buildRelation(entity,DependencyType.CREATE,referredEntity,expression.getLocation())); | |||||
matched = true; | |||||
} | |||||
if (expression.isThrow()) { | |||||
entity.addRelation(buildRelation(entity,DependencyType.THROW,referredEntity,expression.getLocation())); | |||||
matched = true; | |||||
} | |||||
if (expression.isCast()) { | |||||
entity.addRelation(buildRelation(entity,DependencyType.CAST,referredEntity,expression.getLocation())); | |||||
matched = true; | |||||
} | |||||
if (!matched) { | |||||
if (callAsImpl && repo.getEntity(referredEntity.getQualifiedName()) instanceof MultiDeclareEntities && | |||||
(referredEntity instanceof VarEntity ||referredEntity instanceof FunctionEntity)) { | |||||
if (entity.getAncestorOfType(FileEntity.class).getId().equals(referredEntity.getAncestorOfType(FileEntity.class).getId())){ | |||||
entity.addRelation(buildRelation(entity,DependencyType.USE,referredEntity,expression.getLocation())); | |||||
}else { | |||||
MultiDeclareEntities m = (MultiDeclareEntities) (repo.getEntity(referredEntity.getQualifiedName())); | |||||
for (Entity e : m.getEntities()) { | |||||
if (e == referredEntity) { | |||||
entity.addRelation(expression, buildRelation(entity, DependencyType.USE, e, expression.getLocation())); | |||||
} else { | |||||
entity.addRelation(expression, buildRelation(entity, DependencyType.IMPLLINK, e, expression.getLocation())); | |||||
} | |||||
matched = true; | |||||
} | |||||
} | |||||
} | |||||
else { | |||||
entity.addRelation(expression,buildRelation(entity,DependencyType.USE,referredEntity,expression.getLocation())); | |||||
} | |||||
} | |||||
} | |||||
private Relation buildRelation(Entity from, String type, Entity referredEntity) { | |||||
return buildRelation(from,type,referredEntity,from.getLocation()); | |||||
} | |||||
private Relation buildRelation(Entity from, String type, Entity referredEntity,Location location) { | |||||
if (referredEntity instanceof AliasEntity) { | |||||
if (from.getAncestorOfType(FileEntity.class).equals(referredEntity.getAncestorOfType(FileEntity.class))) { | |||||
AliasEntity alias = ((AliasEntity) referredEntity); | |||||
if (alias.deepResolve()!=null) { | |||||
referredEntity = alias.deepResolve(); | |||||
} | |||||
} | |||||
} | |||||
if (this.langProcessor==null) | |||||
return new Relation(type,referredEntity,location); | |||||
return new Relation(langProcessor.getRelationMapping(type),referredEntity,location); | |||||
} | |||||
private void computeTypeRelations(TypeEntity type) { | |||||
for (TypeEntity superType:type.getInheritedTypes()) { | |||||
type.addRelation(buildRelation(type,DependencyType.INHERIT,superType)); | |||||
} | |||||
for (TypeEntity interfaceType:type.getImplementedTypes()) { | |||||
type.addRelation(buildRelation(type,DependencyType.IMPLEMENT,interfaceType)); | |||||
} | |||||
} | |||||
private void computeFunctionRelations(FunctionEntity func) { | |||||
for (Entity returnType:func.getReturnTypes()) { | |||||
func.addRelation(buildRelation(func,DependencyType.RETURN,returnType.getActualReferTo())); | |||||
} | |||||
for (VarEntity parameter:func.getParameters()) { | |||||
if (parameter.getType()!=null) | |||||
func.addRelation(buildRelation(func,DependencyType.PARAMETER,parameter.getActualReferTo())); | |||||
} | |||||
for (Entity throwType:func.getThrowTypes()) { | |||||
func.addRelation(buildRelation(func,DependencyType.THROW,throwType)); | |||||
} | |||||
for (Entity type:func.getResolvedTypeParameters()) { | |||||
func.addRelation(buildRelation(func,DependencyType.PARAMETER,type)); | |||||
} | |||||
if (func instanceof FunctionEntityImpl) { | |||||
FunctionEntityImpl funcImpl = (FunctionEntityImpl)func; | |||||
if(funcImpl.getImplemented()!=null) { | |||||
func.addRelation(buildRelation(func,DependencyType.IMPLEMENT,funcImpl.getImplemented())); | |||||
} | |||||
} | |||||
} | |||||
private void computeImports(FileEntity file) { | |||||
Collection<Entity> imports = file.getImportedRelationEntities(); | |||||
if (imports==null) return; | |||||
for (Entity imported:imports) { | |||||
if (imported instanceof FileEntity) | |||||
{ | |||||
if (((FileEntity)imported).isInProjectScope()) | |||||
file.addRelation(buildRelation(file,DependencyType.IMPORT,imported)); | |||||
}else { | |||||
file.addRelation(buildRelation(file,DependencyType.IMPORT,imported)); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,105 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor.ruby; | |||||
import depends.entity.Entity; | |||||
import depends.entity.PackageEntity; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.extractor.FileParser; | |||||
import depends.extractor.HandlerContext; | |||||
import depends.extractor.IncludedFileLocator; | |||||
import depends.extractor.ParserCreator; | |||||
import depends.importtypes.FileImport; | |||||
import depends.relations.IBindingResolver; | |||||
import multilang.depends.util.file.FileUtil; | |||||
import java.util.Collection; | |||||
public class RubyHandlerContext extends HandlerContext { | |||||
private IncludedFileLocator includedFileLocator; | |||||
private ParserCreator parserCreator; | |||||
public RubyHandlerContext(EntityRepo entityRepo, | |||||
IncludedFileLocator includedFileLocator, | |||||
IBindingResolver bindingResolver, ParserCreator parserCreator) { | |||||
super(entityRepo, bindingResolver); | |||||
this.includedFileLocator = includedFileLocator; | |||||
this.parserCreator = parserCreator; | |||||
} | |||||
public Entity foundNamespace(String nampespaceName, Integer startLine) { | |||||
Entity parentEntity = currentFile(); | |||||
if (latestValidContainer()!=null) | |||||
parentEntity = latestValidContainer(); | |||||
PackageEntity pkgEntity = new PackageEntity(nampespaceName, parentEntity,idGenerator.generateId()); | |||||
entityRepo.add(pkgEntity); | |||||
entityStack.push(pkgEntity); | |||||
return pkgEntity; | |||||
} | |||||
public void processSpecialFuncCall(String methodName, Collection<String> params, Integer startLine) { | |||||
// Handle Import relation | |||||
if(methodName.equals("require") || methodName.equals("require_relative")) { | |||||
for (String importedFilename:params) { | |||||
if (!importedFilename.endsWith(".rb")) importedFilename = importedFilename + ".rb"; | |||||
String dir = FileUtil.getLocatedDir(currentFile().getRawName().uniqName()); | |||||
String inclFileName = includedFileLocator.uniqFileName(dir,importedFilename); | |||||
if (inclFileName==null) { | |||||
System.err.println("Warning: cannot found included file " + importedFilename ); | |||||
continue; | |||||
} | |||||
FileParser importedParser = parserCreator.createFileParser(); | |||||
try { | |||||
importedParser.parse(inclFileName); | |||||
} catch (Exception e) { | |||||
System.err.println("parsing error in "+inclFileName); | |||||
} | |||||
foundNewImport(new FileImport(inclFileName)); | |||||
} | |||||
} | |||||
// Handle Extend relation | |||||
else if (methodName.equals("extend")) { | |||||
for (String moduleName:params) { | |||||
foundExtends(moduleName); | |||||
} | |||||
} | |||||
// Handle mixin relation | |||||
else if (methodName.equals("include")) { | |||||
for (String moduleName:params) { | |||||
foundMixin(moduleName); | |||||
} | |||||
} | |||||
// attribute methods | |||||
else if (methodName.equals("attr_accessor")||methodName.equals("attr_writer")||methodName.equals("attr_reader")) { | |||||
for (String name:params) { | |||||
name = name.replace(":", ""); //remove symbol | |||||
foundMethodDeclarator(name,startLine); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,165 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.extractor.ruby.jruby; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import org.jrubyparser.ast.AssignableNode; | |||||
import org.jrubyparser.ast.CallNode; | |||||
import org.jrubyparser.ast.ClassVarAsgnNode; | |||||
import org.jrubyparser.ast.ClassVarDeclNode; | |||||
import org.jrubyparser.ast.Colon2Node; | |||||
import org.jrubyparser.ast.Colon3Node; | |||||
import org.jrubyparser.ast.ConstDeclNode; | |||||
import org.jrubyparser.ast.DAsgnNode; | |||||
import org.jrubyparser.ast.DefsNode; | |||||
import org.jrubyparser.ast.FCallNode; | |||||
import org.jrubyparser.ast.GlobalAsgnNode; | |||||
import org.jrubyparser.ast.INameNode; | |||||
import org.jrubyparser.ast.InstAsgnNode; | |||||
import org.jrubyparser.ast.ListNode; | |||||
import org.jrubyparser.ast.LocalAsgnNode; | |||||
import org.jrubyparser.ast.MultipleAsgnNode; | |||||
import org.jrubyparser.ast.Node; | |||||
import org.jrubyparser.ast.UnaryCallNode; | |||||
import org.jrubyparser.ast.VCallNode; | |||||
import depends.entity.ContainerEntity; | |||||
import depends.extractor.ruby.RubyBuiltInType; | |||||
import depends.extractor.ruby.RubyHandlerContext; | |||||
public class RubyParserHelper { | |||||
private static RubyParserHelper inst = new RubyParserHelper(); | |||||
public static RubyParserHelper getInst() { | |||||
return inst; | |||||
} | |||||
private RubyBuiltInType buildInType; | |||||
private RubyParserHelper() { | |||||
this.buildInType = new RubyBuiltInType(); | |||||
} | |||||
public String getName(Node node) { | |||||
String name = ""; | |||||
if (node instanceof INameNode) { | |||||
name = ((INameNode)node).getName(); | |||||
if (node instanceof Colon2Node) { | |||||
Node left = ((Colon2Node)node).getLeftNode(); | |||||
if (left!=null) { | |||||
name = getName(left) + "."+name; | |||||
} | |||||
}else if (node instanceof Colon3Node) { | |||||
name = "."+name; | |||||
} | |||||
} | |||||
return name.length()>0?name:null; | |||||
} | |||||
public boolean isFunctionCall(Node ctx) { | |||||
return ctx instanceof CallNode || | |||||
ctx instanceof FCallNode || | |||||
ctx instanceof UnaryCallNode || | |||||
ctx instanceof VCallNode; | |||||
} | |||||
public List<String> getName(AssignableNode ctx) { | |||||
List<String> names = new ArrayList<>(); | |||||
if (ctx instanceof ClassVarAsgnNode) { | |||||
names.add(((ClassVarAsgnNode)ctx).getName()); | |||||
}else if (ctx instanceof ClassVarDeclNode) { | |||||
names.add(((ClassVarDeclNode)ctx).getName()); | |||||
}else if (ctx instanceof ConstDeclNode) { | |||||
names.add(((ConstDeclNode)ctx).getName()); | |||||
}else if (ctx instanceof DAsgnNode) { | |||||
names.add(((DAsgnNode)ctx).getName()); | |||||
}else if (ctx instanceof GlobalAsgnNode) { | |||||
names.add(((GlobalAsgnNode)ctx).getName()); | |||||
}else if (ctx instanceof InstAsgnNode) { | |||||
names.add(((InstAsgnNode)ctx).getName()); | |||||
}else if (ctx instanceof LocalAsgnNode) { | |||||
names.add(((LocalAsgnNode)ctx).getName()); | |||||
}else if (ctx instanceof MultipleAsgnNode) { | |||||
ListNode pre = ((MultipleAsgnNode)ctx).getPre(); | |||||
if (pre!=null) { | |||||
//TODO: support multiple assignment | |||||
} | |||||
} | |||||
return names; | |||||
} | |||||
public String getReciever(Node ctx) { | |||||
Node receiver = null; | |||||
if (ctx instanceof CallNode) { | |||||
receiver = ((CallNode)ctx).getReceiver(); | |||||
}else if (ctx instanceof DefsNode) { | |||||
receiver = ((DefsNode)ctx).getReceiver(); | |||||
} | |||||
if (receiver==null) { | |||||
return null; | |||||
} | |||||
if (receiver instanceof INameNode) { | |||||
return this.getName(receiver); | |||||
} | |||||
return null; | |||||
} | |||||
public ContainerEntity getScopeOfVar(AssignableNode node, RubyHandlerContext context) { | |||||
if (node instanceof LocalAsgnNode) return context.lastContainer(); | |||||
if (node instanceof InstAsgnNode) return context.currentType(); | |||||
if (node instanceof ClassVarAsgnNode) return context.currentType(); | |||||
if (node instanceof GlobalAsgnNode) return context.globalScope(); | |||||
if (node instanceof DAsgnNode) return context.lastContainer(); | |||||
return context.lastContainer(); | |||||
} | |||||
public boolean isArithMeticOperator(String name) { | |||||
return name.equals("+") || | |||||
name.equals("-") || | |||||
name.equals("*") || | |||||
name.equals("/") || | |||||
name.equals("**") || | |||||
name.equals("%") || | |||||
name.equals("&") || | |||||
name.equals("<") || | |||||
name.equals("<=") || | |||||
name.equals(">") || | |||||
name.equals(">=") || | |||||
name.equals("==") || | |||||
name.equals("!=") || | |||||
name.equals("===") || | |||||
name.equals("<<") || | |||||
name.equals(">>") || | |||||
name.equals("~") || | |||||
name.equals("!") || | |||||
name.equals("^"); | |||||
} | |||||
public boolean isCommonOperator(String name) { | |||||
return this.buildInType.isBuildInMethod(name); | |||||
} | |||||
} |
@@ -0,0 +1,172 @@ | |||||
package depends.extractor.ruby; | |||||
import static org.junit.Assert.assertEquals; | |||||
import java.io.IOException; | |||||
import org.junit.Before; | |||||
import org.junit.Test; | |||||
import depends.entity.ContainerEntity; | |||||
import depends.entity.FunctionEntity; | |||||
import depends.entity.TypeEntity; | |||||
import depends.entity.repo.EntityRepo; | |||||
import depends.extractor.FileParser; | |||||
public class RubyVarTest extends RubyParserTest { | |||||
@Before | |||||
public void setUp() { | |||||
super.init(); | |||||
} | |||||
@Test | |||||
public void test_parameter_should_be_created() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/ruby-code-examples/auto_var.rb", | |||||
}; | |||||
for (String src:srcs) { | |||||
FileParser parser = createFileParser(); | |||||
parser.parse(src); | |||||
} | |||||
FunctionEntity function = (FunctionEntity)(entityRepo.getEntity("method1")); | |||||
assertEquals(1,function.getParameters().size()); | |||||
assertContainsParametersWithRawName(function, "param1"); | |||||
} | |||||
@Test | |||||
public void test_var_should_be_created_if_not_declared() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/ruby-code-examples/auto_var.rb", | |||||
}; | |||||
for (String src:srcs) { | |||||
FileParser parser = createFileParser(); | |||||
parser.parse(src); | |||||
} | |||||
FunctionEntity function = (FunctionEntity)(entityRepo.getEntity("method_with_local_var")); | |||||
assertEquals(1,function.getVars().size()); | |||||
assertContainsVarWithRawName(function, "var_1"); | |||||
} | |||||
@Test | |||||
public void test_var_should_only_create_once() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/ruby-code-examples/auto_var.rb", | |||||
}; | |||||
for (String src:srcs) { | |||||
FileParser parser = createFileParser(); | |||||
parser.parse(src); | |||||
} | |||||
FunctionEntity function = (FunctionEntity)(entityRepo.getEntity("method_with_local_var_2times")); | |||||
assertEquals(1,function.getVars().size()); | |||||
} | |||||
@Test | |||||
public void test_var_should_not_be_created_if_it_actually_parameter() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/ruby-code-examples/auto_var.rb", | |||||
}; | |||||
for (String src:srcs) { | |||||
FileParser parser = createFileParser(); | |||||
parser.parse(src); | |||||
} | |||||
FunctionEntity function = (FunctionEntity)(entityRepo.getEntity("method_without_local_var_and_param")); | |||||
assertEquals(0,function.getVars().size()); | |||||
} | |||||
@Test | |||||
public void test_var_should_not_be_created_if_it_actually_a_file_level_var() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/ruby-code-examples/auto_var.rb", | |||||
}; | |||||
for (String src:srcs) { | |||||
FileParser parser = createFileParser(); | |||||
parser.parse(src); | |||||
} | |||||
FunctionEntity function = (FunctionEntity)(entityRepo.getEntity("method_access_file_level_var")); | |||||
assertEquals(0,function.getVars().size()); | |||||
} | |||||
@Test | |||||
public void test_var_should_not_be_created_with_a_lot_of_levels() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/ruby-code-examples/auto_var.rb", | |||||
}; | |||||
for (String src:srcs) { | |||||
FileParser parser = createFileParser(); | |||||
parser.parse(src); | |||||
} | |||||
FunctionEntity function = (FunctionEntity)(entityRepo.getEntity("M.C.method")); | |||||
assertEquals(1,function.getVars().size()); | |||||
} | |||||
@Test | |||||
public void test_var_should_not_be_created_not_in_scope() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/ruby-code-examples/auto_var.rb", | |||||
}; | |||||
for (String src:srcs) { | |||||
FileParser parser = createFileParser(); | |||||
parser.parse(src); | |||||
} | |||||
FunctionEntity function = (FunctionEntity)(entityRepo.getEntity("M.C.method2")); | |||||
assertEquals(1,function.getVars().size()); | |||||
} | |||||
@Test | |||||
public void test_var_should_created_at_class_level() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/ruby-code-examples/auto_var.rb", | |||||
}; | |||||
for (String src:srcs) { | |||||
FileParser parser = createFileParser(); | |||||
parser.parse(src); | |||||
} | |||||
TypeEntity c = (TypeEntity)(entityRepo.getEntity("M.C")); | |||||
assertEquals(3,c.getVars().size()); | |||||
assertContainsVarWithRawName(c,"class_level_var"); | |||||
assertContainsVarWithRawName(c,"class_var"); | |||||
assertContainsVarWithRawName(c,"instance_var"); | |||||
} | |||||
@Test | |||||
public void test_var_in_block() throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/ruby-code-examples/auto_var.rb", | |||||
}; | |||||
for (String src:srcs) { | |||||
FileParser parser = createFileParser(); | |||||
parser.parse(src); | |||||
} | |||||
ContainerEntity c = (ContainerEntity)(entityRepo.getEntity("Block")); | |||||
assertEquals(1,c.getVars().size()); | |||||
assertContainsVarWithRawName(c,"a"); | |||||
} | |||||
@Test | |||||
public void test_global_var()throws IOException { | |||||
String[] srcs = new String[] { | |||||
"./src/test/resources/ruby-code-examples/auto_var.rb", | |||||
}; | |||||
for (String src:srcs) { | |||||
FileParser parser = createFileParser(); | |||||
parser.parse(src); | |||||
} | |||||
ContainerEntity c = (ContainerEntity)(entityRepo.getEntity(EntityRepo.GLOBAL_SCOPE_NAME)); | |||||
assertEquals(1,c.getVars().size()); | |||||
assertContainsVarWithRawName(c,"global_var"); | |||||
} | |||||
} | |||||
@@ -0,0 +1,204 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.entity; | |||||
import depends.relations.IBindingResolver; | |||||
import java.util.ArrayList; | |||||
import java.util.Collection; | |||||
import java.util.HashSet; | |||||
public class TypeEntity extends ContainerEntity { | |||||
static final public TypeEntity buildInType = new TypeEntity(GenericName.build("built-in"), null, -1); | |||||
static final public TypeEntity genericParameterType = new TypeEntity(GenericName.build("T"), null, -3); | |||||
Collection<TypeEntity> inheritedTypes = new ArrayList<>(); | |||||
Collection<TypeEntity> implementedTypes = new ArrayList<>(); | |||||
Collection<GenericName> inhertedTypeIdentifiers; | |||||
Collection<GenericName> implementedIdentifiers; | |||||
TypeEntity inheritedType; | |||||
public TypeEntity() {} | |||||
public TypeEntity(GenericName simpleName, Entity parent, Integer id) { | |||||
super(simpleName, parent, id); | |||||
inhertedTypeIdentifiers = new ArrayList<>(); | |||||
implementedIdentifiers = new ArrayList<>(); | |||||
} | |||||
@Override | |||||
public void inferLocalLevelEntities(IBindingResolver bindingResolver) { | |||||
inheritedTypes = new ArrayList<>(); | |||||
Collection<Entity> r = identiferToEntities(bindingResolver, this.inhertedTypeIdentifiers); | |||||
if (r!=null) { | |||||
r.forEach(item -> { | |||||
Entity typeItem = getTypeEntity(item); | |||||
if (typeItem !=null) { | |||||
inheritedTypes.add((TypeEntity) typeItem); | |||||
}else { | |||||
System.err.println(item.getRawName() + " expected a type, but actually it is "+ item.getClass().getSimpleName()); | |||||
} | |||||
}); | |||||
} | |||||
inheritedTypes.remove(this); | |||||
implementedTypes = new ArrayList<>(); | |||||
r = identiferToEntities(bindingResolver, this.implementedIdentifiers); | |||||
if (r!=null) { | |||||
r.forEach(item -> { | |||||
Entity typeItem = getTypeEntity(item); | |||||
if (typeItem !=null) { | |||||
implementedTypes.add((TypeEntity) typeItem); | |||||
}else { | |||||
System.err.println(item.getRawName() + " expected a type, but actually it is "+ item.getClass().getSimpleName()); | |||||
} | |||||
}); | |||||
} | |||||
implementedTypes.remove(this); | |||||
if (inheritedTypes.size() > 0) | |||||
inheritedType = inheritedTypes.iterator().next(); | |||||
super.inferLocalLevelEntities(bindingResolver); | |||||
} | |||||
private Entity getTypeEntity(Entity item) { | |||||
if (item==null) return null; | |||||
if (item instanceof TypeEntity) return item; | |||||
if (item instanceof MultiDeclareEntities) return ((MultiDeclareEntities)item).getType(); | |||||
if (item instanceof AliasEntity) return item.getType(); | |||||
return null; | |||||
} | |||||
public void addImplements(GenericName typeName) { | |||||
if (typeName==null) { | |||||
return; | |||||
} | |||||
if (typeName.equals(this.getRawName())) | |||||
return; | |||||
if (implementedIdentifiers.contains(typeName)) | |||||
return; | |||||
if (typeName.equals(this.rawName)) | |||||
return; | |||||
this.implementedIdentifiers.add(typeName); | |||||
} | |||||
public void addExtends(GenericName typeName) { | |||||
if (typeName==null) { | |||||
return; | |||||
} | |||||
if (typeName.equals(this.getRawName())) | |||||
return; | |||||
if (inhertedTypeIdentifiers.contains(typeName)) | |||||
return; | |||||
if (typeName.equals(this.rawName)) | |||||
return; | |||||
this.inhertedTypeIdentifiers.add(typeName); | |||||
} | |||||
public Collection<TypeEntity> getInheritedTypes() { | |||||
return inheritedTypes; | |||||
} | |||||
public Collection<TypeEntity> getImplementedTypes() { | |||||
return implementedTypes; | |||||
} | |||||
public TypeEntity getInheritedType() { | |||||
return inheritedType; | |||||
} | |||||
@Override | |||||
public FunctionEntity lookupFunctionLocally(GenericName functionName) { | |||||
Collection<TypeEntity> searchedTypes = new ArrayList<>(); | |||||
return lookupFunctionLocally(functionName,searchedTypes); | |||||
} | |||||
private FunctionEntity lookupFunctionLocally(GenericName functionName, Collection<TypeEntity> searched) { | |||||
if (searched.contains(this)) return null; | |||||
searched.add(this); | |||||
FunctionEntity func = super.lookupFunctionLocally(functionName); | |||||
if (func != null) | |||||
return func; | |||||
for (TypeEntity inhertedType : getInheritedTypes()) { | |||||
func = inhertedType.lookupFunctionLocally(functionName, searched); | |||||
if (func != null) | |||||
break; | |||||
} | |||||
if (func != null) | |||||
return func; | |||||
for (TypeEntity implType : getImplementedTypes()) { | |||||
func = implType.lookupFunctionLocally(functionName,searched); | |||||
if (func != null) | |||||
break; | |||||
} | |||||
return func; | |||||
} | |||||
@Override | |||||
public VarEntity lookupVarLocally(GenericName varName) { | |||||
Collection<TypeEntity> searchedTypes = new ArrayList<>(); | |||||
return lookupVarLocally(varName,searchedTypes); | |||||
} | |||||
private VarEntity lookupVarLocally(GenericName varName, Collection<TypeEntity> searched) { | |||||
if (searched.contains(this)) return null; | |||||
searched.add(this); | |||||
VarEntity var = super.lookupVarLocally(varName); | |||||
if (var != null) | |||||
return var; | |||||
for (TypeEntity inhertedType : getInheritedTypes()) { | |||||
var = inhertedType.lookupVarLocally(varName,searched); | |||||
if (var != null) | |||||
break; | |||||
} | |||||
if (var != null) | |||||
return var; | |||||
for (TypeEntity implType : getImplementedTypes()) { | |||||
var = implType.lookupVarLocally(varName,searched); | |||||
if (var != null) | |||||
break; | |||||
} | |||||
return var; | |||||
} | |||||
@Override | |||||
public TypeEntity getType() { | |||||
return this; | |||||
} | |||||
@Override | |||||
public Entity getByName(String name, HashSet<Entity> searched) { | |||||
Entity entity = super.getByName(name, searched); | |||||
if (entity!=null) | |||||
return entity; | |||||
for (TypeEntity child:getInheritedTypes()) { | |||||
if (searched.contains(child)) continue; | |||||
entity = child.getByName(name, searched); | |||||
if (entity!=null) return entity; | |||||
} | |||||
for (TypeEntity child:getImplementedTypes()) { | |||||
if (searched.contains(child)) continue; | |||||
entity = child.getByName(name,searched); | |||||
if (entity!=null) return entity; | |||||
} | |||||
return null; | |||||
} | |||||
} |
@@ -0,0 +1,96 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.format.detail; | |||||
import java.io.File; | |||||
import java.io.FileNotFoundException; | |||||
import java.io.PrintWriter; | |||||
import java.util.HashSet; | |||||
import java.util.Set; | |||||
import java.util.TreeMap; | |||||
import com.fasterxml.jackson.annotation.JsonInclude.Include; | |||||
import com.fasterxml.jackson.databind.ObjectMapper; | |||||
import com.fasterxml.jackson.databind.SerializationFeature; | |||||
import depends.extractor.UnsolvedBindings; | |||||
import multilang.depends.util.file.strip.LeadingNameStripper; | |||||
public class UnsolvedSymbolDumper{ | |||||
private Set<UnsolvedBindings> unsolved; | |||||
private String name; | |||||
private String outputDir; | |||||
private LeadingNameStripper leadingNameStripper; | |||||
public UnsolvedSymbolDumper(Set<UnsolvedBindings> unsolved, String name, String outputDir, LeadingNameStripper leadingNameStripper) { | |||||
this.unsolved = unsolved; | |||||
this.name = name; | |||||
this.outputDir = outputDir; | |||||
this.leadingNameStripper = leadingNameStripper; | |||||
} | |||||
public void output() { | |||||
outputDetail(); | |||||
outputGrouped(); | |||||
} | |||||
private void outputGrouped() { | |||||
TreeMap<String, Set<String>> grouped = new TreeMap<String, Set<String>>(); | |||||
for (UnsolvedBindings symbol: unsolved) { | |||||
String depended = symbol.getRawName(); | |||||
String from = leadingNameStripper.stripFilename(symbol.getSourceDisplay()); | |||||
Set<String> list = grouped.get(depended); | |||||
if (list==null) { | |||||
list = new HashSet<>(); | |||||
grouped.put(depended, list); | |||||
} | |||||
list.add(from); | |||||
} | |||||
ObjectMapper om = new ObjectMapper(); | |||||
om.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false); | |||||
om.configure(SerializationFeature.INDENT_OUTPUT, true); | |||||
om.setSerializationInclusion(Include.NON_NULL); | |||||
try { | |||||
om.writerWithDefaultPrettyPrinter().writeValue(new File(outputDir + File.separator + name +"-PotentialExternalDependencies.json"), grouped); | |||||
} catch (Exception e) { | |||||
e.printStackTrace(); | |||||
} | |||||
} | |||||
private void outputDetail() { | |||||
PrintWriter writer; | |||||
try { | |||||
writer = new PrintWriter(outputDir + File.separator + name +"-PotentialExternalDependencies.txt"); | |||||
for (UnsolvedBindings symbol: unsolved) { | |||||
String source = leadingNameStripper.stripFilename(symbol.getSourceDisplay()); | |||||
writer.println(""+symbol.getRawName()+", "+source); | |||||
} | |||||
writer.close(); | |||||
} catch (FileNotFoundException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,106 @@ | |||||
/* | |||||
MIT License | |||||
Copyright (c) 2018-2019 Gang ZHANG | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. | |||||
*/ | |||||
package depends.entity; | |||||
import depends.relations.IBindingResolver; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
public class VarEntity extends ContainerEntity { | |||||
private GenericName rawType; | |||||
private TypeEntity type; | |||||
private List<FunctionCall> functionCalls; | |||||
public VarEntity() { | |||||
} | |||||
public VarEntity(GenericName simpleName, GenericName rawType, Entity parent, int id) { | |||||
super(simpleName, parent,id); | |||||
this.rawType = rawType; | |||||
} | |||||
public void setRawType(GenericName rawType) { | |||||
this.rawType =rawType; | |||||
} | |||||
public GenericName getRawType() { | |||||
return rawType; | |||||
} | |||||
@Override | |||||
public TypeEntity getType() { | |||||
return type; | |||||
} | |||||
public void setType(TypeEntity type) { | |||||
this.type = type; | |||||
} | |||||
@Override | |||||
public void inferLocalLevelEntities(IBindingResolver bindingResolver) { | |||||
super.inferLocalLevelEntities(bindingResolver); | |||||
Entity entity = bindingResolver.resolveName(this, rawType, true); | |||||
if (entity!=null) { | |||||
this.setActualReferTo(entity); | |||||
type = entity.getType(); | |||||
if (type==null) { | |||||
if (((ContainerEntity)getParent()).isGenericTypeParameter(rawType)) { | |||||
type = TypeEntity.genericParameterType; | |||||
} | |||||
} | |||||
} | |||||
if (type==null) { | |||||
fillCandidateTypes(bindingResolver); | |||||
} | |||||
} | |||||
public List<FunctionCall> getCalledFunctions() { | |||||
if (this.functionCalls!=null) | |||||
return functionCalls; | |||||
return new ArrayList<>(); | |||||
} | |||||
public void addFunctionCall(GenericName fname) { | |||||
if (this.functionCalls==null) | |||||
{ | |||||
functionCalls = new ArrayList<>(); | |||||
} | |||||
this.functionCalls.add(new FunctionCall(fname)); | |||||
} | |||||
public void fillCandidateTypes(IBindingResolver bindingResolver) { | |||||
if (!bindingResolver.isEagerExpressionResolve()) return; | |||||
if (type!=null && !(type instanceof CandidateTypes)) return ; //it is a strong type lang, do not need deduce candidate types | |||||
if (functionCalls==null) return; | |||||
if (functionCalls.size()==0) return; //no information avaliable for type deduction | |||||
if (this.rawType==null) { | |||||
List<TypeEntity> candidateTypes = bindingResolver.calculateCandidateTypes(this,this.functionCalls); | |||||
if (candidateTypes.size()>0) { | |||||
this.type = new CandidateTypes(candidateTypes, bindingResolver.getRepo().generateId()); | |||||
bindingResolver.getRepo().add(this.type); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,52 @@ | |||||
package com.educoder.bridge.utils; | |||||
import org.apache.commons.codec.binary.Base64; | |||||
import java.nio.charset.StandardCharsets; | |||||
/** | |||||
* Created by guange on 23/02/2017. | |||||
*/ | |||||
public class Base64Util { | |||||
/** | |||||
* base64编码 | |||||
* | |||||
* @param code | |||||
* @return | |||||
*/ | |||||
public static String encode(String code) { | |||||
byte[] encode = Base64.encodeBase64URLSafe(code.getBytes(StandardCharsets.UTF_8)); | |||||
return new String(encode, StandardCharsets.UTF_8); | |||||
} | |||||
public static byte[] encodeBytes(byte[] codes) { | |||||
return Base64.encodeBase64(codes); | |||||
} | |||||
/** | |||||
* base64解码 | |||||
* | |||||
* @param code | |||||
* @return | |||||
*/ | |||||
public static String decode(String code) { | |||||
byte[] decode = Base64.decodeBase64(code); | |||||
return new String(decode, StandardCharsets.UTF_8); | |||||
} | |||||
/** | |||||
* base64再解码,把原本的非URL safe编码转换为URL safe编码 | |||||
* | |||||
* @param code | |||||
* @return | |||||
*/ | |||||
public static String reencode(String code) { | |||||
String str = decode(code); | |||||
str = str.replace("\n", "\r\n"); | |||||
return encode(str); | |||||
} | |||||
} |
@@ -0,0 +1,42 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<beans xmlns="http://www.springframework.org/schema/beans" | |||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xmlns:context="http://www.springframework.org/schema/context" | |||||
xmlns:websocket="http://www.springframework.org/schema/websocket" | |||||
xmlns:aop="http://www.springframework.org/schema/aop" | |||||
xsi:schemaLocation="http://www.springframework.org/schema/beans | |||||
http://www.springframework.org/schema/beans/spring-beans.xsd | |||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd | |||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd | |||||
http://www.springframework.org/schema/websocket | |||||
http://www.springframework.org/schema/websocket/spring-websocket.xsd"> | |||||
<aop:aspectj-autoproxy/> | |||||
<context:component-scan base-package="com.educoder.bridge.controller"/> | |||||
<context:component-scan base-package="com.educoder.bridge.service"/> | |||||
<context:component-scan base-package="com.educoder.bridge.handler"/> | |||||
<!-- freemaker配置 --> | |||||
<bean id="freemarkerConfig" | |||||
class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> | |||||
<property name="templateLoaderPath" value="/WEB-INF/pages/" /> | |||||
<property name="freemarkerSettings"> | |||||
<props> | |||||
<prop key="template_update_delay">0</prop> | |||||
<prop key="default_encoding">UTF-8</prop> | |||||
<prop key="number_format">0.##########</prop> | |||||
<prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop> | |||||
<prop key="classic_compatible">true</prop> | |||||
<prop key="template_exception_handler">ignore</prop> | |||||
</props> | |||||
</property> | |||||
</bean> | |||||
<!--注册消息处理器,指定WebsshHandler处理消息,并将/ws映射到其中--> | |||||
<websocket:handlers allowed-origins="*"> | |||||
<websocket:mapping path="/ws" handler="websshHandler"/> | |||||
</websocket:handlers> | |||||
<bean id="websshHandler" class="com.educoder.bridge.handler.WebsshHandler"/> | |||||
</beans> |
@@ -0,0 +1,47 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<configuration> | |||||
<property name="log_path" value="/home/ww/test/tomcat-test/logs/"/> | |||||
<!-- 打印在标准控制台 --> | |||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | |||||
<encoder> | |||||
<!--格式化输出:%d:时间,%thread:线程名,%-5level:级别从左显示5个字符宽度, | |||||
%logger{50}:输出日志的类, 50代表包名加类名的总长度限制, %M 方法名 %L 行号 %msg:日志消息,%n是换行符--> | |||||
<pattern>%d{MM-dd HH:mm:ss} [%thread] %-5level %logger{30} %M %L - %msg%n</pattern> | |||||
</encoder> | |||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> | |||||
<level>DEBUG</level> | |||||
</filter> | |||||
</appender> | |||||
<!-- 错误信息 --> | |||||
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"> | |||||
<Encoding>UTF-8</Encoding> | |||||
<encoder> | |||||
<pattern>%d{MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{30} %M - %msg%n%L</pattern> | |||||
</encoder> | |||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> | |||||
<level>ERROR</level> | |||||
</filter> | |||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | |||||
<fileNamePattern>${log_path}error.%d{MM-dd}.log</fileNamePattern> | |||||
</rollingPolicy> | |||||
</appender> | |||||
<!-- 屏蔽框架输出 --> | |||||
<logger name="org.slf4j" level="ERROR"/> | |||||
<logger name="org.springframework" level="ERROR"/> | |||||
<logger name="io.swagger" level="ERROR"/> | |||||
<logger name="ch.qos.logback" level="OFF"/> | |||||
<logger name="springfox.documentation" level="ERROR"/> | |||||
<!-- 所有的日志同时应用“STDOUT”和“EROOR”的策略 --> | |||||
<root> | |||||
<level value="DEBUG"/> | |||||
<!--<appender-ref ref="TPM"/>--> | |||||
<appender-ref ref="ERROR"/> | |||||
<appender-ref ref="STDOUT"/> | |||||
</root> | |||||
</configuration> |
@@ -0,0 +1,34 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<beans xmlns="http://www.springframework.org/schema/beans" | |||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xmlns:context="http://www.springframework.org/schema/context" | |||||
xmlns:mvc="http://www.springframework.org/schema/mvc" | |||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> | |||||
<!--指明 controller 所在包,并扫描其中的注解--> | |||||
<context:component-scan base-package="com.educoder.bridge.controller"/> | |||||
<!-- 静态资源(js、image等)的访问 --> | |||||
<mvc:default-servlet-handler/> | |||||
<!--ViewResolver 视图解析器--> | |||||
<!--用于支持freemarker视图解析--> | |||||
<!--视图解释器 --> | |||||
<bean id="viewResolver" | |||||
class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"> | |||||
<property name="suffix"> | |||||
<value>.ftl</value> | |||||
</property> | |||||
<property name="contentType" value="text/html;charset=UTF-8"></property> | |||||
</bean> | |||||
<!-- 开启注解 --> | |||||
<mvc:annotation-driven/> | |||||
<bean class="springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration" id="swagger2Config"/> | |||||
<mvc:resources location="classpath:/META-INF/resources/" mapping="swagger-ui.html"/> | |||||
<mvc:resources location="classpath:/META-INF/resources/webjars/" mapping="/webjars/**"/> | |||||
</beans> |
@@ -0,0 +1,61 @@ | |||||
<!DOCTYPE html> | |||||
<html lang="en"> | |||||
<head> | |||||
<meta charset="utf-8"> | |||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||||
<title>JWebssh</title> | |||||
<link rel='shortcut icon' type='image/x-icon' href='/static/image/favicon.ico'/> | |||||
<link rel="stylesheet" href="/static/css/pure-min.css"> | |||||
<link href="/static/css/xterm.css" rel="stylesheet" type="text/css"/> | |||||
<link rel='stylesheet' href='http://fonts.googleapis.com/css?family=PT+Sans:400,700'> | |||||
<link rel="stylesheet" href="/static/css/reset.css"> | |||||
<link rel="stylesheet" href="/static/css/supersized.css"> | |||||
<link rel="stylesheet" href="/static/css/style.css"> | |||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements --> | |||||
<!--[if lt IE 9]> | |||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> | |||||
<![endif]--> | |||||
</head> | |||||
<body> | |||||
<div class="page-container" id="form" name="form" > | |||||
<h1>JWebssh</h1> | |||||
<form action="" method="post"> | |||||
<fieldset> | |||||
<input id="terminalRow" name="terminalRow" type="hidden" value=100> | |||||
<div class="pure-item"> | |||||
<input id="host" name="host" type="text" placeholder="Host" value=${host}> | |||||
</div> | |||||
<div class="pure-item"> | |||||
<input id="port" name="port" type="text" placeholder="Port" value=${port}> | |||||
</div> | |||||
<div class="pure-item"> | |||||
<input id="username" name="username" type="text" placeholder="Username" value=${username}> | |||||
</div> | |||||
<div class="pure-item"> | |||||
<input id="password" name="password" type="password" placeholder="Password" value=${password}> | |||||
</div> | |||||
<button type="button" onclick="connect()">Connect</button> | |||||
</fieldset> | |||||
</form> | |||||
</div> | |||||
<div id="term" align="center"></div> | |||||
<!-- Javascript --> | |||||
<script src="/static/js/jquerymin.js"></script> | |||||
<script src="/static/js/supersized.3.2.7.min.js"></script> | |||||
<script src="/static/js/supersized-init.js"></script> | |||||
<script src="/static/js/base64.js"></script> | |||||
<script src="/static/js/jquerymin.js"></script> | |||||
<script src="/static/js/xterm.js"></script> | |||||
<script src="/static/js/ws.js"></script> | |||||
<script src="/static/js/formvalid.js"></script> | |||||
<script src="/static/js/main.js?${digest}"></script> | |||||
</body> | |||||
</html> |
@@ -0,0 +1,63 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" | |||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" | |||||
version="3.1"> | |||||
<display-name>educoder bridge</display-name> | |||||
<!-- Spring 上下文参数 --> | |||||
<context-param> | |||||
<param-name>contextConfigLocation</param-name> | |||||
<param-value>classpath:applicationContext.xml</param-value> | |||||
</context-param> | |||||
<listener> | |||||
<listener-class> | |||||
org.springframework.web.context.ContextLoaderListener | |||||
</listener-class> | |||||
</listener> | |||||
<!-- logback --> | |||||
<context-param> | |||||
<param-name>logbackConfigLocation</param-name> | |||||
<param-value>classpath:logback.xml</param-value> | |||||
</context-param> | |||||
<listener> | |||||
<listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class> | |||||
</listener> | |||||
<servlet> | |||||
<servlet-name>mvc-dispatcher</servlet-name> | |||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> | |||||
<load-on-startup>1</load-on-startup> | |||||
</servlet> | |||||
<servlet-mapping> | |||||
<servlet-name>mvc-dispatcher</servlet-name> | |||||
<url-pattern>/</url-pattern> | |||||
</servlet-mapping> | |||||
<filter> | |||||
<filter-name>encodingFilter</filter-name> | |||||
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> | |||||
<init-param> | |||||
<param-name>encoding</param-name> | |||||
<param-value>UTF-8</param-value> | |||||
</init-param> | |||||
<init-param> | |||||
<param-name>forceEncoding</param-name> | |||||
<param-value>true</param-value> | |||||
</init-param> | |||||
</filter> | |||||
<filter-mapping> | |||||
<filter-name>encodingFilter</filter-name> | |||||
<url-pattern>/*</url-pattern> | |||||
</filter-mapping> | |||||
<welcome-file-list> | |||||
<welcome-file></welcome-file> | |||||
</welcome-file-list> | |||||
</web-app> |
@@ -0,0 +1,2 @@ | |||||
<!--保留以防后续看swagger UI界面需要--> | |||||
<!--<meta http-equiv="refresh" content="0; url=/swagger-ui.html" />--> |
@@ -0,0 +1,53 @@ | |||||
.aside { | |||||
text-align: center; | |||||
background: #1f8dd6; | |||||
height: 100px; | |||||
color: #fff; | |||||
vertical-align: middle; | |||||
line-height: 100px; | |||||
font-size: 30px | |||||
} | |||||
#main { | |||||
margin-top: 20px; | |||||
} | |||||
#ratio-group { | |||||
float: right; | |||||
} | |||||
.pure-item { | |||||
margin: 0 auto 10px; | |||||
width: 300px; | |||||
position: relative; | |||||
} | |||||
.pure-radio { | |||||
margin-left: 10px; | |||||
} | |||||
.pure-item:after { | |||||
content: ""; | |||||
display: table; | |||||
clear: both; | |||||
} | |||||
.pure-item label { | |||||
float: left; | |||||
line-height: 34px; | |||||
} | |||||
.pure-item input { | |||||
float: right; | |||||
} | |||||
.terminal { | |||||
float: none; | |||||
border: #000 solid 5px; | |||||
font-family: "Monaco", "DejaVu Sans Mono", "Liberation Mono", monospace; | |||||
font-size: 11px; | |||||
color: #f0f0f0; | |||||
width: 600px; | |||||
background: #000; | |||||
box-shadow: rgba(0, 0, 0, 0.8) 2px 2px 20px; | |||||
} |
@@ -0,0 +1,44 @@ | |||||
/* ------- This is the CSS Reset ------- */ | |||||
html, body, div, span, applet, object, iframe, | |||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, | |||||
abbr, acronym, address, big, cite, code, del, | |||||
dfn, em, img, ins, kbd, q, s, samp, small, | |||||
strike, strong, sub, sup, tt, var, u, i, center, | |||||
dl, dt, dd, ol, ul, li, fieldset, form, label, | |||||
legend, table, caption, tbody, tfoot, thead, tr, | |||||
th, td, article, aside, canvas, details, embed, | |||||
figure, figcaption, footer, header, hgroup, menu, | |||||
nav, output, ruby, section, summary, time, mark, audio, video { | |||||
margin: 0; | |||||
padding: 0; | |||||
border: 0; | |||||
font-size: 100%; | |||||
font: inherit; | |||||
vertical-align: baseline; | |||||
} | |||||
/* ------- HTML5 display-role reset for older browsers ------- */ | |||||
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { | |||||
display: block; | |||||
} | |||||
body { | |||||
line-height: 1; | |||||
} | |||||
ol, ul { | |||||
list-style: none; | |||||
} | |||||
blockquote, q { | |||||
quotes: none; | |||||
} | |||||
blockquote:before, blockquote:after, q:before, q:after { | |||||
content: ''; | |||||
content: none; | |||||
} | |||||
table { | |||||
border-collapse: collapse; | |||||
border-spacing: 0; | |||||
} | |||||
@@ -0,0 +1,187 @@ | |||||
/* | |||||
* | |||||
* Template Name: Fullscreen Login | |||||
* Description: Login Template with Fullscreen Background Slideshow | |||||
* Author: Anli Zaimi | |||||
* Author URI: http://azmind.com | |||||
* | |||||
*/ | |||||
body { | |||||
background: #f8f8f8; | |||||
font-family: 'PT Sans', Helvetica, Arial, sans-serif; | |||||
text-align: center; | |||||
color: #fff; | |||||
} | |||||
.page-container { | |||||
margin: 120px auto 0 auto; | |||||
} | |||||
h1 { | |||||
font-size: 30px; | |||||
font-weight: 700; | |||||
text-shadow: 0 1px 4px rgba(0,0,0,.2); | |||||
} | |||||
form { | |||||
position: relative; | |||||
width: 305px; | |||||
margin: 15px auto 0 auto; | |||||
text-align: center; | |||||
} | |||||
input { | |||||
width: 270px; | |||||
height: 42px; | |||||
margin-top: 25px; | |||||
padding: 0 15px; | |||||
background: #2d2d2d; /* browsers that don't support rgba */ | |||||
background: rgba(45,45,45,.15); | |||||
-moz-border-radius: 6px; | |||||
-webkit-border-radius: 6px; | |||||
border-radius: 6px; | |||||
border: 1px solid #3d3d3d; /* browsers that don't support rgba */ | |||||
border: 1px solid rgba(255,255,255,.15); | |||||
-moz-box-shadow: 0 2px 3px 0 rgba(0,0,0,.1) inset; | |||||
-webkit-box-shadow: 0 2px 3px 0 rgba(0,0,0,.1) inset; | |||||
box-shadow: 0 2px 3px 0 rgba(0,0,0,.1) inset; | |||||
font-family: 'PT Sans', Helvetica, Arial, sans-serif; | |||||
font-size: 14px; | |||||
color: #fff; | |||||
text-shadow: 0 1px 2px rgba(0,0,0,.1); | |||||
-o-transition: all .2s; | |||||
-moz-transition: all .2s; | |||||
-webkit-transition: all .2s; | |||||
-ms-transition: all .2s; | |||||
} | |||||
input:-moz-placeholder { color: #fff; } | |||||
input:-ms-input-placeholder { color: #fff; } | |||||
input::-webkit-input-placeholder { color: #fff; } | |||||
input:focus { | |||||
outline: none; | |||||
-moz-box-shadow: | |||||
0 2px 3px 0 rgba(0,0,0,.1) inset, | |||||
0 2px 7px 0 rgba(0,0,0,.2); | |||||
-webkit-box-shadow: | |||||
0 2px 3px 0 rgba(0,0,0,.1) inset, | |||||
0 2px 7px 0 rgba(0,0,0,.2); | |||||
box-shadow: | |||||
0 2px 3px 0 rgba(0,0,0,.1) inset, | |||||
0 2px 7px 0 rgba(0,0,0,.2); | |||||
} | |||||
button { | |||||
cursor: pointer; | |||||
width: 300px; | |||||
height: 44px; | |||||
margin-top: 25px; | |||||
padding: 0; | |||||
background: #ef4300; | |||||
-moz-border-radius: 6px; | |||||
-webkit-border-radius: 6px; | |||||
border-radius: 6px; | |||||
border: 1px solid #ff730e; | |||||
-moz-box-shadow: | |||||
0 15px 30px 0 rgba(255,255,255,.25) inset, | |||||
0 2px 7px 0 rgba(0,0,0,.2); | |||||
-webkit-box-shadow: | |||||
0 15px 30px 0 rgba(255,255,255,.25) inset, | |||||
0 2px 7px 0 rgba(0,0,0,.2); | |||||
box-shadow: | |||||
0 15px 30px 0 rgba(255,255,255,.25) inset, | |||||
0 2px 7px 0 rgba(0,0,0,.2); | |||||
font-family: 'PT Sans', Helvetica, Arial, sans-serif; | |||||
font-size: 14px; | |||||
font-weight: 700; | |||||
color: #fff; | |||||
text-shadow: 0 1px 2px rgba(0,0,0,.1); | |||||
-o-transition: all .2s; | |||||
-moz-transition: all .2s; | |||||
-webkit-transition: all .2s; | |||||
-ms-transition: all .2s; | |||||
} | |||||
button:hover { | |||||
-moz-box-shadow: | |||||
0 15px 30px 0 rgba(255,255,255,.15) inset, | |||||
0 2px 7px 0 rgba(0,0,0,.2); | |||||
-webkit-box-shadow: | |||||
0 15px 30px 0 rgba(255,255,255,.15) inset, | |||||
0 2px 7px 0 rgba(0,0,0,.2); | |||||
box-shadow: | |||||
0 15px 30px 0 rgba(255,255,255,.15) inset, | |||||
0 2px 7px 0 rgba(0,0,0,.2); | |||||
} | |||||
button:active { | |||||
-moz-box-shadow: | |||||
0 15px 30px 0 rgba(255,255,255,.15) inset, | |||||
0 2px 7px 0 rgba(0,0,0,.2); | |||||
-webkit-box-shadow: | |||||
0 15px 30px 0 rgba(255,255,255,.15) inset, | |||||
0 2px 7px 0 rgba(0,0,0,.2); | |||||
box-shadow: | |||||
0 5px 8px 0 rgba(0,0,0,.1) inset, | |||||
0 1px 4px 0 rgba(0,0,0,.1); | |||||
border: 0px solid #ef4300; | |||||
} | |||||
.error { | |||||
display: none; | |||||
position: absolute; | |||||
top: 27px; | |||||
right: -55px; | |||||
width: 40px; | |||||
height: 40px; | |||||
background: #2d2d2d; /* browsers that don't support rgba */ | |||||
background: rgba(45,45,45,.25); | |||||
-moz-border-radius: 8px; | |||||
-webkit-border-radius: 8px; | |||||
border-radius: 8px; | |||||
} | |||||
.error span { | |||||
display: inline-block; | |||||
margin-left: 2px; | |||||
font-size: 40px; | |||||
font-weight: 700; | |||||
line-height: 40px; | |||||
text-shadow: 0 1px 2px rgba(0,0,0,.1); | |||||
-o-transform: rotate(45deg); | |||||
-moz-transform: rotate(45deg); | |||||
-webkit-transform: rotate(45deg); | |||||
-ms-transform: rotate(45deg); | |||||
} | |||||
.connect { | |||||
width: 305px; | |||||
margin: 35px auto 0 auto; | |||||
font-size: 18px; | |||||
font-weight: 700; | |||||
text-shadow: 0 1px 3px rgba(0,0,0,.2); | |||||
} | |||||
.connect a { | |||||
display: inline-block; | |||||
width: 32px; | |||||
height: 35px; | |||||
margin-top: 15px; | |||||
-o-transition: all .2s; | |||||
-moz-transition: all .2s; | |||||
-webkit-transition: all .2s; | |||||
-ms-transition: all .2s; | |||||
} | |||||
.connect a.facebook { background: url(../image/facebook.png) center center no-repeat; } | |||||
.connect a.twitter { background: url(../image/twitter.png) center center no-repeat; } | |||||
.connect a:hover { background-position: center bottom; } | |||||
@@ -0,0 +1,34 @@ | |||||
/* | |||||
Supersized - Fullscreen Slideshow jQuery Plugin | |||||
Version : 3.2.7 | |||||
Site : www.buildinternet.com/project/supersized | |||||
Author : Sam Dunn | |||||
Company : One Mighty Roar (www.onemightyroar.com) | |||||
License : MIT License / GPL License | |||||
*/ | |||||
* { margin:0; padding:0; } | |||||
body { background:#111; height:100%; } | |||||
img { border:none; } | |||||
#supersized-loader { position:absolute; top:50%; left:50%; z-index:0; width:60px; height:60px; margin:-30px 0 0 -30px; text-indent:-999em; background:url(../image/progress.gif) no-repeat center center;} | |||||
#supersized { display:block; position:fixed; left:0; top:0; overflow:hidden; z-index:-999; height:100%; width:100%; } | |||||
#supersized img { width:auto; height:auto; position:relative; display:none; outline:none; border:none; } | |||||
#supersized.speed img { -ms-interpolation-mode:nearest-neighbor; image-rendering: -moz-crisp-edges; } /*Speed*/ | |||||
#supersized.quality img { -ms-interpolation-mode:bicubic; image-rendering: optimizeQuality; } /*Quality*/ | |||||
#supersized li { display:block; list-style:none; z-index:-30; position:fixed; overflow:hidden; top:0; left:0; width:100%; height:100%; background:#111; } | |||||
#supersized a { width:100%; height:100%; display:block; } | |||||
#supersized li.prevslide { z-index:-20; } | |||||
#supersized li.activeslide { z-index:-10; } | |||||
#supersized li.image-loading { background:#111 url(../image/progress.gif) no-repeat center center; width:100%; height:100%; } | |||||
#supersized li.image-loading img{ visibility:hidden; } | |||||
#supersized li.prevslide img, #supersized li.activeslide img{ display:inline; } | |||||
#supersized img { max-width: none !important } | |||||
@@ -0,0 +1,25 @@ | |||||
.tooltip{ | |||||
position: absolute; | |||||
max-width: 300px; | |||||
top: 3px; | |||||
left: 105%; | |||||
padding: 8px 10px; | |||||
border-radius: 5px; | |||||
color: #fff; | |||||
background: #000000; | |||||
box-shadow: 0 2px 2px 0 #7F7C7C; | |||||
white-space: nowrap; | |||||
} | |||||
.tooltip:after{ | |||||
content: ''; | |||||
position: absolute; | |||||
top: 35%; | |||||
right:100%; | |||||
margin-left: 10px; | |||||
width: 0; | |||||
height: 0; | |||||
border: 5px solid transparent; | |||||
border-right: 7px #000; | |||||
border-left-width: 7px; | |||||
} | |||||
@@ -0,0 +1,103 @@ | |||||
function Base64() { | |||||
// private property | |||||
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; | |||||
// public method for encoding | |||||
this.encode = function (input) { | |||||
var output = ""; | |||||
var chr1, chr2, chr3, enc1, enc2, enc3, enc4; | |||||
var i = 0; | |||||
input = _utf8_encode(input); | |||||
while (i < input.length) { | |||||
chr1 = input.charCodeAt(i++); | |||||
chr2 = input.charCodeAt(i++); | |||||
chr3 = input.charCodeAt(i++); | |||||
enc1 = chr1 >> 2; | |||||
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); | |||||
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); | |||||
enc4 = chr3 & 63; | |||||
if (isNaN(chr2)) { | |||||
enc3 = enc4 = 64; | |||||
} else if (isNaN(chr3)) { | |||||
enc4 = 64; | |||||
} | |||||
output = output + | |||||
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) + | |||||
_keyStr.charAt(enc3) + _keyStr.charAt(enc4); | |||||
} | |||||
return output; | |||||
} | |||||
// public method for decoding | |||||
this.decode = function (input) { | |||||
var output = ""; | |||||
var chr1, chr2, chr3; | |||||
var enc1, enc2, enc3, enc4; | |||||
var i = 0; | |||||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); | |||||
while (i < input.length) { | |||||
enc1 = _keyStr.indexOf(input.charAt(i++)); | |||||
enc2 = _keyStr.indexOf(input.charAt(i++)); | |||||
enc3 = _keyStr.indexOf(input.charAt(i++)); | |||||
enc4 = _keyStr.indexOf(input.charAt(i++)); | |||||
chr1 = (enc1 << 2) | (enc2 >> 4); | |||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); | |||||
chr3 = ((enc3 & 3) << 6) | enc4; | |||||
output = output + String.fromCharCode(chr1); | |||||
if (enc3 != 64) { | |||||
output = output + String.fromCharCode(chr2); | |||||
} | |||||
if (enc4 != 64) { | |||||
output = output + String.fromCharCode(chr3); | |||||
} | |||||
} | |||||
output = _utf8_decode(output); | |||||
return output; | |||||
} | |||||
// private method for UTF-8 encoding | |||||
_utf8_encode = function (string) { | |||||
string = string.replace(/\r\n/g,"\n"); | |||||
var utftext = ""; | |||||
for (var n = 0; n < string.length; n++) { | |||||
var c = string.charCodeAt(n); | |||||
if (c < 128) { | |||||
utftext += String.fromCharCode(c); | |||||
} else if((c > 127) && (c < 2048)) { | |||||
utftext += String.fromCharCode((c >> 6) | 192); | |||||
utftext += String.fromCharCode((c & 63) | 128); | |||||
} else { | |||||
utftext += String.fromCharCode((c >> 12) | 224); | |||||
utftext += String.fromCharCode(((c >> 6) & 63) | 128); | |||||
utftext += String.fromCharCode((c & 63) | 128); | |||||
} | |||||
} | |||||
return utftext; | |||||
} | |||||
// private method for UTF-8 decoding | |||||
_utf8_decode = function (utftext) { | |||||
var string = ""; | |||||
var i = 0; | |||||
var c = c1 = c2 = 0; | |||||
while ( i < utftext.length ) { | |||||
c = utftext.charCodeAt(i); | |||||
if (c < 128) { | |||||
string += String.fromCharCode(c); | |||||
i++; | |||||
} else if((c > 191) && (c < 224)) { | |||||
c2 = utftext.charCodeAt(i+1); | |||||
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); | |||||
i += 2; | |||||
} else { | |||||
c2 = utftext.charCodeAt(i+1); | |||||
c3 = utftext.charCodeAt(i+2); | |||||
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); | |||||
i += 3; | |||||
} | |||||
} | |||||
return string; | |||||
} | |||||
} |
@@ -0,0 +1,234 @@ | |||||
/* | |||||
Jquery | |||||
janchie 2010.1 | |||||
1.02版 | |||||
*/ | |||||
var validResult = {}; | |||||
var errorMsg = {}; | |||||
(function ($) { | |||||
$.fn.extend({ | |||||
valid: function () { | |||||
if (!$(this).is("form")) return; | |||||
var items = $.isArray(arguments[0]) ? arguments[0] : [], | |||||
isBindSubmit = typeof arguments[1] === "boolean" ? arguments[1] : true, | |||||
isAlert = typeof arguments[2] === "boolean" ? arguments[2] : false, | |||||
rule = { | |||||
"eng": /^[A-Za-z]+$/, | |||||
"chn": /^[\u0391-\uFFE5]+$/, | |||||
"mail": /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/, | |||||
"url": /^http[s]?:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+[\/=\?%\-&_~`@[\]\':+!]*([^<>\"\"])*$/, | |||||
"currency": /^\d+(\.\d+)?$/, | |||||
"number": /^\d+$/, | |||||
"int": /^[0-9]{1,30}$/, | |||||
"double": /^[-\+]?\d+(\.\d+)?$/, | |||||
"username": /^[a-zA-Z]{1}([a-zA-Z0-9]|[._]){3,19}$/, | |||||
"password": /^[\w\W]{6,20}$/, | |||||
"safe": />|<|,|\[|\]|\{|\}|\?|\/|\+|=|\||\'|\\|\"|:|;|\~|\!|\@|\#|\*|\$|\%|\^|\&|\(|\)|`/i, | |||||
"dbc": /[a-zA-Z0-9!@#¥%^&*()_+{}[]|:"';.,/?<>`~ ]/, | |||||
"qq": /[1-9][0-9]{4,}/, | |||||
"date": /^((((1[6-9]|[2-9]\d)\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\d|3[01]))|(((1[6-9]|[2-9]\d)\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\d|30))|(((1[6-9]|[2-9]\d)\d{2})-0?2-(0?[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-))$/, | |||||
"year": /^(19|20)[0-9]{2}$/, | |||||
"month": /^(0?[1-9]|1[0-2])$/, | |||||
"day": /^((0?[1-9])|((1|2)[0-9])|30|31)$/, | |||||
"hour": /^((0?[1-9])|((1|2)[0-3]))$/, | |||||
"minute": /^((0?[1-9])|((1|5)[0-9]))$/, | |||||
"second": /^((0?[1-9])|((1|5)[0-9]))$/, | |||||
"mobile": /^((\(\d{2,3}\))|(\d{3}\-))?13\d{9}$/, | |||||
"phone": /^[+]{0,1}(\d){1,3}[ ]?([-]?((\d)|[ ]){1,12})+$/, | |||||
"zipcode": /^[1-9]\d{5}$/, | |||||
"IDcard": /^((1[1-5])|(2[1-3])|(3[1-7])|(4[1-6])|(5[0-4])|(6[1-5])|71|(8[12])|91)\d{4}((19\d{2}(0[13-9]|1[012])(0[1-9]|[12]\d|30))|(19\d{2}(0[13578]|1[02])31)|(19\d{2}02(0[1-9]|1\d|2[0-8]))|(19([13579][26]|[2468][048]|0[48])0229))\d{3}(\d|X|x)?$/, | |||||
"ip": /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/, | |||||
"file": /^[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/, | |||||
"image": /.+\.(jpg|gif|png|bmp)$/i, | |||||
"word": /.+\.(doc|rtf|pdf)$/i, | |||||
"port": function (port) { | |||||
return (!isNaN(port) && port > 0 && port < 65536) ? true : false; | |||||
}, | |||||
"eq": function (arg1, arg2) { | |||||
return arg1 == arg2 ? true : false; | |||||
}, | |||||
"gt": function (arg1, arg2) { | |||||
return arg1 > arg2 ? true : false; | |||||
}, | |||||
"gte": function (arg1, arg2) { | |||||
return arg1 >= arg2 ? true : false; | |||||
}, | |||||
"lt": function (arg1, arg2) { | |||||
return arg1 < arg2 ? true : false; | |||||
}, | |||||
"lte": function (arg1, arg2) { | |||||
return arg1 <= arg2 ? true : false; | |||||
} | |||||
}, | |||||
msgSuffix = { | |||||
"eng": "only english welcomed", | |||||
"chn": "only chinese welcomed", | |||||
"mail": "invalid email format", | |||||
"url": "invalid url format", | |||||
"currency": "invalid number format", | |||||
"number": "only number welcomed", | |||||
"int": "only integer welcomed", | |||||
"double": "only float welcomed", | |||||
"username": "invalid username format,4-20 characters", | |||||
"password": "warning, you'd better use 6-20 characters", | |||||
"safe": "forbidden special characters", | |||||
"dbc": "forbidden full width characters", | |||||
"qq": "invalid qq format", | |||||
"date": "invalid date format", | |||||
"year": "invalid year format", | |||||
"month": "invalid month format", | |||||
"day": "invalid day format", | |||||
"hour": "invalid hour format", | |||||
"minute": "invalid minute format", | |||||
"second": "invalid second format", | |||||
"mobile": "invalid mobile format", | |||||
"phone": "invalid phone format", | |||||
"zipcode": "invalid zipcode format", | |||||
"IDcard": "invalid identity format", | |||||
"ip": "invalid ip format", | |||||
"port": "invalid port format", | |||||
"file": "invalid file format", | |||||
"image": "invalid image format", | |||||
"word": "invalid word file format", | |||||
"eq": "not equal", | |||||
"gt": "no greater than", | |||||
"gte": "no greater than or equal", | |||||
"lt": "no smaller than", | |||||
"lte": "no smaller than or equal" | |||||
}, | |||||
msg = "", formObj = $(this), checkRet = true, isAll, | |||||
tipname = function (namestr) { | |||||
return "tip_" + namestr.replace(/([a-zA-Z0-9])/g, "-$1"); | |||||
}, | |||||
typeTest = function () { | |||||
var result = true, args = arguments; | |||||
if (rule.hasOwnProperty(args[0])) { | |||||
var t = rule[args[0]], v = args[1]; | |||||
result = args.length > 2 ? t.apply(arguments, [].slice.call(args, 1)) : ($.isFunction(t) ? t(v) : t.test(v)); | |||||
} | |||||
return result; | |||||
}, | |||||
showError = function (fieldObj, filedName, warnInfo) { | |||||
checkRet = false; | |||||
var tipObj = $("#" + tipname(filedName)); | |||||
if (tipObj.length > 0) tipObj.remove(); | |||||
var tipPosition = fieldObj.next().length > 0 ? fieldObj.nextAll().eq(this.length - 1) : fieldObj.eq(this.length - 1); | |||||
//tipPosition.after("<span class='tooltip' id='" + tipname(filedName) + "'> " + warnInfo + " </span>"); | |||||
validResult[filedName] = false; | |||||
errorMsg[filedName] = warnInfo; | |||||
if (isAlert && isAll) msg = warnInfo; | |||||
}, | |||||
showRight = function (fieldObj, filedName) { | |||||
var tipObj = $("#" + tipname(filedName)); | |||||
if (tipObj.length > 0) tipObj.remove(); | |||||
var tipPosition = fieldObj.next().length > 0 ? fieldObj.nextAll().eq(this.length - 1) : fieldObj.eq(this.length - 1); | |||||
//tipPosition.after("<span class='tooltip' id='" + tipname(filedName) + "'>correct</span>"); | |||||
validResult[filedName] = true; | |||||
}, | |||||
findTo = function (objName) { | |||||
var find; | |||||
$.each(items, function () { | |||||
if (this.name == objName && this.simple) { | |||||
find = this.simple; | |||||
return false; | |||||
} | |||||
}); | |||||
if (!find) find = $("[name='" + objName + "']")[0].name; | |||||
return find; | |||||
}, | |||||
fieldCheck = function (item) { | |||||
var i = item, field = $("[name='" + i.name + "']", formObj[0]); | |||||
if (!field[0]) return; | |||||
var warnMsg, fv = $.trim(field.val()), isRq = typeof i.require === "boolean" ? i.require : true; | |||||
if (isRq && ((field.is(":radio") || field.is(":checkbox")) && !field.is(":checked"))) { | |||||
warnMsg = i.message || "choice needed"; | |||||
showError(field, i.name, warnMsg); | |||||
} else if (isRq && fv == "") { | |||||
warnMsg = i.message || ( field.is("select") ? "choice needed" : "not none" ); | |||||
showError(field, i.name, warnMsg); | |||||
} else if (fv != "") { | |||||
if (i.min || i.max) { | |||||
var len = fv.length, min = i.min || 0, max = i.max; | |||||
warnMsg = i.message || (max ? "range" + min + "~" + max + "" : "min length" + min); | |||||
if ((max && (len > max || len < min)) || (!max && len < min)) { | |||||
showError(field, i.name, warnMsg); | |||||
return; | |||||
} | |||||
} | |||||
if (i.type) { | |||||
var matchVal = i.to ? $.trim($("[name='" + i.to + "']").val()) : i.value; | |||||
var matchRet = matchVal ? typeTest(i.type, fv, matchVal) : typeTest(i.type, fv); | |||||
warnMsg = i.message || msgSuffix[i.type]; | |||||
if (matchVal) warnMsg += (i.to ? findTo(i.to) + "value" : i.value); | |||||
if (!matchRet) showError(field, i.name, warnMsg); | |||||
else showRight(field, i.name); | |||||
} else { | |||||
showRight(field, i.name); | |||||
} | |||||
} else if (isRq) { | |||||
showRight(field, i.name); | |||||
} | |||||
}, | |||||
validate = function () { | |||||
$.each(items, function () { | |||||
isAll = true; | |||||
fieldCheck(this); | |||||
}); | |||||
if (isAlert && msg != "") { | |||||
alert(msg); | |||||
msg = ""; | |||||
} | |||||
return checkRet; | |||||
}; | |||||
$.each(items, function () { | |||||
var field = $("[name='" + this.name + "']", formObj[0]); | |||||
if (field.is(":hidden")) return; | |||||
var obj = this, toCheck = function () { | |||||
isAll = false; | |||||
fieldCheck(obj); | |||||
}; | |||||
if (field.is(":file") || field.is("select")) { | |||||
field.change(toCheck); | |||||
} else { | |||||
field.blur(toCheck); | |||||
} | |||||
}); | |||||
if (isBindSubmit) { | |||||
$(this).submit(validate); | |||||
} else { | |||||
return validate(); | |||||
} | |||||
} | |||||
}); | |||||
})(jQuery); |
@@ -0,0 +1,124 @@ | |||||
function openTerminal(options) { | |||||
//var CONNECT_TIME = 0; // 请求连接次数 | |||||
$("#form").hide(); | |||||
Rows = parseInt(options.Rows); | |||||
var client = new WSSHClient(); | |||||
var base64 = new Base64(); | |||||
var term = new Terminal({cols: 80, rows: Rows, screenKeys: true, useStyle: true}); | |||||
// 发送客户端数据 | |||||
term.on('data', function (data) { | |||||
console.log("xterm data: "); | |||||
console.log(data); | |||||
client.sendClientData(data); | |||||
}); | |||||
term.open(); | |||||
$('.terminal').detach().appendTo('#term'); | |||||
$("#term").show(); | |||||
term.write("Connecting..."); | |||||
console.debug(options); | |||||
//var interTime = setInterval(client_connect, 1000) | |||||
setTimeout(client_connect, 3000); | |||||
var intervalId = null; | |||||
function client_connect() { | |||||
// var TIMEINIT = 0; // 初始化时间 | |||||
// var TIMEOUT = 60 * 15; // 超时时间 | |||||
var CONNECTED = false; // 是否连接成功过 | |||||
console.log("连接中...."); | |||||
console.log(options); | |||||
client.connect({ | |||||
onError: function (error) { | |||||
term.write('Error: ' + error + '\r\n'); | |||||
console.log('error happened'); | |||||
}, | |||||
onConnect: function () { | |||||
console.log('connection established'); | |||||
// 连接上之后发送初始化数据 | |||||
client.sendInitData(options); | |||||
term.focus(); | |||||
}, | |||||
onClose: function () { | |||||
console.log("连接关闭"); | |||||
term.write("\r\nconnection closed"); | |||||
if (CONNECTED) { | |||||
console.log('connection reset by peer'); | |||||
$('term').hide(); | |||||
} | |||||
}, | |||||
// 当收到服务端返回的数据 | |||||
onData: function (data) { | |||||
if (!CONNECTED) { | |||||
console.log("first connected."); | |||||
term.write("\r"); //换行 | |||||
term.focus(); //焦点移动到框上 | |||||
} | |||||
/*if(interTime){ | |||||
clearInterval(interTime); | |||||
}*/ | |||||
CONNECTED = true; | |||||
data = base64.decode(data); | |||||
/* TIMEINIT = 0;*/ | |||||
term.write(data); | |||||
console.log('get data:' + data); | |||||
} | |||||
}) | |||||
} | |||||
} | |||||
var charWidth = 6.2; | |||||
var charHeight = 15.2; | |||||
/** | |||||
* for full screen | |||||
* @returns {{w: number, h: number}} | |||||
*/ | |||||
function getTerminalSize() { | |||||
var width = window.innerWidth; | |||||
var height = window.innerHeight; | |||||
return { | |||||
w: Math.floor(width / charWidth), | |||||
h: Math.floor(height / charHeight) | |||||
}; | |||||
} | |||||
function store(options) { | |||||
window.localStorage.host = options.host | |||||
window.localStorage.port = options.port | |||||
window.localStorage.username = options.username | |||||
window.localStorage.ispwd = options.ispwd | |||||
window.localStorage.password = options.password | |||||
} | |||||
function check() { | |||||
return validResult["host"] && validResult["port"] && validResult["username"]; | |||||
} | |||||
function connect() { | |||||
var remember = $("#remember").is(":checked") | |||||
var options = { | |||||
host: $("#host").val(), | |||||
port: $("#port").val(), | |||||
username: $("#username").val(), | |||||
password: $("#password").val(), | |||||
Rows: $("#terminalRow").val(), | |||||
} | |||||
if (remember) { | |||||
store(options) | |||||
} | |||||
if (true) { | |||||
openTerminal(options) | |||||
} else { | |||||
for (var key in validResult) { | |||||
if (!validResult[key]) { | |||||
alert(errorMsg[key]); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,30 @@ | |||||
jQuery(function($){ | |||||
$.supersized({ | |||||
// Functionality | |||||
slide_interval : 4000, // Length between transitions | |||||
transition : 1, // 0-None, 1-Fade, 2-Slide Top, 3-Slide Right, 4-Slide Bottom, 5-Slide Left, 6-Carousel Right, 7-Carousel Left | |||||
transition_speed : 1000, // Speed of transition | |||||
performance : 1, // 0-Normal, 1-Hybrid speed/quality, 2-Optimizes image quality, 3-Optimizes transition speed // (Only works for Firefox/IE, not Webkit) | |||||
// Size & Position | |||||
min_width : 0, // Min width allowed (in pixels) | |||||
min_height : 0, // Min height allowed (in pixels) | |||||
vertical_center : 1, // Vertically center background | |||||
horizontal_center : 1, // Horizontally center background | |||||
fit_always : 0, // Image will never exceed browser width or height (Ignores min. dimensions) | |||||
fit_portrait : 1, // Portrait images will not exceed browser height | |||||
fit_landscape : 0, // Landscape images will not exceed browser width | |||||
// Components | |||||
slide_links : 'blank', // Individual links for each slide (Options: false, 'num', 'name', 'blank') | |||||
slides : [ // Slideshow Images | |||||
{image : '/static/image/backgrounds/1.jpg'}, | |||||
{image : '/static/image/backgrounds/2.jpg'}, | |||||
{image : '/static/image/backgrounds/3.jpg'} | |||||
] | |||||
}); | |||||
}); |
@@ -0,0 +1,67 @@ | |||||
function WSSHClient() { | |||||
}; | |||||
WSSHClient.prototype._generateEndpoint = function () { | |||||
if (window.location.protocol == 'https:') { | |||||
var protocol = 'wss://'; | |||||
} else { | |||||
var protocol = 'ws://'; | |||||
} | |||||
var endpoint = protocol + window.location.host + '/ws'; | |||||
return endpoint; | |||||
}; | |||||
WSSHClient.prototype.connect = function (options) { | |||||
var endpoint = this._generateEndpoint(); | |||||
if (window.WebSocket) { | |||||
this._connection = new WebSocket(endpoint); | |||||
} | |||||
else if (window.MozWebSocket) { | |||||
this._connection = MozWebSocket(endpoint); | |||||
} | |||||
else { | |||||
options.onError('WebSocket Not Supported'); | |||||
return; | |||||
} | |||||
this._connection.onopen = function () { | |||||
options.onConnect(); | |||||
}; | |||||
this._connection.onmessage = function (evt) { | |||||
var data = evt.data.toString() | |||||
options.onData(data); | |||||
}; | |||||
this._connection.onclose = function (evt) { | |||||
options.onClose(); | |||||
}; | |||||
}; | |||||
WSSHClient.prototype.close = function () { | |||||
this._connection.close(); | |||||
}; | |||||
WSSHClient.prototype.send = function (data) { | |||||
this._connection.send(JSON.stringify(data)); | |||||
}; | |||||
WSSHClient.prototype.sendInitData = function (options) { | |||||
var data = { | |||||
hostname: options.host, | |||||
port: options.port, | |||||
username: options.username, | |||||
password: options.password | |||||
}; | |||||
this._connection.send(JSON.stringify({"tp": "init", "data": options})) | |||||
console.log("发送初始化数据:" + options) | |||||
} | |||||
WSSHClient.prototype.sendClientData = function (data) { | |||||
this._connection.send(JSON.stringify({"tp": "client", "data": data})) | |||||
console.log("发送客户端数据:" + data) | |||||
} | |||||
var client = new WSSHClient(); |