From 1268e9f2045dc8a53a60283b78ae2ade09e384b5 Mon Sep 17 00:00:00 2001 From: lusiyi Date: Thu, 10 Nov 2022 16:25:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 10 + pom.xml | 257 + src/main/go-mod/excel.go | 48 + src/main/go-mod/func.go | 24 + src/main/go-mod/main.go | 4 + src/main/go-mod/struct.go | 7 + .../educoder/bridge/controller/BaseController.java | 25 + .../educoder/bridge/controller/MainController.java | 49 + .../com/educoder/bridge/handler/WebsshHandler.java | 38 + .../java/com/educoder/bridge/model/SSHInfo.java | 41 + .../java/com/educoder/bridge/model/SSHSession.java | 45 + .../com/educoder/bridge/service/JchService.java | 261 ++ .../com/educoder/bridge/tmp/ASTStringUtilExt.java | 169 + .../educoder/bridge/tmp/AbstractLangProcessor.java | 248 + .../java/com/educoder/bridge/tmp/AliasEntity.java | 203 + .../educoder/bridge/tmp/AnnotationProcessor.java | 121 + .../com/educoder/bridge/tmp/BindingResolver.java | 297 ++ .../java/com/educoder/bridge/tmp/BuiltInType.java | 110 + .../com/educoder/bridge/tmp/CandidateTypes.java | 311 ++ .../com/educoder/bridge/tmp/CdtCppFileParser.java | 86 + .../com/educoder/bridge/tmp/ContainerEntity.java | 467 ++ .../com/educoder/bridge/tmp/CppBuiltInType.java | 66 + .../bridge/tmp/CppImportLookupStrategy.java | 133 + .../java/com/educoder/bridge/tmp/CppVisitor.java | 338 ++ .../com/educoder/bridge/tmp/DecoratedEntity.java | 148 + .../educoder/bridge/tmp/DependencyGenerator.java | 160 + .../com/educoder/bridge/tmp/DependencyMatrix.java | 102 + .../com/educoder/bridge/tmp/DependsCommand.java | 161 + ...lipseTestBase_No_ResponseDuirngTypeResolve.java | 222 + src/main/java/com/educoder/bridge/tmp/Entity.java | 275 ++ .../com/educoder/bridge/tmp/EntityExtractTest.java | 101 + .../bridge/tmp/ExcelXlsFormatDependencyDumper.java | 120 + .../tmp/ExcelXlsxFormatDependencyDumper.java | 119 + .../java/com/educoder/bridge/tmp/Expression.java | 413 ++ .../com/educoder/bridge/tmp/ExpressionUsage.java | 298 ++ .../java/com/educoder/bridge/tmp/ExtractText.java | 129 + .../java/com/educoder/bridge/tmp/FileEntity.java | 169 + .../tmp/FormalParameterListContextHelper.java | 99 + .../com/educoder/bridge/tmp/FunctionEntity.java | 149 + .../java/com/educoder/bridge/tmp/GenericName.java | 117 + .../bridge/tmp/GoImportLookupStrategy.java | 103 + .../java/com/educoder/bridge/tmp/GoListener.java | 136 + .../java/com/educoder/bridge/tmp/GoParserBase.java | 132 + .../com/educoder/bridge/tmp/HandlerContext.java | 333 ++ .../educoder/bridge/tmp/InMemoryEntityRepo.java | 135 + .../educoder/bridge/tmp/IncludeRelationTest.java | 109 + .../java/com/educoder/bridge/tmp/JDataBuilder.java | 88 + .../java/com/educoder/bridge/tmp/JRubyVisitor.java | 281 ++ .../com/educoder/bridge/tmp/JavaBuiltInType.java | 84 + .../bridge/tmp/JavaImportLookupStrategy.java | 105 + .../java/com/educoder/bridge/tmp/JavaListener.java | 337 ++ .../educoder/bridge/tmp/JavaVarResolveTest.java | 100 + .../java/com/educoder/bridge/tmp/JchService.java | 261 ++ src/main/java/com/educoder/bridge/tmp/Main.java | 197 + .../educoder/bridge/tmp/MatrixLevelReducer.java | 131 + .../java/com/educoder/bridge/tmp/ParserTest.java | 109 + .../java/com/educoder/bridge/tmp/PomListener.java | 165 + .../educoder/bridge/tmp/PreprocessorHandler.java | 126 + .../com/educoder/bridge/tmp/PythonBuiltInType.java | 96 + .../educoder/bridge/tmp/PythonCodeListener.java | 371 ++ .../com/educoder/bridge/tmp/PythonImportTest.java | 284 ++ .../com/educoder/bridge/tmp/PythonLexerBase.java | 186 + .../bridge/tmp/PythonParameterTypeDedudceTest.java | 118 + .../com/educoder/bridge/tmp/RelationCounter.java | 246 + .../educoder/bridge/tmp/RubyHandlerContext.java | 105 + .../com/educoder/bridge/tmp/RubyParserHelper.java | 165 + .../java/com/educoder/bridge/tmp/RubyVarTest.java | 172 + .../java/com/educoder/bridge/tmp/TypeEntity.java | 204 + .../educoder/bridge/tmp/UnsolvedSymbolDumper.java | 96 + .../java/com/educoder/bridge/tmp/VarEntity.java | 106 + .../java/com/educoder/bridge/utils/Base64Util.java | 52 + src/main/resources/applicationContext.xml | 42 + src/main/resources/logback.xml | 47 + src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml | 34 + src/main/webapp/WEB-INF/pages/index.ftl | 61 + src/main/webapp/WEB-INF/web.xml | 63 + src/main/webapp/index.html | 2 + src/main/webapp/static/css/main.css | 53 + src/main/webapp/static/css/pure-min.css | 11 + src/main/webapp/static/css/reset.css | 44 + src/main/webapp/static/css/style.css | 187 + src/main/webapp/static/css/supersized.css | 34 + src/main/webapp/static/css/tooltip.css | 25 + src/main/webapp/static/css/xterm.css | 2273 +++++++++ src/main/webapp/static/image/backgrounds/1.jpg | Bin 0 -> 72128 bytes src/main/webapp/static/image/backgrounds/2.jpg | Bin 0 -> 68833 bytes src/main/webapp/static/image/backgrounds/3.jpg | Bin 0 -> 52931 bytes src/main/webapp/static/image/facebook.png | Bin 0 -> 623 bytes src/main/webapp/static/image/favicon.ico | Bin 0 -> 16958 bytes src/main/webapp/static/image/progress.gif | Bin 0 -> 2608 bytes src/main/webapp/static/image/twitter.png | Bin 0 -> 933 bytes src/main/webapp/static/js/base64.js | 103 + src/main/webapp/static/js/formvalid.js | 234 + src/main/webapp/static/js/jquerymin.js | 5 + src/main/webapp/static/js/main.js | 124 + src/main/webapp/static/js/supersized-init.js | 30 + src/main/webapp/static/js/supersized.3.2.7.min.js | 13 + src/main/webapp/static/js/ws.js | 67 + src/main/webapp/static/js/xterm.js | 4959 ++++++++++++++++++++ 99 files changed, 19684 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/go-mod/excel.go create mode 100644 src/main/go-mod/func.go create mode 100644 src/main/go-mod/main.go create mode 100644 src/main/go-mod/struct.go create mode 100644 src/main/java/com/educoder/bridge/controller/BaseController.java create mode 100644 src/main/java/com/educoder/bridge/controller/MainController.java create mode 100644 src/main/java/com/educoder/bridge/handler/WebsshHandler.java create mode 100644 src/main/java/com/educoder/bridge/model/SSHInfo.java create mode 100644 src/main/java/com/educoder/bridge/model/SSHSession.java create mode 100644 src/main/java/com/educoder/bridge/service/JchService.java create mode 100644 src/main/java/com/educoder/bridge/tmp/ASTStringUtilExt.java create mode 100644 src/main/java/com/educoder/bridge/tmp/AbstractLangProcessor.java create mode 100644 src/main/java/com/educoder/bridge/tmp/AliasEntity.java create mode 100644 src/main/java/com/educoder/bridge/tmp/AnnotationProcessor.java create mode 100644 src/main/java/com/educoder/bridge/tmp/BindingResolver.java create mode 100644 src/main/java/com/educoder/bridge/tmp/BuiltInType.java create mode 100644 src/main/java/com/educoder/bridge/tmp/CandidateTypes.java create mode 100644 src/main/java/com/educoder/bridge/tmp/CdtCppFileParser.java create mode 100644 src/main/java/com/educoder/bridge/tmp/ContainerEntity.java create mode 100644 src/main/java/com/educoder/bridge/tmp/CppBuiltInType.java create mode 100644 src/main/java/com/educoder/bridge/tmp/CppImportLookupStrategy.java create mode 100644 src/main/java/com/educoder/bridge/tmp/CppVisitor.java create mode 100644 src/main/java/com/educoder/bridge/tmp/DecoratedEntity.java create mode 100644 src/main/java/com/educoder/bridge/tmp/DependencyGenerator.java create mode 100644 src/main/java/com/educoder/bridge/tmp/DependencyMatrix.java create mode 100644 src/main/java/com/educoder/bridge/tmp/DependsCommand.java create mode 100644 src/main/java/com/educoder/bridge/tmp/EclipseTestBase_No_ResponseDuirngTypeResolve.java create mode 100644 src/main/java/com/educoder/bridge/tmp/Entity.java create mode 100644 src/main/java/com/educoder/bridge/tmp/EntityExtractTest.java create mode 100644 src/main/java/com/educoder/bridge/tmp/ExcelXlsFormatDependencyDumper.java create mode 100644 src/main/java/com/educoder/bridge/tmp/ExcelXlsxFormatDependencyDumper.java create mode 100644 src/main/java/com/educoder/bridge/tmp/Expression.java create mode 100644 src/main/java/com/educoder/bridge/tmp/ExpressionUsage.java create mode 100644 src/main/java/com/educoder/bridge/tmp/ExtractText.java create mode 100644 src/main/java/com/educoder/bridge/tmp/FileEntity.java create mode 100644 src/main/java/com/educoder/bridge/tmp/FormalParameterListContextHelper.java create mode 100644 src/main/java/com/educoder/bridge/tmp/FunctionEntity.java create mode 100644 src/main/java/com/educoder/bridge/tmp/GenericName.java create mode 100644 src/main/java/com/educoder/bridge/tmp/GoImportLookupStrategy.java create mode 100644 src/main/java/com/educoder/bridge/tmp/GoListener.java create mode 100644 src/main/java/com/educoder/bridge/tmp/GoParserBase.java create mode 100644 src/main/java/com/educoder/bridge/tmp/HandlerContext.java create mode 100644 src/main/java/com/educoder/bridge/tmp/InMemoryEntityRepo.java create mode 100644 src/main/java/com/educoder/bridge/tmp/IncludeRelationTest.java create mode 100644 src/main/java/com/educoder/bridge/tmp/JDataBuilder.java create mode 100644 src/main/java/com/educoder/bridge/tmp/JRubyVisitor.java create mode 100644 src/main/java/com/educoder/bridge/tmp/JavaBuiltInType.java create mode 100644 src/main/java/com/educoder/bridge/tmp/JavaImportLookupStrategy.java create mode 100644 src/main/java/com/educoder/bridge/tmp/JavaListener.java create mode 100644 src/main/java/com/educoder/bridge/tmp/JavaVarResolveTest.java create mode 100644 src/main/java/com/educoder/bridge/tmp/JchService.java create mode 100644 src/main/java/com/educoder/bridge/tmp/Main.java create mode 100644 src/main/java/com/educoder/bridge/tmp/MatrixLevelReducer.java create mode 100644 src/main/java/com/educoder/bridge/tmp/ParserTest.java create mode 100644 src/main/java/com/educoder/bridge/tmp/PomListener.java create mode 100644 src/main/java/com/educoder/bridge/tmp/PreprocessorHandler.java create mode 100644 src/main/java/com/educoder/bridge/tmp/PythonBuiltInType.java create mode 100644 src/main/java/com/educoder/bridge/tmp/PythonCodeListener.java create mode 100644 src/main/java/com/educoder/bridge/tmp/PythonImportTest.java create mode 100644 src/main/java/com/educoder/bridge/tmp/PythonLexerBase.java create mode 100644 src/main/java/com/educoder/bridge/tmp/PythonParameterTypeDedudceTest.java create mode 100644 src/main/java/com/educoder/bridge/tmp/RelationCounter.java create mode 100644 src/main/java/com/educoder/bridge/tmp/RubyHandlerContext.java create mode 100644 src/main/java/com/educoder/bridge/tmp/RubyParserHelper.java create mode 100644 src/main/java/com/educoder/bridge/tmp/RubyVarTest.java create mode 100644 src/main/java/com/educoder/bridge/tmp/TypeEntity.java create mode 100644 src/main/java/com/educoder/bridge/tmp/UnsolvedSymbolDumper.java create mode 100644 src/main/java/com/educoder/bridge/tmp/VarEntity.java create mode 100644 src/main/java/com/educoder/bridge/utils/Base64Util.java create mode 100644 src/main/resources/applicationContext.xml create mode 100644 src/main/resources/logback.xml create mode 100644 src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml create mode 100644 src/main/webapp/WEB-INF/pages/index.ftl create mode 100644 src/main/webapp/WEB-INF/web.xml create mode 100644 src/main/webapp/index.html create mode 100644 src/main/webapp/static/css/main.css create mode 100644 src/main/webapp/static/css/pure-min.css create mode 100644 src/main/webapp/static/css/reset.css create mode 100644 src/main/webapp/static/css/style.css create mode 100644 src/main/webapp/static/css/supersized.css create mode 100644 src/main/webapp/static/css/tooltip.css create mode 100644 src/main/webapp/static/css/xterm.css create mode 100644 src/main/webapp/static/image/backgrounds/1.jpg create mode 100644 src/main/webapp/static/image/backgrounds/2.jpg create mode 100644 src/main/webapp/static/image/backgrounds/3.jpg create mode 100644 src/main/webapp/static/image/facebook.png create mode 100644 src/main/webapp/static/image/favicon.ico create mode 100644 src/main/webapp/static/image/progress.gif create mode 100644 src/main/webapp/static/image/twitter.png create mode 100644 src/main/webapp/static/js/base64.js create mode 100644 src/main/webapp/static/js/formvalid.js create mode 100644 src/main/webapp/static/js/jquerymin.js create mode 100644 src/main/webapp/static/js/main.js create mode 100644 src/main/webapp/static/js/supersized-init.js create mode 100644 src/main/webapp/static/js/supersized.3.2.7.min.js create mode 100644 src/main/webapp/static/js/ws.js create mode 100644 src/main/webapp/static/js/xterm.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8ae05cd --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +.idea/ +*.iml +target/ +2017* +.project +.classpath +.settings/ +*.log +bin/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3d2baa8 --- /dev/null +++ b/pom.xml @@ -0,0 +1,257 @@ + + 4.0.0 + com.educoder + webssh + war + 1.0-SNAPSHOT + webssh Maven Webapp + http://maven.apache.org + + + 2.6.1 + 4.3.6.RELEASE + 2.3.25-incubating + 0.1.54 + 7.0 + 2.6 + 2.4 + 1.7.21 + 1.2.20 + 2.8.6 + 1.10 + + 1.8 + 1.8 + UTF-8 + + + + + + org.freemarker + freemarker + ${freemarker.version} + + + + com.jcraft + jsch + ${jsch.version} + + + + io.springfox + springfox-swagger2 + ${swagger2.version} + + + io.springfox + springfox-swagger-ui + ${swagger2.version} + + + + org.springframework + spring-webmvc + ${spring.version} + + + + org.springframework + spring-websocket + ${spring.version} + + + + org.springframework + spring-context-support + ${spring.version} + + + + org.springframework + spring-test + ${spring.version} + + + + javax + javaee-api + ${javax.version} + provided + + + + commons-lang + commons-lang + ${commons-lang.version} + + + + commons-io + commons-io + ${commons-io.version} + + + + junit + junit + 4.12 + test + + + + org.springframework + spring-aop + 4.3.6.RELEASE + + + + org.aspectj + aspectjrt + 1.8.10 + + + + org.springframework + spring-aspects + ${spring.version} + + + + org.springframework + spring-test + ${spring.version} + + + + ch.qos.logback + logback-classic + 1.2.3 + + + org.logback-extensions + logback-ext-spring + 0.1.4 + + + + com.alibaba + fastjson + ${fastjson.version} + + + + commons-codec + commons-codec + ${codec.version} + + + + com.fasterxml.jackson.core + jackson-core + ${jackson.version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + true + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.0.0 + + + package + + shade + + + ${project.build.directory}/dependency-reduced-pom.xml + + + META-INF/spring.handlers + + + META-INF/spring.schemas + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + org.apache.tomcat.maven + tomcat7-maven-plugin + 2.2 + +
0.0.0.0
+ 9001 + / + UTF-8 + webssh + tomcat7 +
+
+ + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + 1.8 + 1.8 + + +
+ + webssh +
+
diff --git a/src/main/go-mod/excel.go b/src/main/go-mod/excel.go new file mode 100644 index 0000000..1bfb4a0 --- /dev/null +++ b/src/main/go-mod/excel.go @@ -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 +} diff --git a/src/main/go-mod/func.go b/src/main/go-mod/func.go new file mode 100644 index 0000000..d88120a --- /dev/null +++ b/src/main/go-mod/func.go @@ -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 +} \ No newline at end of file diff --git a/src/main/go-mod/main.go b/src/main/go-mod/main.go new file mode 100644 index 0000000..b9754f0 --- /dev/null +++ b/src/main/go-mod/main.go @@ -0,0 +1,4 @@ +func main(){ + printf('hello wold') + io.out('ok') + } \ No newline at end of file diff --git a/src/main/go-mod/struct.go b/src/main/go-mod/struct.go new file mode 100644 index 0000000..1de4533 --- /dev/null +++ b/src/main/go-mod/struct.go @@ -0,0 +1,7 @@ +package test +type Books struct { + title string + author string + subject string + book_id int +} diff --git a/src/main/java/com/educoder/bridge/controller/BaseController.java b/src/main/java/com/educoder/bridge/controller/BaseController.java new file mode 100644 index 0000000..5fafd7d --- /dev/null +++ b/src/main/java/com/educoder/bridge/controller/BaseController.java @@ -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(); + } +} diff --git a/src/main/java/com/educoder/bridge/controller/MainController.java b/src/main/java/com/educoder/bridge/controller/MainController.java new file mode 100644 index 0000000..f232380 --- /dev/null +++ b/src/main/java/com/educoder/bridge/controller/MainController.java @@ -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; + } + +} diff --git a/src/main/java/com/educoder/bridge/handler/WebsshHandler.java b/src/main/java/com/educoder/bridge/handler/WebsshHandler.java new file mode 100644 index 0000000..34665bc --- /dev/null +++ b/src/main/java/com/educoder/bridge/handler/WebsshHandler.java @@ -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); + } +} diff --git a/src/main/java/com/educoder/bridge/model/SSHInfo.java b/src/main/java/com/educoder/bridge/model/SSHInfo.java new file mode 100644 index 0000000..4881f63 --- /dev/null +++ b/src/main/java/com/educoder/bridge/model/SSHInfo.java @@ -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; + } + +} diff --git a/src/main/java/com/educoder/bridge/model/SSHSession.java b/src/main/java/com/educoder/bridge/model/SSHSession.java new file mode 100644 index 0000000..e9db868 --- /dev/null +++ b/src/main/java/com/educoder/bridge/model/SSHSession.java @@ -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; + } +} diff --git a/src/main/java/com/educoder/bridge/service/JchService.java b/src/main/java/com/educoder/bridge/service/JchService.java new file mode 100644 index 0000000..8657d1d --- /dev/null +++ b/src/main/java/com/educoder/bridge/service/JchService.java @@ -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 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 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)); + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/ASTStringUtilExt.java b/src/main/java/com/educoder/bridge/tmp/ASTStringUtilExt.java new file mode 100644 index 0000000..c148844 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/ASTStringUtilExt.java @@ -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 getTemplateParameters(IASTDeclSpecifier declSpecifier) { + List 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 "); + } + } + + 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 "); + } + } + + 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; + } + + + + +} \ No newline at end of file diff --git a/src/main/java/com/educoder/bridge/tmp/AbstractLangProcessor.java b/src/main/java/com/educoder/bridge/tmp/AbstractLangProcessor.java new file mode 100644 index 0000000..da5728c --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/AbstractLangProcessor.java @@ -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 potentialExternalDependencies; + private List 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 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 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 includePaths() { + if (this.includePaths ==null) { + this.includePaths = buildIncludePath(); + } + return includePaths; + } + + private List buildIncludePath() { + includePaths = new ArrayList(); + 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 supportedRelations(); + + public Set 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;}; +} diff --git a/src/main/java/com/educoder/bridge/tmp/AliasEntity.java b/src/main/java/com/educoder/bridge/tmp/AliasEntity.java new file mode 100644 index 0000000..219fba5 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/AliasEntity.java @@ -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 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 getResolvedTypeParameters() { + if (!(referToEntity instanceof DecoratedEntity)) + return new ArrayList<>(); + DecoratedEntity origin = (DecoratedEntity) referToEntity; + return origin.getResolvedTypeParameters(); + } + + public Collection getResolvedAnnotations() { + if (!(referToEntity instanceof DecoratedEntity)) + return new ArrayList<>(); + DecoratedEntity origin = (DecoratedEntity) referToEntity; + return origin.getResolvedAnnotations(); + } + + public ArrayList getVars() { + if (!(referToEntity instanceof ContainerEntity)) + return new ArrayList<>(); + ContainerEntity origin = (ContainerEntity) referToEntity; + return origin.getVars(); + } + + public ArrayList 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 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 getResolvedMixins() { + if (!(referToEntity instanceof ContainerEntity)) + return new ArrayList<>(); + ContainerEntity origin = (ContainerEntity) referToEntity; + return origin.getResolvedMixins(); + } + + public Collection getInheritedTypes() { + if (referToEntity instanceof TypeEntity) + return ((TypeEntity) referToEntity).getInheritedTypes(); + return new ArrayList<>(); + } + + public Collection 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 getReturnTypes() { + if (!(referToEntity instanceof FunctionEntity)) + return new ArrayList<>(); + FunctionEntity origin = (FunctionEntity) referToEntity; + return origin.getReturnTypes(); + } + + public TypeEntity getType() { + return referToEntity.getType(); + } + + public Collection getParameters() { + if (!(referToEntity instanceof FunctionEntity)) + return new ArrayList<>(); + FunctionEntity origin = (FunctionEntity) referToEntity; + return origin.getParameters(); + } + + public Collection 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 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; + } + + +} diff --git a/src/main/java/com/educoder/bridge/tmp/AnnotationProcessor.java b/src/main/java/com/educoder/bridge/tmp/AnnotationProcessor.java new file mode 100644 index 0000000..cf40e49 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/AnnotationProcessor.java @@ -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 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 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 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; + } + } + + + + +} diff --git a/src/main/java/com/educoder/bridge/tmp/BindingResolver.java b/src/main/java/com/educoder/bridge/tmp/BindingResolver.java new file mode 100644 index 0000000..66134db --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/BindingResolver.java @@ -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 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 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 iterator = repo.sortedFileIterator(); + while(iterator.hasNext()) { + Entity entity= iterator.next(); + entity.inferEntities(this); + } + } + + + @Override + public Collection getImportedRelationEntities(List importedNames) { + return importLookupStrategy.getImportedRelationEntities(importedNames); + } + + @Override + public Collection getImportedTypes(List importedNames, FileEntity fileEntity) { + HashSet unsolved = new HashSet(); + Collection 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 getImportedFiles(List 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 calculateCandidateTypes(VarEntity fromEntity, List functionCalls) { + if (buildInTypeManager.isBuildInTypeMethods(functionCalls)) { + return new ArrayList<>(); + } + if (!isDuckTypingDeduce) + return new ArrayList<>(); + return searchTypesInRepo(fromEntity, functionCalls); + } + + private List searchTypesInRepo(VarEntity fromEntity, List functionCalls) { + List types = new ArrayList<>(); + Iterator 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; + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/BuiltInType.java b/src/main/java/com/educoder/bridge/tmp/BuiltInType.java new file mode 100644 index 0000000..1771cc9 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/BuiltInType.java @@ -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 builtInType = new HashSet<>(); + private Set builtInPrefix = new HashSet<>(); + private Set 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 functionCalls) { + return false; + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/CandidateTypes.java b/src/main/java/com/educoder/bridge/tmp/CandidateTypes.java new file mode 100644 index 0000000..04d669b --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/CandidateTypes.java @@ -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 candidateTypes; + + public CandidateTypes() { + candidateTypes = new ArrayList<>(); + } + public CandidateTypes(List candidateTypes, Integer id) { + super(GenericName.build("candidateTypes"), null, id); + this.candidateTypes = candidateTypes; + } + + public List getCandidateTypes() { + return candidateTypes; + } + + @Override + public Collection getInheritedTypes() { + List result = new ArrayList<>(); + for (TypeEntity type:candidateTypes) { + result.addAll(type.getInheritedTypes()); + } + return result; + } + + @Override + public Collection getImplementedTypes() { + List result = new ArrayList<>(); + for (TypeEntity type:candidateTypes) { + result.addAll(type.getImplementedTypes()); + } + return result; + } + + @Override + public ArrayList getFunctions() { + ArrayList result = new ArrayList<>(); + for (TypeEntity type:candidateTypes) { + result.addAll(type.getFunctions()); + } + return result; + } + + @Override + public TypeEntity getInheritedType() { + return inheritedType; + } + @Override + public List lookupFunctionInVisibleScope(GenericName functionName) { + List functions = new ArrayList<>(); + for (TypeEntity type:candidateTypes) { + List 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 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 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 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 getResolvedTypeParameters() { + System.err.println("error: getResolvedTypeParameters should not been invoked"); + return super.getResolvedTypeParameters(); + } + + @Override + public Collection 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 identiferToEntities(IBindingResolver bindingResolver, Collection 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 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 getChildren() { + List 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 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; + } + + + +} diff --git a/src/main/java/com/educoder/bridge/tmp/CdtCppFileParser.java b/src/main/java/com/educoder/bridge/tmp/CdtCppFileParser.java new file mode 100644 index 0000000..42360f6 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/CdtCppFileParser.java @@ -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 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 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 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; + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/ContainerEntity.java b/src/main/java/com/educoder/bridge/tmp/ContainerEntity.java new file mode 100644 index 0000000..b299763 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/ContainerEntity.java @@ -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 vars; + private ArrayList functions; + WeakReference> expressionWeakReference; + private ArrayList expressionList; + private int expressionCount = 0; + private Collection mixins; + private Collection resolvedMixins; + + private ArrayList vars() { + if (vars==null) + vars = new ArrayList<>(); + return this.vars; + } + + private Collection mixins() { + if (mixins==null) + mixins = new ArrayList<>(); + return this.mixins; + } + + private ArrayList 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 getVars() { + if (vars==null) + return new ArrayList<>(); + return this.vars(); + } + + public void addFunction(FunctionEntity functionEntity) { + this.functions().add(functionEntity); + } + + public ArrayList getFunctions() { + if (functions==null) + return new ArrayList<>(); + return this.functions; + } + + public HashMap expressions() { + if (expressionWeakReference==null) + expressionWeakReference= new WeakReference>(new HashMap<>()); + HashMap 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 getMixins() { + if (mixins==null) + return new ArrayList<>(); + return mixins; + } + + private Collection identiferToContainerEntity(IBindingResolver bindingResolver, Collection identifiers) { + if (identifiers.size()==0) return null; + ArrayList 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 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) 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 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 lookupFunctionInVisibleScope(GenericName functionName) { + List 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 getResolvedMixins() { + if (resolvedMixins==null) return new ArrayList<>(); + return resolvedMixins; + } + + HashMap> 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 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; + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/CppBuiltInType.java b/src/main/java/com/educoder/bridge/tmp/CppBuiltInType.java new file mode 100644 index 0000000..e94528f --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/CppBuiltInType.java @@ -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", "", + "__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[] {"__"}; + } +} \ No newline at end of file diff --git a/src/main/java/com/educoder/bridge/tmp/CppImportLookupStrategy.java b/src/main/java/com/educoder/bridge/tmp/CppImportLookupStrategy.java new file mode 100644 index 0000000..b723455 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/CppImportLookupStrategy.java @@ -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 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 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 > includedFiles = new HashMap<>(); + private HashSet getIncludedFiles(FileEntity fileEntity) { + + if (includedFiles.containsKey(fileEntity.getId())) { + return includedFiles.get(fileEntity.getId()); + } + HashSet fileSet = new HashSet<>(); + foundIncludedFiles(fileSet, fileEntity.getImportedFiles()); + includedFiles.put(fileEntity.getId(), fileSet); + return fileSet; + } + + private void foundIncludedFiles(HashSet fileSet, Collection 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 getImportedRelationEntities(List importedList) { + ArrayList 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 getImportedTypes(List importedList, Set unsolvedBindings) { + ArrayList 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 getImportedFiles(List importedList) { + return getImportedRelationEntities(importedList); + } + + @Override + public boolean supportGlobalNameLookup() { + return false; + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/CppVisitor.java b/src/main/java/com/educoder/bridge/tmp/CppVisitor.java new file mode 100644 index 0000000..e291c7f --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/CppVisitor.java @@ -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 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 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 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 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 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); + } +} \ No newline at end of file diff --git a/src/main/java/com/educoder/bridge/tmp/DecoratedEntity.java b/src/main/java/com/educoder/bridge/tmp/DecoratedEntity.java new file mode 100644 index 0000000..a18344d --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/DecoratedEntity.java @@ -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 annotations; + private Collection resolvedAnnotations; + private Collection 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 parameters) { + this.getRawName().appendArguments(parameters); + } + + + + public void addTypeParameter(GenericName parameter) { + this.getRawName().appendArguments(parameter); + } + + protected void appendTypeParameters(Collection 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 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 typeParametersToEntities(IBindingResolver bindingResolver) { + ArrayList r = new ArrayList<>(); + for (GenericName typeParameter:this.getRawName().getArguments()) { + toEntityList(bindingResolver, r,typeParameter); + } + return r; + } + + protected void toEntityList(IBindingResolver bindingResolver, ArrayList 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 getResolvedTypeParameters() { + if (resolvedTypeParameters==null) + return new ArrayList<>(); + return resolvedTypeParameters; + } + + + public Collection 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 identiferToEntities(IBindingResolver bindingResolver, Collection identifiers) { + if (identifiers==null) return null; + if (identifiers.size()==0) return null; + ArrayList 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; + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/DependencyGenerator.java b/src/main/java/com/educoder/bridge/tmp/DependencyGenerator.java new file mode 100644 index 0000000..6f943f5 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/DependencyGenerator.java @@ -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 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 typeFilter) { + DependencyMatrix dependencyMatrix = new DependencyMatrix(typeFilter); + Iterator 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 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 expandEntity(Entity relatedEntity) { + List 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; + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/DependencyMatrix.java b/src/main/java/com/educoder/bridge/tmp/DependencyMatrix.java new file mode 100644 index 0000000..f74cc84 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/DependencyMatrix.java @@ -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 dependencyPairs = new HashMap<>(); + private ArrayList nodes = new ArrayList<>(); + private HashMap nodeIdToName = new HashMap<>(); + private List typeFilter; + public DependencyMatrix() { + } + public DependencyMatrix(int size) { + dependencyPairs = new HashMap<>(size); + } + public DependencyMatrix(List typeFilter) { + this.typeFilter = typeFilter; + } + public Collection 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 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 getNodes() { + return nodes; + } + + + public DependencyMatrix reWriteFilenamePattern(FilenameWritter filenameRewritter) { + this.nodeIdToName = new HashMap<>(); + for (int i=0;i { + private static final long serialVersionUID = 1L; + public SupportedLangs() { super( LangProcessorRegistration.getRegistry().getLangs()); } + } + + public static class SupportedTypes extends ArrayList { + 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 . " + + "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 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; + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/EclipseTestBase_No_ResponseDuirngTypeResolve.java b/src/main/java/com/educoder/bridge/tmp/EclipseTestBase_No_ResponseDuirngTypeResolve.java new file mode 100644 index 0000000..25f2020 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/EclipseTestBase_No_ResponseDuirngTypeResolve.java @@ -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 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; + } +} \ No newline at end of file diff --git a/src/main/java/com/educoder/bridge/tmp/Entity.java b/src/main/java/com/educoder/bridge/tmp/Entity.java new file mode 100644 index 0000000..75174c5 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/Entity.java @@ -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 children; + ArrayList relations; + private Entity actualReferTo = null; + private boolean inScope = true; + protected HashMap 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 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 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 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 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; + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/EntityExtractTest.java b/src/main/java/com/educoder/bridge/tmp/EntityExtractTest.java new file mode 100644 index 0000000..9c5e1a1 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/EntityExtractTest.java @@ -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_")); + /* + 1.00 + 3.1.4 + Apache ActiveMQ + activemq-${project.version} */ + 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_")); + /* + 1.00 + 3.1.4 + Apache ActiveMQ + activemq-${project.version}--${activeio-version} */ + 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")); + } +} \ No newline at end of file diff --git a/src/main/java/com/educoder/bridge/tmp/ExcelXlsFormatDependencyDumper.java b/src/main/java/com/educoder/bridge/tmp/ExcelXlsFormatDependencyDumper.java new file mode 100644 index 0000000..6831f18 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/ExcelXlsFormatDependencyDumper.java @@ -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 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 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"); + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/ExcelXlsxFormatDependencyDumper.java b/src/main/java/com/educoder/bridge/tmp/ExcelXlsxFormatDependencyDumper.java new file mode 100644 index 0000000..c27b68a --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/ExcelXlsxFormatDependencyDumper.java @@ -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 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 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"); + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/Expression.java b/src/main/java/com/educoder/bridge/tmp/Expression.java new file mode 100644 index 0000000..5639248 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/Expression.java @@ -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 deducedTypeVars = new ArrayList<>(); + private List deducedTypeVarsId = new ArrayList<>(); + + private transient List deducedTypeFunctions= new ArrayList<>(); + private List 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 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 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 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 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("")) return true; + if (name.toLowerCase().equals("")) 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; + } +} \ No newline at end of file diff --git a/src/main/java/com/educoder/bridge/tmp/ExpressionUsage.java b/src/main/java/com/educoder/bridge/tmp/ExpressionUsage.java new file mode 100644 index 0000000..2d9762a --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/ExpressionUsage.java @@ -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 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 getName(Testlist_star_exprContext testlist_star_expr) { + List 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 + | 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(""); + expression.setIdentifier(""); + 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{ + private List names; + NameCollector(List names){ + this.names = names; + } + @Override + public Void visitAtom(AtomContext ctx) { + if (ctx.name()!=null) + names.add(ctx.name().getText()); + return super.visitAtom(ctx); + } +} \ No newline at end of file diff --git a/src/main/java/com/educoder/bridge/tmp/ExtractText.java b/src/main/java/com/educoder/bridge/tmp/ExtractText.java new file mode 100644 index 0000000..4ba8951 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/ExtractText.java @@ -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); + } + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/FileEntity.java b/src/main/java/com/educoder/bridge/tmp/FileEntity.java new file mode 100644 index 0000000..8765a3a --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/FileEntity.java @@ -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 importedNames = new ArrayList<>(); + private boolean isInProjectScope = false; + private Collection importedRelationEntities = new ArrayList<>(); + private Collection importedFiles = new ArrayList<>(); + private Collection importedTypes = new ArrayList<>(); + private List 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 getImportedRelationEntities() { + return importedRelationEntities; + } + + public Collection getImportedFiles() { + return importedFiles; + } + + public Collection getImportedTypes() { + return importedTypes; + } + + public List getDeclaredTypes() { + return this.declaredTypes; + } + + public void addType(TypeEntity currentTypeEntity) { + this.declaredTypes.add(currentTypeEntity); + } + + public Set getImportedFilesInAllLevel() { + if (importedFileCollector==null) + importedFileCollector = new ImportedFileCollector(this); + + return importedFileCollector.getFiles(); + } + + public List getImportedNames() { + return importedNames; + } + + public void cacheAllExpressions() { + this.cacheChildExpressions(); + } + + + @Override + public Entity getByName(String name, HashSet 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); + } + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/FormalParameterListContextHelper.java b/src/main/java/com/educoder/bridge/tmp/FormalParameterListContextHelper.java new file mode 100644 index 0000000..4c24ca9 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/FormalParameterListContextHelper.java @@ -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 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 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 getAnnotations() { + return annotations; + } + + +} diff --git a/src/main/java/com/educoder/bridge/tmp/FunctionEntity.java b/src/main/java/com/educoder/bridge/tmp/FunctionEntity.java new file mode 100644 index 0000000..91f7e7a --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/FunctionEntity.java @@ -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 returnTypeIdentifiers = new ArrayList<>(); + Collection parameters; + Collection throwTypesIdentifiers = new ArrayList<>(); + private Collection returnTypes = new ArrayList<>(); + private Collection 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 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 throwedType) { + throwTypesIdentifiers.addAll(throwedType); + } + + @Override + public void inferLocalLevelEntities(IBindingResolver bindingResolver) { + for (VarEntity param:parameters) { + param.fillCandidateTypes(bindingResolver); + param.inferLocalLevelEntities(bindingResolver); + } + if (returnTypes.size() typeEntities = typeParametersToEntities(bindingResolver, returnTypeName); + this.appendTypeParameters(typeEntities); + } + } + if (throwTypes.size() typeParametersToEntities(IBindingResolver bindingResolver, GenericName name) { + ArrayList r = new ArrayList<>(); + for (GenericName typeParameter:name.getArguments()) { + toEntityList(bindingResolver, r,typeParameter); + } + return r; + } + + + public Collection getParameters() { + return parameters; + } + public Collection 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); + } + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/GenericName.java b/src/main/java/com/educoder/bridge/tmp/GenericName.java new file mode 100644 index 0000000..1645f52 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/GenericName.java @@ -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 arguments; + public GenericName(String name) { + this.name = name.toCharArray(); + } + public GenericName(String name, List 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 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 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 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; + } + + +} diff --git a/src/main/java/com/educoder/bridge/tmp/GoImportLookupStrategy.java b/src/main/java/com/educoder/bridge/tmp/GoImportLookupStrategy.java new file mode 100644 index 0000000..cda04d7 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/GoImportLookupStrategy.java @@ -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 getImportedRelationEntities(List importedList) { + ArrayList 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 getImportedTypes(List importedList, Set unsolvedBindings) { + ArrayList 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 getImportedFiles(List importedList) { + return new ArrayList(); + } + + + @Override + public boolean supportGlobalNameLookup() { + return true; + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/GoListener.java b/src/main/java/com/educoder/bridge/tmp/GoListener.java new file mode 100644 index 0000000..b8fafe7 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/GoListener.java @@ -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() { + + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/GoParserBase.java b/src/main/java/com/educoder/bridge/tmp/GoParserBase.java new file mode 100644 index 0000000..9f3c099 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/GoParserBase.java @@ -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 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); + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/HandlerContext.java b/src/main/java/com/educoder/bridge/tmp/HandlerContext.java new file mode 100644 index 0000000..a18547c --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/HandlerContext.java @@ -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(); + 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 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 foundVarDefinitions(List varNames, String type, List 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 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 entityStack = new Stack(); + + 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); + } + + +} \ No newline at end of file diff --git a/src/main/java/com/educoder/bridge/tmp/InMemoryEntityRepo.java b/src/main/java/com/educoder/bridge/tmp/InMemoryEntityRepo.java new file mode 100644 index 0000000..435676b --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/InMemoryEntityRepo.java @@ -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{ + + private Iterator> entryIterator; + + public EntityMapIterator(Set> entries) { + this.entryIterator = entries.iterator(); + } + @Override + public boolean hasNext() { + return entryIterator.hasNext(); + } + + @Override + public Entity next() { + return entryIterator.next().getValue(); + } + + } + + private Map allEntieisByName; + private Map allEntitiesById; + private List 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 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 getFileEntities() { + return allFileEntitiesByOrder; + } + + @Override + public Iterator 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); + } + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/IncludeRelationTest.java b/src/main/java/com/educoder/bridge/tmp/IncludeRelationTest.java new file mode 100644 index 0000000..3356fb4 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/IncludeRelationTest.java @@ -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 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])); + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/JDataBuilder.java b/src/main/java/com/educoder/bridge/tmp/JDataBuilder.java new file mode 100644 index 0000000..5e44240 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/JDataBuilder.java @@ -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 files = dependencyMatrix.getNodes(); + Collection dependencyPairs = dependencyMatrix.getDependencyPairs(); + ArrayList 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 buildCellObjects(Collection dependencyPairs) { + ArrayList cellObjects = new ArrayList(); + + for (DependencyPair dependencyPair : dependencyPairs) { + Map valueObject = buildValueObject(dependencyPair.getDependencies()); + List 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 buildDetails(Collection dependencies) { + List 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 buildValueObject(Collection dependencies) { + Map valueObject = new HashMap(); + for (DependencyValue dependency : dependencies) { + valueObject.put(dependency.getType(), (float) dependency.getWeight()); + } + return valueObject; + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/JRubyVisitor.java b/src/main/java/com/educoder/bridge/tmp/JRubyVisitor.java new file mode 100644 index 0000000..052b755 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/JRubyVisitor.java @@ -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 params = getParams(node); + context.processSpecialFuncCall(fname, params, node.getPosition().getStartLine()+1); + return super.visitFCallNode(node); + } + + private Collection getParams(IArgumentNode node) { + Node args = node.getArgs(); + Collection 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 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 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); + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/JavaBuiltInType.java b/src/main/java/com/educoder/bridge/tmp/JavaBuiltInType.java new file mode 100644 index 0000000..7402f58 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/JavaBuiltInType.java @@ -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", + "" + }; + } + @Override + protected String[] getBuiltInTypePrefix() { + return new String[]{ + "java.","javax.","com.sun." + }; + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/JavaImportLookupStrategy.java b/src/main/java/com/educoder/bridge/tmp/JavaImportLookupStrategy.java new file mode 100644 index 0000000..d791697 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/JavaImportLookupStrategy.java @@ -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 getImportedRelationEntities(List importedList) { + ArrayList 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 getImportedTypes(List importedList,Set unsolvedBindings) { + ArrayList 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 getImportedFiles(List importedList) { + return new ArrayList(); + } + + + @Override + public boolean supportGlobalNameLookup() { + return true; + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/JavaListener.java b/src/main/java/com/educoder/bridge/tmp/JavaListener.java new file mode 100644 index 0000000..326e788 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/JavaListener.java @@ -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 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 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 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 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 varNames = VariableDeclaratorsContextHelper.getVariables(ctx.variableDeclarators()); + String type = ClassTypeContextHelper.getClassName(ctx.typeType()); + List typeArguments = ClassTypeContextHelper.getTypeArguments(ctx.typeType()); + List 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 typeArguments = ClassTypeContextHelper.getTypeArguments(ctx.typeType()); + List 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 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 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 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 , <> 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())); + } + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/JavaVarResolveTest.java b/src/main/java/com/educoder/bridge/tmp/JavaVarResolveTest.java new file mode 100644 index 0000000..4660844 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/JavaVarResolveTest.java @@ -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"); + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/JchService.java b/src/main/java/com/educoder/bridge/tmp/JchService.java new file mode 100644 index 0000000..8657d1d --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/JchService.java @@ -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 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 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)); + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/Main.java b/src/main/java/com/educoder/bridge/tmp/Main.java new file mode 100644 index 0000000..260ae11 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/Main.java @@ -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 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 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; + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/MatrixLevelReducer.java b/src/main/java/com/educoder/bridge/tmp/MatrixLevelReducer.java new file mode 100644 index 0000000..4cb0d5d --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/MatrixLevelReducer.java @@ -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 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 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() { + @Override + public int compare(String o1, String o2) { + return o1.compareTo(o2); + } + }); + DependencyMatrix ordered = new DependencyMatrix(); + for (int id=0;id 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; + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/ParserTest.java b/src/main/java/com/educoder/bridge/tmp/ParserTest.java new file mode 100644 index 0000000..4df73d0 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/ParserTest.java @@ -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 resolveAllBindings() { + Set result = bindingResolver.resolveAllBindings(langProcessor.isEagerExpressionResolve()); + new RelationCounter(entityRepo,langProcessor, bindingResolver).computeRelations(); + return result; + } + + protected Set resolveAllBindings(boolean callAsImpl) { + Set 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 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 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 types = function.getReturnTypes(); + for (Entity type:types) { + if (type.getRawName().uniqName().equals(name)) { + return; + } + } + fail("cannot found return type with rawname " + name); + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/PomListener.java b/src/main/java/com/educoder/bridge/tmp/PomListener.java new file mode 100644 index 0000000..8bcd2f1 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/PomListener.java @@ -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 includePaths; + private IBindingResolver IBindingResolver; + private Stack pomCoords= new Stack<>(); + + public PomListener(String fileFullPath, EntityRepo entityRepo, List 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; + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/PreprocessorHandler.java b/src/main/java/com/educoder/bridge/tmp/PreprocessorHandler.java new file mode 100644 index 0000000..decaedf --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/PreprocessorHandler.java @@ -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 includePaths; + private String inputSrcPath; + private HashSet allFiles = new HashSet<>(); + public PreprocessorHandler(String inputSrcPath, List 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 getDirectIncludedFiles(IASTPreprocessorStatement[] statements, String fileLocation) { + ArrayList includedFullPathNames = new ArrayList<>(); + for (int statementIndex=0;statementIndex 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 getIncludePaths() { + return includePaths; + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/PythonBuiltInType.java b/src/main/java/com/educoder/bridge/tmp/PythonBuiltInType.java new file mode 100644 index 0000000..934c870 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/PythonBuiltInType.java @@ -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 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 functionCalls) { + for (TypeEntity type:buildInTypes) { + FunctionMatcher functionMatcher = new FunctionMatcher(type.getFunctions()); + if (functionMatcher.containsAll(functionCalls)) { + return true; + } + } + return false; + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/PythonCodeListener.java b/src/main/java/com/educoder/bridge/tmp/PythonCodeListener.java new file mode 100644 index 0000000..e406a13 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/PythonCodeListener.java @@ -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 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 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 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 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 =""; + String name = getName(ctx.name()); + if (name!=null) { + functionName = name; + } + + FunctionEntity method = context.foundMethodDeclarator(functionName,ctx.getStart().getLine()); + if (ctx.typedargslist()!=null) { + List 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 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 getParameterList(List def_parameters) { + List 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 getArgList(ArglistContext arglist) { + List 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); + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/PythonImportTest.java b/src/main/java/com/educoder/bridge/tmp/PythonImportTest.java new file mode 100644 index 0000000..6b36c4f --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/PythonImportTest.java @@ -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")); + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/PythonLexerBase.java b/src/main/java/com/educoder/bridge/tmp/PythonLexerBase.java new file mode 100644 index 0000000..ca98458 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/PythonLexerBase.java @@ -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 _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); + } +} + diff --git a/src/main/java/com/educoder/bridge/tmp/PythonParameterTypeDedudceTest.java b/src/main/java/com/educoder/bridge/tmp/PythonParameterTypeDedudceTest.java new file mode 100644 index 0000000..8fcb6fd --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/PythonParameterTypeDedudceTest.java @@ -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); + } + + +} + diff --git a/src/main/java/com/educoder/bridge/tmp/RelationCounter.java b/src/main/java/com/educoder/bridge/tmp/RelationCounter.java new file mode 100644 index 0000000..c3a163f --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/RelationCounter.java @@ -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 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 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 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)); + } + } + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/RubyHandlerContext.java b/src/main/java/com/educoder/bridge/tmp/RubyHandlerContext.java new file mode 100644 index 0000000..2ce057c --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/RubyHandlerContext.java @@ -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 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); + } + } + } + + + +} diff --git a/src/main/java/com/educoder/bridge/tmp/RubyParserHelper.java b/src/main/java/com/educoder/bridge/tmp/RubyParserHelper.java new file mode 100644 index 0000000..bfb80fb --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/RubyParserHelper.java @@ -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 getName(AssignableNode ctx) { + List 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); + } +} diff --git a/src/main/java/com/educoder/bridge/tmp/RubyVarTest.java b/src/main/java/com/educoder/bridge/tmp/RubyVarTest.java new file mode 100644 index 0000000..fcd7dcc --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/RubyVarTest.java @@ -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"); + } +} + diff --git a/src/main/java/com/educoder/bridge/tmp/TypeEntity.java b/src/main/java/com/educoder/bridge/tmp/TypeEntity.java new file mode 100644 index 0000000..6282ef4 --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/TypeEntity.java @@ -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 inheritedTypes = new ArrayList<>(); + Collection implementedTypes = new ArrayList<>(); + Collection inhertedTypeIdentifiers; + Collection 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 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 getInheritedTypes() { + return inheritedTypes; + } + + public Collection getImplementedTypes() { + return implementedTypes; + } + + public TypeEntity getInheritedType() { + return inheritedType; + } + + @Override + public FunctionEntity lookupFunctionLocally(GenericName functionName) { + Collection searchedTypes = new ArrayList<>(); + return lookupFunctionLocally(functionName,searchedTypes); + } + + private FunctionEntity lookupFunctionLocally(GenericName functionName, Collection 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 searchedTypes = new ArrayList<>(); + return lookupVarLocally(varName,searchedTypes); + } + + private VarEntity lookupVarLocally(GenericName varName, Collection 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 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; + } + + +} diff --git a/src/main/java/com/educoder/bridge/tmp/UnsolvedSymbolDumper.java b/src/main/java/com/educoder/bridge/tmp/UnsolvedSymbolDumper.java new file mode 100644 index 0000000..874369f --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/UnsolvedSymbolDumper.java @@ -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 unsolved; + private String name; + private String outputDir; + private LeadingNameStripper leadingNameStripper; + + public UnsolvedSymbolDumper(Set 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> grouped = new TreeMap>(); + for (UnsolvedBindings symbol: unsolved) { + String depended = symbol.getRawName(); + String from = leadingNameStripper.stripFilename(symbol.getSourceDisplay()); + Set 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(); + } + } + +} diff --git a/src/main/java/com/educoder/bridge/tmp/VarEntity.java b/src/main/java/com/educoder/bridge/tmp/VarEntity.java new file mode 100644 index 0000000..f81bdbc --- /dev/null +++ b/src/main/java/com/educoder/bridge/tmp/VarEntity.java @@ -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 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 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 candidateTypes = bindingResolver.calculateCandidateTypes(this,this.functionCalls); + if (candidateTypes.size()>0) { + this.type = new CandidateTypes(candidateTypes, bindingResolver.getRepo().generateId()); + bindingResolver.getRepo().add(this.type); + } + } + } +} diff --git a/src/main/java/com/educoder/bridge/utils/Base64Util.java b/src/main/java/com/educoder/bridge/utils/Base64Util.java new file mode 100644 index 0000000..3ef71ed --- /dev/null +++ b/src/main/java/com/educoder/bridge/utils/Base64Util.java @@ -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); + } + +} diff --git a/src/main/resources/applicationContext.xml b/src/main/resources/applicationContext.xml new file mode 100644 index 0000000..620ed30 --- /dev/null +++ b/src/main/resources/applicationContext.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + 0 + UTF-8 + 0.########## + yyyy-MM-dd HH:mm:ss + true + ignore + + + + + + + + + + + + diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..fe3a44e --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,47 @@ + + + + + + + + + %d{MM-dd HH:mm:ss} [%thread] %-5level %logger{30} %M %L - %msg%n + + + + DEBUG + + + + + + UTF-8 + + %d{MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{30} %M - %msg%n%L + + + ERROR + + + ${log_path}error.%d{MM-dd}.log + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml b/src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml new file mode 100644 index 0000000..d81863c --- /dev/null +++ b/src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + .ftl + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/pages/index.ftl b/src/main/webapp/WEB-INF/pages/index.ftl new file mode 100644 index 0000000..bd2f036 --- /dev/null +++ b/src/main/webapp/WEB-INF/pages/index.ftl @@ -0,0 +1,61 @@ + + + + + + + JWebssh + + + + + + + + + + + + + + +
+

JWebssh

+
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+ +
+ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..e71e27e --- /dev/null +++ b/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,63 @@ + + + + educoder bridge + + + + contextConfigLocation + classpath:applicationContext.xml + + + + + org.springframework.web.context.ContextLoaderListener + + + + + + logbackConfigLocation + classpath:logback.xml + + + + ch.qos.logback.ext.spring.web.LogbackConfigListener + + + + mvc-dispatcher + org.springframework.web.servlet.DispatcherServlet + 1 + + + + mvc-dispatcher + / + + + + encodingFilter + org.springframework.web.filter.CharacterEncodingFilter + + encoding + UTF-8 + + + forceEncoding + true + + + + encodingFilter + /* + + + + + + + diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html new file mode 100644 index 0000000..e8bbf0a --- /dev/null +++ b/src/main/webapp/index.html @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/main/webapp/static/css/main.css b/src/main/webapp/static/css/main.css new file mode 100644 index 0000000..d0bb3c4 --- /dev/null +++ b/src/main/webapp/static/css/main.css @@ -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; +} \ No newline at end of file diff --git a/src/main/webapp/static/css/pure-min.css b/src/main/webapp/static/css/pure-min.css new file mode 100644 index 0000000..f0aa374 --- /dev/null +++ b/src/main/webapp/static/css/pure-min.css @@ -0,0 +1,11 @@ +/*! +Pure v0.6.0 +Copyright 2014 Yahoo! Inc. All rights reserved. +Licensed under the BSD License. +https://github.com/yahoo/pure/blob/master/LICENSE.md +*/ +/*! +normalize.css v^3.0 | MIT License | git.io/normalize +Copyright (c) Nicolas Gallagher and Jonathan Neal +*/ +/*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{letter-spacing:-.31em;*letter-spacing:normal;*word-spacing:-.43em;text-rendering:optimizespeed;font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;display:-webkit-flex;-webkit-flex-flow:row wrap;display:-ms-flexbox;-ms-flex-flow:row wrap;-ms-align-content:flex-start;-webkit-align-content:flex-start;align-content:flex-start}.opera-only :-o-prefocus,.pure-g{word-spacing:-.43em}.pure-u{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-g [class *="pure-u"]{font-family:sans-serif}.pure-u-1,.pure-u-1-1,.pure-u-1-2,.pure-u-1-3,.pure-u-2-3,.pure-u-1-4,.pure-u-3-4,.pure-u-1-5,.pure-u-2-5,.pure-u-3-5,.pure-u-4-5,.pure-u-5-5,.pure-u-1-6,.pure-u-5-6,.pure-u-1-8,.pure-u-3-8,.pure-u-5-8,.pure-u-7-8,.pure-u-1-12,.pure-u-5-12,.pure-u-7-12,.pure-u-11-12,.pure-u-1-24,.pure-u-2-24,.pure-u-3-24,.pure-u-4-24,.pure-u-5-24,.pure-u-6-24,.pure-u-7-24,.pure-u-8-24,.pure-u-9-24,.pure-u-10-24,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%;*width:4.1357%}.pure-u-1-12,.pure-u-2-24{width:8.3333%;*width:8.3023%}.pure-u-1-8,.pure-u-3-24{width:12.5%;*width:12.469%}.pure-u-1-6,.pure-u-4-24{width:16.6667%;*width:16.6357%}.pure-u-1-5{width:20%;*width:19.969%}.pure-u-5-24{width:20.8333%;*width:20.8023%}.pure-u-1-4,.pure-u-6-24{width:25%;*width:24.969%}.pure-u-7-24{width:29.1667%;*width:29.1357%}.pure-u-1-3,.pure-u-8-24{width:33.3333%;*width:33.3023%}.pure-u-3-8,.pure-u-9-24{width:37.5%;*width:37.469%}.pure-u-2-5{width:40%;*width:39.969%}.pure-u-5-12,.pure-u-10-24{width:41.6667%;*width:41.6357%}.pure-u-11-24{width:45.8333%;*width:45.8023%}.pure-u-1-2,.pure-u-12-24{width:50%;*width:49.969%}.pure-u-13-24{width:54.1667%;*width:54.1357%}.pure-u-7-12,.pure-u-14-24{width:58.3333%;*width:58.3023%}.pure-u-3-5{width:60%;*width:59.969%}.pure-u-5-8,.pure-u-15-24{width:62.5%;*width:62.469%}.pure-u-2-3,.pure-u-16-24{width:66.6667%;*width:66.6357%}.pure-u-17-24{width:70.8333%;*width:70.8023%}.pure-u-3-4,.pure-u-18-24{width:75%;*width:74.969%}.pure-u-19-24{width:79.1667%;*width:79.1357%}.pure-u-4-5{width:80%;*width:79.969%}.pure-u-5-6,.pure-u-20-24{width:83.3333%;*width:83.3023%}.pure-u-7-8,.pure-u-21-24{width:87.5%;*width:87.469%}.pure-u-11-12,.pure-u-22-24{width:91.6667%;*width:91.6357%}.pure-u-23-24{width:95.8333%;*width:95.8023%}.pure-u-1,.pure-u-1-1,.pure-u-5-5,.pure-u-24-24{width:100%}.pure-button{display:inline-block;zoom:1;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button{font-family:inherit;font-size:100%;padding:.5em 1em;color:#444;color:rgba(0,0,0,.8);border:1px solid #999;border:0 rgba(0,0,0,0);background-color:#E6E6E6;text-decoration:none;border-radius:2px}.pure-button-hover,.pure-button:hover,.pure-button:focus{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000', GradientType=0);background-image:-webkit-gradient(linear,0 0,0 100%,from(transparent),color-stop(40%,rgba(0,0,0,.05)),to(rgba(0,0,0,.1)));background-image:-webkit-linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1));background-image:-moz-linear-gradient(top,rgba(0,0,0,.05) 0,rgba(0,0,0,.1));background-image:-o-linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1));background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button:focus{outline:0}.pure-button-active,.pure-button:active{box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;border-color:#000\9}.pure-button[disabled],.pure-button-disabled,.pure-button-disabled:hover,.pure-button-disabled:focus,.pure-button-disabled:active{border:0;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);filter:alpha(opacity=40);-khtml-opacity:.4;-moz-opacity:.4;opacity:.4;cursor:not-allowed;box-shadow:none}.pure-button-hidden{display:none}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-form input[type=text],.pure-form input[type=password],.pure-form input[type=email],.pure-form input[type=url],.pure-form input[type=date],.pure-form input[type=month],.pure-form input[type=time],.pure-form input[type=datetime],.pure-form input[type=datetime-local],.pure-form input[type=week],.pure-form input[type=number],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=color],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-form input[type=color]{padding:.2em .5em}.pure-form input[type=text]:focus,.pure-form input[type=password]:focus,.pure-form input[type=email]:focus,.pure-form input[type=url]:focus,.pure-form input[type=date]:focus,.pure-form input[type=month]:focus,.pure-form input[type=time]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=week]:focus,.pure-form input[type=number]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=color]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;border-color:#129FEA}.pure-form input:not([type]):focus{outline:0;border-color:#129FEA}.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus,.pure-form input[type=checkbox]:focus{outline:thin solid #129FEA;outline:1px auto #129FEA}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input[type=text][disabled],.pure-form input[type=password][disabled],.pure-form input[type=email][disabled],.pure-form input[type=url][disabled],.pure-form input[type=date][disabled],.pure-form input[type=month][disabled],.pure-form input[type=time][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=week][disabled],.pure-form input[type=number][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=color][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input:not([type])[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form textarea:focus:invalid,.pure-form select:focus:invalid{color:#b94a48;border-color:#e9322d}.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus,.pure-form input[type=checkbox]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input[type=text],.pure-form-stacked input[type=password],.pure-form-stacked input[type=email],.pure-form-stacked input[type=url],.pure-form-stacked input[type=date],.pure-form-stacked input[type=month],.pure-form-stacked input[type=time],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=week],.pure-form-stacked input[type=number],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=color],.pure-form-stacked input[type=file],.pure-form-stacked select,.pure-form-stacked label,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-stacked input:not([type]){display:block;margin:.25em 0}.pure-form-aligned input,.pure-form-aligned textarea,.pure-form-aligned select,.pure-form-aligned .pure-help-inline,.pure-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}.pure-form input.pure-input-rounded,.pure-form .pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input,.pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus,.pure-form .pure-group textarea:focus{z-index:3}.pure-form .pure-group input:first-child,.pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}.pure-form .pure-group input:first-child:last-child,.pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}.pure-form .pure-group input:last-child,.pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form .pure-help-inline,.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}.pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=text],.pure-form input[type=password],.pure-form input[type=email],.pure-form input[type=url],.pure-form input[type=date],.pure-form input[type=month],.pure-form input[type=time],.pure-form input[type=datetime],.pure-form input[type=datetime-local],.pure-form input[type=week],.pure-form input[type=number],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=color],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=text],.pure-group input[type=password],.pure-group input[type=email],.pure-group input[type=url],.pure-group input[type=date],.pure-group input[type=month],.pure-group input[type=time],.pure-group input[type=datetime],.pure-group input[type=datetime-local],.pure-group input[type=week],.pure-group input[type=number],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=color]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0}.pure-form .pure-help-inline,.pure-form-message-inline,.pure-form-message{display:block;font-size:.75em;padding:.2em 0 .8em}}.pure-menu{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-list,.pure-menu-item{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-link,.pure-menu-heading{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-separator{display:inline-block;*display:inline;zoom:1;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-allow-hover:hover>.pure-menu-children,.pure-menu-active>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;padding:.5em 0}.pure-menu-horizontal.pure-menu-scrollable::-webkit-scrollbar{display:none}.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-link,.pure-menu-disabled,.pure-menu-heading{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent}.pure-menu-active>.pure-menu-link,.pure-menu-link:hover,.pure-menu-link:focus{background-color:#eee}.pure-menu-selected .pure-menu-link,.pure-menu-selected .pure-menu-link:visited{color:#000}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table td:first-child,.pure-table th:first-child{border-left-width:0}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0} \ No newline at end of file diff --git a/src/main/webapp/static/css/reset.css b/src/main/webapp/static/css/reset.css new file mode 100644 index 0000000..248aacf --- /dev/null +++ b/src/main/webapp/static/css/reset.css @@ -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; +} + diff --git a/src/main/webapp/static/css/style.css b/src/main/webapp/static/css/style.css new file mode 100644 index 0000000..e2073ba --- /dev/null +++ b/src/main/webapp/static/css/style.css @@ -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; } + + + diff --git a/src/main/webapp/static/css/supersized.css b/src/main/webapp/static/css/supersized.css new file mode 100644 index 0000000..a507e4d --- /dev/null +++ b/src/main/webapp/static/css/supersized.css @@ -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 } + diff --git a/src/main/webapp/static/css/tooltip.css b/src/main/webapp/static/css/tooltip.css new file mode 100644 index 0000000..978506f --- /dev/null +++ b/src/main/webapp/static/css/tooltip.css @@ -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; +} + diff --git a/src/main/webapp/static/css/xterm.css b/src/main/webapp/static/css/xterm.css new file mode 100644 index 0000000..e6f3909 --- /dev/null +++ b/src/main/webapp/static/css/xterm.css @@ -0,0 +1,2273 @@ +/** + * xterm.js: xterm, in the browser + * Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License) + * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) + * https://github.com/chjj/term.js + * + * 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. + * + * Originally forked from (with the author's permission): + * Fabrice Bellard's javascript vt100 for jslinux: + * http://bellard.org/jslinux/ + * Copyright (c) 2011 Fabrice Bellard + * The original design remains. The terminal itself + * has been extended to include xterm CSI codes, among + * other features. + */ + +/* + * Default style for xterm.js + */ +::-webkit-scrollbar { width:10px; height:10px; background-color: #F5F5F5; } +::-webkit-scrollbar-track { -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); background-color: #F5F5F5; } +::-webkit-scrollbar-thumb { -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); background-color: #ccc; } + +.terminal { + background-color: #000; + color: #fff; + font-family: courier-new, courier, monospace; + font-feature-settings: "liga" 0; + position: relative; + user-select: none; + -ms-user-select: none; + -webkit-user-select: none; +} + +.terminal.focus, +.terminal:focus { + outline: none; +} + +.terminal .xterm-helpers { + position: absolute; + top: 0; +} + +.terminal .xterm-helper-textarea { + /* + * HACK: to fix IE's blinking cursor + * Move textarea out of the screen to the far left, so that the cursor is not visible. + */ + position: absolute; + opacity: 0; + left: -9999em; + top: 0; + width: 0; + height: 0; + z-index: -10; + /** Prevent wrapping so the IME appears against the textarea at the correct position */ + white-space: nowrap; + overflow: hidden; + resize: none; +} + +.terminal a { + color: inherit; + text-decoration: none; +} + +.terminal a:hover { + cursor: pointer; + text-decoration: underline; +} + +.terminal a.xterm-invalid-link:hover { + cursor: text; + text-decoration: none; +} + +.terminal.focus:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar) .terminal-cursor { + background-color: #fff; + color: #000; +} + +.terminal:not(.focus) .terminal-cursor { + outline: 1px solid #fff; + outline-offset: -1px; + background-color: transparent; +} + +.terminal:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar).focus.xterm-cursor-blink-on .terminal-cursor { + background-color: transparent; + color: inherit; +} + +.terminal.xterm-cursor-style-bar .terminal-cursor, +.terminal.xterm-cursor-style-underline .terminal-cursor { + position: relative; +} +.terminal.xterm-cursor-style-bar .terminal-cursor::before, +.terminal.xterm-cursor-style-underline .terminal-cursor::before { + content: ""; + display: block; + position: absolute; + background-color: #fff; +} +.terminal.xterm-cursor-style-bar .terminal-cursor::before { + top: 0; + bottom: 0; + left: 0; + width: 1px; +} +.terminal.xterm-cursor-style-underline .terminal-cursor::before { + bottom: 0; + left: 0; + right: 0; + height: 1px; +} +.terminal.xterm-cursor-style-bar.focus.xterm-cursor-blink.xterm-cursor-blink-on .terminal-cursor::before, +.terminal.xterm-cursor-style-underline.focus.xterm-cursor-blink.xterm-cursor-blink-on .terminal-cursor::before { + background-color: transparent; +} +.terminal.xterm-cursor-style-bar.focus.xterm-cursor-blink .terminal-cursor::before, +.terminal.xterm-cursor-style-underline.focus.xterm-cursor-blink .terminal-cursor::before { + background-color: #fff; +} + +.terminal .composition-view { + background: #000; + color: #FFF; + display: none; + position: absolute; + white-space: nowrap; + z-index: 1; +} + +.terminal .composition-view.active { + display: block; +} + +.terminal .xterm-viewport { + /* On OS X this is required in order for the scroll bar to appear fully opaque */ + background-color: #000; + overflow-y: auto; +} + +.terminal .xterm-wide-char, +.terminal .xterm-normal-char { + display: inline-block; +} + +.terminal .xterm-rows { + position: absolute; + left: 0; + top: 0; +} + +.terminal .xterm-rows > div { + /* Lines containing spans and text nodes ocassionally wrap despite being the same width (#327) */ + white-space: nowrap; +} + +.terminal .xterm-scroll-area { + visibility: hidden; +} + +.terminal .xterm-char-measure-element { + display: inline-block; + visibility: hidden; + position: absolute; + left: -9999em; +} + +.terminal.enable-mouse-events { + /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */ + cursor: default; +} + +.terminal .xterm-selection { + position: absolute; + top: 0; + left: 0; + z-index: 1; + opacity: 0.3; + pointer-events: none; +} + +.terminal .xterm-selection div { + position: absolute; + background-color: #fff; +} + +/* + * Determine default colors for xterm.js + */ +.terminal .xterm-bold { + font-weight: bold; +} + +.terminal .xterm-underline { + text-decoration: underline; +} + +.terminal .xterm-blink { + text-decoration: blink; +} + +.terminal .xterm-hidden { + visibility: hidden; +} + +.terminal .xterm-color-0 { + color: #2e3436; +} + +.terminal .xterm-bg-color-0 { + background-color: #2e3436; +} + +.terminal .xterm-color-1 { + color: #cc0000; +} + +.terminal .xterm-bg-color-1 { + background-color: #cc0000; +} + +.terminal .xterm-color-2 { + color: #4e9a06; +} + +.terminal .xterm-bg-color-2 { + background-color: #4e9a06; +} + +.terminal .xterm-color-3 { + color: #c4a000; +} + +.terminal .xterm-bg-color-3 { + background-color: #c4a000; +} + +.terminal .xterm-color-4 { + color: #3465a4; +} + +.terminal .xterm-bg-color-4 { + background-color: #3465a4; +} + +.terminal .xterm-color-5 { + color: #75507b; +} + +.terminal .xterm-bg-color-5 { + background-color: #75507b; +} + +.terminal .xterm-color-6 { + color: #06989a; +} + +.terminal .xterm-bg-color-6 { + background-color: #06989a; +} + +.terminal .xterm-color-7 { + color: #d3d7cf; +} + +.terminal .xterm-bg-color-7 { + background-color: #d3d7cf; +} + +.terminal .xterm-color-8 { + color: #555753; +} + +.terminal .xterm-bg-color-8 { + background-color: #555753; +} + +.terminal .xterm-color-9 { + color: #ef2929; +} + +.terminal .xterm-bg-color-9 { + background-color: #ef2929; +} + +.terminal .xterm-color-10 { + color: #8ae234; +} + +.terminal .xterm-bg-color-10 { + background-color: #8ae234; +} + +.terminal .xterm-color-11 { + color: #fce94f; +} + +.terminal .xterm-bg-color-11 { + background-color: #fce94f; +} + +.terminal .xterm-color-12 { + color: #729fcf; +} + +.terminal .xterm-bg-color-12 { + background-color: #729fcf; +} + +.terminal .xterm-color-13 { + color: #ad7fa8; +} + +.terminal .xterm-bg-color-13 { + background-color: #ad7fa8; +} + +.terminal .xterm-color-14 { + color: #34e2e2; +} + +.terminal .xterm-bg-color-14 { + background-color: #34e2e2; +} + +.terminal .xterm-color-15 { + color: #eeeeec; +} + +.terminal .xterm-bg-color-15 { + background-color: #eeeeec; +} + +.terminal .xterm-color-16 { + color: #000000; +} + +.terminal .xterm-bg-color-16 { + background-color: #000000; +} + +.terminal .xterm-color-17 { + color: #00005f; +} + +.terminal .xterm-bg-color-17 { + background-color: #00005f; +} + +.terminal .xterm-color-18 { + color: #000087; +} + +.terminal .xterm-bg-color-18 { + background-color: #000087; +} + +.terminal .xterm-color-19 { + color: #0000af; +} + +.terminal .xterm-bg-color-19 { + background-color: #0000af; +} + +.terminal .xterm-color-20 { + color: #0000d7; +} + +.terminal .xterm-bg-color-20 { + background-color: #0000d7; +} + +.terminal .xterm-color-21 { + color: #0000ff; +} + +.terminal .xterm-bg-color-21 { + background-color: #0000ff; +} + +.terminal .xterm-color-22 { + color: #005f00; +} + +.terminal .xterm-bg-color-22 { + background-color: #005f00; +} + +.terminal .xterm-color-23 { + color: #005f5f; +} + +.terminal .xterm-bg-color-23 { + background-color: #005f5f; +} + +.terminal .xterm-color-24 { + color: #005f87; +} + +.terminal .xterm-bg-color-24 { + background-color: #005f87; +} + +.terminal .xterm-color-25 { + color: #005faf; +} + +.terminal .xterm-bg-color-25 { + background-color: #005faf; +} + +.terminal .xterm-color-26 { + color: #005fd7; +} + +.terminal .xterm-bg-color-26 { + background-color: #005fd7; +} + +.terminal .xterm-color-27 { + color: #005fff; +} + +.terminal .xterm-bg-color-27 { + background-color: #005fff; +} + +.terminal .xterm-color-28 { + color: #008700; +} + +.terminal .xterm-bg-color-28 { + background-color: #008700; +} + +.terminal .xterm-color-29 { + color: #00875f; +} + +.terminal .xterm-bg-color-29 { + background-color: #00875f; +} + +.terminal .xterm-color-30 { + color: #008787; +} + +.terminal .xterm-bg-color-30 { + background-color: #008787; +} + +.terminal .xterm-color-31 { + color: #0087af; +} + +.terminal .xterm-bg-color-31 { + background-color: #0087af; +} + +.terminal .xterm-color-32 { + color: #0087d7; +} + +.terminal .xterm-bg-color-32 { + background-color: #0087d7; +} + +.terminal .xterm-color-33 { + color: #0087ff; +} + +.terminal .xterm-bg-color-33 { + background-color: #0087ff; +} + +.terminal .xterm-color-34 { + color: #00af00; +} + +.terminal .xterm-bg-color-34 { + background-color: #00af00; +} + +.terminal .xterm-color-35 { + color: #00af5f; +} + +.terminal .xterm-bg-color-35 { + background-color: #00af5f; +} + +.terminal .xterm-color-36 { + color: #00af87; +} + +.terminal .xterm-bg-color-36 { + background-color: #00af87; +} + +.terminal .xterm-color-37 { + color: #00afaf; +} + +.terminal .xterm-bg-color-37 { + background-color: #00afaf; +} + +.terminal .xterm-color-38 { + color: #00afd7; +} + +.terminal .xterm-bg-color-38 { + background-color: #00afd7; +} + +.terminal .xterm-color-39 { + color: #00afff; +} + +.terminal .xterm-bg-color-39 { + background-color: #00afff; +} + +.terminal .xterm-color-40 { + color: #00d700; +} + +.terminal .xterm-bg-color-40 { + background-color: #00d700; +} + +.terminal .xterm-color-41 { + color: #00d75f; +} + +.terminal .xterm-bg-color-41 { + background-color: #00d75f; +} + +.terminal .xterm-color-42 { + color: #00d787; +} + +.terminal .xterm-bg-color-42 { + background-color: #00d787; +} + +.terminal .xterm-color-43 { + color: #00d7af; +} + +.terminal .xterm-bg-color-43 { + background-color: #00d7af; +} + +.terminal .xterm-color-44 { + color: #00d7d7; +} + +.terminal .xterm-bg-color-44 { + background-color: #00d7d7; +} + +.terminal .xterm-color-45 { + color: #00d7ff; +} + +.terminal .xterm-bg-color-45 { + background-color: #00d7ff; +} + +.terminal .xterm-color-46 { + color: #00ff00; +} + +.terminal .xterm-bg-color-46 { + background-color: #00ff00; +} + +.terminal .xterm-color-47 { + color: #00ff5f; +} + +.terminal .xterm-bg-color-47 { + background-color: #00ff5f; +} + +.terminal .xterm-color-48 { + color: #00ff87; +} + +.terminal .xterm-bg-color-48 { + background-color: #00ff87; +} + +.terminal .xterm-color-49 { + color: #00ffaf; +} + +.terminal .xterm-bg-color-49 { + background-color: #00ffaf; +} + +.terminal .xterm-color-50 { + color: #00ffd7; +} + +.terminal .xterm-bg-color-50 { + background-color: #00ffd7; +} + +.terminal .xterm-color-51 { + color: #00ffff; +} + +.terminal .xterm-bg-color-51 { + background-color: #00ffff; +} + +.terminal .xterm-color-52 { + color: #5f0000; +} + +.terminal .xterm-bg-color-52 { + background-color: #5f0000; +} + +.terminal .xterm-color-53 { + color: #5f005f; +} + +.terminal .xterm-bg-color-53 { + background-color: #5f005f; +} + +.terminal .xterm-color-54 { + color: #5f0087; +} + +.terminal .xterm-bg-color-54 { + background-color: #5f0087; +} + +.terminal .xterm-color-55 { + color: #5f00af; +} + +.terminal .xterm-bg-color-55 { + background-color: #5f00af; +} + +.terminal .xterm-color-56 { + color: #5f00d7; +} + +.terminal .xterm-bg-color-56 { + background-color: #5f00d7; +} + +.terminal .xterm-color-57 { + color: #5f00ff; +} + +.terminal .xterm-bg-color-57 { + background-color: #5f00ff; +} + +.terminal .xterm-color-58 { + color: #5f5f00; +} + +.terminal .xterm-bg-color-58 { + background-color: #5f5f00; +} + +.terminal .xterm-color-59 { + color: #5f5f5f; +} + +.terminal .xterm-bg-color-59 { + background-color: #5f5f5f; +} + +.terminal .xterm-color-60 { + color: #5f5f87; +} + +.terminal .xterm-bg-color-60 { + background-color: #5f5f87; +} + +.terminal .xterm-color-61 { + color: #5f5faf; +} + +.terminal .xterm-bg-color-61 { + background-color: #5f5faf; +} + +.terminal .xterm-color-62 { + color: #5f5fd7; +} + +.terminal .xterm-bg-color-62 { + background-color: #5f5fd7; +} + +.terminal .xterm-color-63 { + color: #5f5fff; +} + +.terminal .xterm-bg-color-63 { + background-color: #5f5fff; +} + +.terminal .xterm-color-64 { + color: #5f8700; +} + +.terminal .xterm-bg-color-64 { + background-color: #5f8700; +} + +.terminal .xterm-color-65 { + color: #5f875f; +} + +.terminal .xterm-bg-color-65 { + background-color: #5f875f; +} + +.terminal .xterm-color-66 { + color: #5f8787; +} + +.terminal .xterm-bg-color-66 { + background-color: #5f8787; +} + +.terminal .xterm-color-67 { + color: #5f87af; +} + +.terminal .xterm-bg-color-67 { + background-color: #5f87af; +} + +.terminal .xterm-color-68 { + color: #5f87d7; +} + +.terminal .xterm-bg-color-68 { + background-color: #5f87d7; +} + +.terminal .xterm-color-69 { + color: #5f87ff; +} + +.terminal .xterm-bg-color-69 { + background-color: #5f87ff; +} + +.terminal .xterm-color-70 { + color: #5faf00; +} + +.terminal .xterm-bg-color-70 { + background-color: #5faf00; +} + +.terminal .xterm-color-71 { + color: #5faf5f; +} + +.terminal .xterm-bg-color-71 { + background-color: #5faf5f; +} + +.terminal .xterm-color-72 { + color: #5faf87; +} + +.terminal .xterm-bg-color-72 { + background-color: #5faf87; +} + +.terminal .xterm-color-73 { + color: #5fafaf; +} + +.terminal .xterm-bg-color-73 { + background-color: #5fafaf; +} + +.terminal .xterm-color-74 { + color: #5fafd7; +} + +.terminal .xterm-bg-color-74 { + background-color: #5fafd7; +} + +.terminal .xterm-color-75 { + color: #5fafff; +} + +.terminal .xterm-bg-color-75 { + background-color: #5fafff; +} + +.terminal .xterm-color-76 { + color: #5fd700; +} + +.terminal .xterm-bg-color-76 { + background-color: #5fd700; +} + +.terminal .xterm-color-77 { + color: #5fd75f; +} + +.terminal .xterm-bg-color-77 { + background-color: #5fd75f; +} + +.terminal .xterm-color-78 { + color: #5fd787; +} + +.terminal .xterm-bg-color-78 { + background-color: #5fd787; +} + +.terminal .xterm-color-79 { + color: #5fd7af; +} + +.terminal .xterm-bg-color-79 { + background-color: #5fd7af; +} + +.terminal .xterm-color-80 { + color: #5fd7d7; +} + +.terminal .xterm-bg-color-80 { + background-color: #5fd7d7; +} + +.terminal .xterm-color-81 { + color: #5fd7ff; +} + +.terminal .xterm-bg-color-81 { + background-color: #5fd7ff; +} + +.terminal .xterm-color-82 { + color: #5fff00; +} + +.terminal .xterm-bg-color-82 { + background-color: #5fff00; +} + +.terminal .xterm-color-83 { + color: #5fff5f; +} + +.terminal .xterm-bg-color-83 { + background-color: #5fff5f; +} + +.terminal .xterm-color-84 { + color: #5fff87; +} + +.terminal .xterm-bg-color-84 { + background-color: #5fff87; +} + +.terminal .xterm-color-85 { + color: #5fffaf; +} + +.terminal .xterm-bg-color-85 { + background-color: #5fffaf; +} + +.terminal .xterm-color-86 { + color: #5fffd7; +} + +.terminal .xterm-bg-color-86 { + background-color: #5fffd7; +} + +.terminal .xterm-color-87 { + color: #5fffff; +} + +.terminal .xterm-bg-color-87 { + background-color: #5fffff; +} + +.terminal .xterm-color-88 { + color: #870000; +} + +.terminal .xterm-bg-color-88 { + background-color: #870000; +} + +.terminal .xterm-color-89 { + color: #87005f; +} + +.terminal .xterm-bg-color-89 { + background-color: #87005f; +} + +.terminal .xterm-color-90 { + color: #870087; +} + +.terminal .xterm-bg-color-90 { + background-color: #870087; +} + +.terminal .xterm-color-91 { + color: #8700af; +} + +.terminal .xterm-bg-color-91 { + background-color: #8700af; +} + +.terminal .xterm-color-92 { + color: #8700d7; +} + +.terminal .xterm-bg-color-92 { + background-color: #8700d7; +} + +.terminal .xterm-color-93 { + color: #8700ff; +} + +.terminal .xterm-bg-color-93 { + background-color: #8700ff; +} + +.terminal .xterm-color-94 { + color: #875f00; +} + +.terminal .xterm-bg-color-94 { + background-color: #875f00; +} + +.terminal .xterm-color-95 { + color: #875f5f; +} + +.terminal .xterm-bg-color-95 { + background-color: #875f5f; +} + +.terminal .xterm-color-96 { + color: #875f87; +} + +.terminal .xterm-bg-color-96 { + background-color: #875f87; +} + +.terminal .xterm-color-97 { + color: #875faf; +} + +.terminal .xterm-bg-color-97 { + background-color: #875faf; +} + +.terminal .xterm-color-98 { + color: #875fd7; +} + +.terminal .xterm-bg-color-98 { + background-color: #875fd7; +} + +.terminal .xterm-color-99 { + color: #875fff; +} + +.terminal .xterm-bg-color-99 { + background-color: #875fff; +} + +.terminal .xterm-color-100 { + color: #878700; +} + +.terminal .xterm-bg-color-100 { + background-color: #878700; +} + +.terminal .xterm-color-101 { + color: #87875f; +} + +.terminal .xterm-bg-color-101 { + background-color: #87875f; +} + +.terminal .xterm-color-102 { + color: #878787; +} + +.terminal .xterm-bg-color-102 { + background-color: #878787; +} + +.terminal .xterm-color-103 { + color: #8787af; +} + +.terminal .xterm-bg-color-103 { + background-color: #8787af; +} + +.terminal .xterm-color-104 { + color: #8787d7; +} + +.terminal .xterm-bg-color-104 { + background-color: #8787d7; +} + +.terminal .xterm-color-105 { + color: #8787ff; +} + +.terminal .xterm-bg-color-105 { + background-color: #8787ff; +} + +.terminal .xterm-color-106 { + color: #87af00; +} + +.terminal .xterm-bg-color-106 { + background-color: #87af00; +} + +.terminal .xterm-color-107 { + color: #87af5f; +} + +.terminal .xterm-bg-color-107 { + background-color: #87af5f; +} + +.terminal .xterm-color-108 { + color: #87af87; +} + +.terminal .xterm-bg-color-108 { + background-color: #87af87; +} + +.terminal .xterm-color-109 { + color: #87afaf; +} + +.terminal .xterm-bg-color-109 { + background-color: #87afaf; +} + +.terminal .xterm-color-110 { + color: #87afd7; +} + +.terminal .xterm-bg-color-110 { + background-color: #87afd7; +} + +.terminal .xterm-color-111 { + color: #87afff; +} + +.terminal .xterm-bg-color-111 { + background-color: #87afff; +} + +.terminal .xterm-color-112 { + color: #87d700; +} + +.terminal .xterm-bg-color-112 { + background-color: #87d700; +} + +.terminal .xterm-color-113 { + color: #87d75f; +} + +.terminal .xterm-bg-color-113 { + background-color: #87d75f; +} + +.terminal .xterm-color-114 { + color: #87d787; +} + +.terminal .xterm-bg-color-114 { + background-color: #87d787; +} + +.terminal .xterm-color-115 { + color: #87d7af; +} + +.terminal .xterm-bg-color-115 { + background-color: #87d7af; +} + +.terminal .xterm-color-116 { + color: #87d7d7; +} + +.terminal .xterm-bg-color-116 { + background-color: #87d7d7; +} + +.terminal .xterm-color-117 { + color: #87d7ff; +} + +.terminal .xterm-bg-color-117 { + background-color: #87d7ff; +} + +.terminal .xterm-color-118 { + color: #87ff00; +} + +.terminal .xterm-bg-color-118 { + background-color: #87ff00; +} + +.terminal .xterm-color-119 { + color: #87ff5f; +} + +.terminal .xterm-bg-color-119 { + background-color: #87ff5f; +} + +.terminal .xterm-color-120 { + color: #87ff87; +} + +.terminal .xterm-bg-color-120 { + background-color: #87ff87; +} + +.terminal .xterm-color-121 { + color: #87ffaf; +} + +.terminal .xterm-bg-color-121 { + background-color: #87ffaf; +} + +.terminal .xterm-color-122 { + color: #87ffd7; +} + +.terminal .xterm-bg-color-122 { + background-color: #87ffd7; +} + +.terminal .xterm-color-123 { + color: #87ffff; +} + +.terminal .xterm-bg-color-123 { + background-color: #87ffff; +} + +.terminal .xterm-color-124 { + color: #af0000; +} + +.terminal .xterm-bg-color-124 { + background-color: #af0000; +} + +.terminal .xterm-color-125 { + color: #af005f; +} + +.terminal .xterm-bg-color-125 { + background-color: #af005f; +} + +.terminal .xterm-color-126 { + color: #af0087; +} + +.terminal .xterm-bg-color-126 { + background-color: #af0087; +} + +.terminal .xterm-color-127 { + color: #af00af; +} + +.terminal .xterm-bg-color-127 { + background-color: #af00af; +} + +.terminal .xterm-color-128 { + color: #af00d7; +} + +.terminal .xterm-bg-color-128 { + background-color: #af00d7; +} + +.terminal .xterm-color-129 { + color: #af00ff; +} + +.terminal .xterm-bg-color-129 { + background-color: #af00ff; +} + +.terminal .xterm-color-130 { + color: #af5f00; +} + +.terminal .xterm-bg-color-130 { + background-color: #af5f00; +} + +.terminal .xterm-color-131 { + color: #af5f5f; +} + +.terminal .xterm-bg-color-131 { + background-color: #af5f5f; +} + +.terminal .xterm-color-132 { + color: #af5f87; +} + +.terminal .xterm-bg-color-132 { + background-color: #af5f87; +} + +.terminal .xterm-color-133 { + color: #af5faf; +} + +.terminal .xterm-bg-color-133 { + background-color: #af5faf; +} + +.terminal .xterm-color-134 { + color: #af5fd7; +} + +.terminal .xterm-bg-color-134 { + background-color: #af5fd7; +} + +.terminal .xterm-color-135 { + color: #af5fff; +} + +.terminal .xterm-bg-color-135 { + background-color: #af5fff; +} + +.terminal .xterm-color-136 { + color: #af8700; +} + +.terminal .xterm-bg-color-136 { + background-color: #af8700; +} + +.terminal .xterm-color-137 { + color: #af875f; +} + +.terminal .xterm-bg-color-137 { + background-color: #af875f; +} + +.terminal .xterm-color-138 { + color: #af8787; +} + +.terminal .xterm-bg-color-138 { + background-color: #af8787; +} + +.terminal .xterm-color-139 { + color: #af87af; +} + +.terminal .xterm-bg-color-139 { + background-color: #af87af; +} + +.terminal .xterm-color-140 { + color: #af87d7; +} + +.terminal .xterm-bg-color-140 { + background-color: #af87d7; +} + +.terminal .xterm-color-141 { + color: #af87ff; +} + +.terminal .xterm-bg-color-141 { + background-color: #af87ff; +} + +.terminal .xterm-color-142 { + color: #afaf00; +} + +.terminal .xterm-bg-color-142 { + background-color: #afaf00; +} + +.terminal .xterm-color-143 { + color: #afaf5f; +} + +.terminal .xterm-bg-color-143 { + background-color: #afaf5f; +} + +.terminal .xterm-color-144 { + color: #afaf87; +} + +.terminal .xterm-bg-color-144 { + background-color: #afaf87; +} + +.terminal .xterm-color-145 { + color: #afafaf; +} + +.terminal .xterm-bg-color-145 { + background-color: #afafaf; +} + +.terminal .xterm-color-146 { + color: #afafd7; +} + +.terminal .xterm-bg-color-146 { + background-color: #afafd7; +} + +.terminal .xterm-color-147 { + color: #afafff; +} + +.terminal .xterm-bg-color-147 { + background-color: #afafff; +} + +.terminal .xterm-color-148 { + color: #afd700; +} + +.terminal .xterm-bg-color-148 { + background-color: #afd700; +} + +.terminal .xterm-color-149 { + color: #afd75f; +} + +.terminal .xterm-bg-color-149 { + background-color: #afd75f; +} + +.terminal .xterm-color-150 { + color: #afd787; +} + +.terminal .xterm-bg-color-150 { + background-color: #afd787; +} + +.terminal .xterm-color-151 { + color: #afd7af; +} + +.terminal .xterm-bg-color-151 { + background-color: #afd7af; +} + +.terminal .xterm-color-152 { + color: #afd7d7; +} + +.terminal .xterm-bg-color-152 { + background-color: #afd7d7; +} + +.terminal .xterm-color-153 { + color: #afd7ff; +} + +.terminal .xterm-bg-color-153 { + background-color: #afd7ff; +} + +.terminal .xterm-color-154 { + color: #afff00; +} + +.terminal .xterm-bg-color-154 { + background-color: #afff00; +} + +.terminal .xterm-color-155 { + color: #afff5f; +} + +.terminal .xterm-bg-color-155 { + background-color: #afff5f; +} + +.terminal .xterm-color-156 { + color: #afff87; +} + +.terminal .xterm-bg-color-156 { + background-color: #afff87; +} + +.terminal .xterm-color-157 { + color: #afffaf; +} + +.terminal .xterm-bg-color-157 { + background-color: #afffaf; +} + +.terminal .xterm-color-158 { + color: #afffd7; +} + +.terminal .xterm-bg-color-158 { + background-color: #afffd7; +} + +.terminal .xterm-color-159 { + color: #afffff; +} + +.terminal .xterm-bg-color-159 { + background-color: #afffff; +} + +.terminal .xterm-color-160 { + color: #d70000; +} + +.terminal .xterm-bg-color-160 { + background-color: #d70000; +} + +.terminal .xterm-color-161 { + color: #d7005f; +} + +.terminal .xterm-bg-color-161 { + background-color: #d7005f; +} + +.terminal .xterm-color-162 { + color: #d70087; +} + +.terminal .xterm-bg-color-162 { + background-color: #d70087; +} + +.terminal .xterm-color-163 { + color: #d700af; +} + +.terminal .xterm-bg-color-163 { + background-color: #d700af; +} + +.terminal .xterm-color-164 { + color: #d700d7; +} + +.terminal .xterm-bg-color-164 { + background-color: #d700d7; +} + +.terminal .xterm-color-165 { + color: #d700ff; +} + +.terminal .xterm-bg-color-165 { + background-color: #d700ff; +} + +.terminal .xterm-color-166 { + color: #d75f00; +} + +.terminal .xterm-bg-color-166 { + background-color: #d75f00; +} + +.terminal .xterm-color-167 { + color: #d75f5f; +} + +.terminal .xterm-bg-color-167 { + background-color: #d75f5f; +} + +.terminal .xterm-color-168 { + color: #d75f87; +} + +.terminal .xterm-bg-color-168 { + background-color: #d75f87; +} + +.terminal .xterm-color-169 { + color: #d75faf; +} + +.terminal .xterm-bg-color-169 { + background-color: #d75faf; +} + +.terminal .xterm-color-170 { + color: #d75fd7; +} + +.terminal .xterm-bg-color-170 { + background-color: #d75fd7; +} + +.terminal .xterm-color-171 { + color: #d75fff; +} + +.terminal .xterm-bg-color-171 { + background-color: #d75fff; +} + +.terminal .xterm-color-172 { + color: #d78700; +} + +.terminal .xterm-bg-color-172 { + background-color: #d78700; +} + +.terminal .xterm-color-173 { + color: #d7875f; +} + +.terminal .xterm-bg-color-173 { + background-color: #d7875f; +} + +.terminal .xterm-color-174 { + color: #d78787; +} + +.terminal .xterm-bg-color-174 { + background-color: #d78787; +} + +.terminal .xterm-color-175 { + color: #d787af; +} + +.terminal .xterm-bg-color-175 { + background-color: #d787af; +} + +.terminal .xterm-color-176 { + color: #d787d7; +} + +.terminal .xterm-bg-color-176 { + background-color: #d787d7; +} + +.terminal .xterm-color-177 { + color: #d787ff; +} + +.terminal .xterm-bg-color-177 { + background-color: #d787ff; +} + +.terminal .xterm-color-178 { + color: #d7af00; +} + +.terminal .xterm-bg-color-178 { + background-color: #d7af00; +} + +.terminal .xterm-color-179 { + color: #d7af5f; +} + +.terminal .xterm-bg-color-179 { + background-color: #d7af5f; +} + +.terminal .xterm-color-180 { + color: #d7af87; +} + +.terminal .xterm-bg-color-180 { + background-color: #d7af87; +} + +.terminal .xterm-color-181 { + color: #d7afaf; +} + +.terminal .xterm-bg-color-181 { + background-color: #d7afaf; +} + +.terminal .xterm-color-182 { + color: #d7afd7; +} + +.terminal .xterm-bg-color-182 { + background-color: #d7afd7; +} + +.terminal .xterm-color-183 { + color: #d7afff; +} + +.terminal .xterm-bg-color-183 { + background-color: #d7afff; +} + +.terminal .xterm-color-184 { + color: #d7d700; +} + +.terminal .xterm-bg-color-184 { + background-color: #d7d700; +} + +.terminal .xterm-color-185 { + color: #d7d75f; +} + +.terminal .xterm-bg-color-185 { + background-color: #d7d75f; +} + +.terminal .xterm-color-186 { + color: #d7d787; +} + +.terminal .xterm-bg-color-186 { + background-color: #d7d787; +} + +.terminal .xterm-color-187 { + color: #d7d7af; +} + +.terminal .xterm-bg-color-187 { + background-color: #d7d7af; +} + +.terminal .xterm-color-188 { + color: #d7d7d7; +} + +.terminal .xterm-bg-color-188 { + background-color: #d7d7d7; +} + +.terminal .xterm-color-189 { + color: #d7d7ff; +} + +.terminal .xterm-bg-color-189 { + background-color: #d7d7ff; +} + +.terminal .xterm-color-190 { + color: #d7ff00; +} + +.terminal .xterm-bg-color-190 { + background-color: #d7ff00; +} + +.terminal .xterm-color-191 { + color: #d7ff5f; +} + +.terminal .xterm-bg-color-191 { + background-color: #d7ff5f; +} + +.terminal .xterm-color-192 { + color: #d7ff87; +} + +.terminal .xterm-bg-color-192 { + background-color: #d7ff87; +} + +.terminal .xterm-color-193 { + color: #d7ffaf; +} + +.terminal .xterm-bg-color-193 { + background-color: #d7ffaf; +} + +.terminal .xterm-color-194 { + color: #d7ffd7; +} + +.terminal .xterm-bg-color-194 { + background-color: #d7ffd7; +} + +.terminal .xterm-color-195 { + color: #d7ffff; +} + +.terminal .xterm-bg-color-195 { + background-color: #d7ffff; +} + +.terminal .xterm-color-196 { + color: #ff0000; +} + +.terminal .xterm-bg-color-196 { + background-color: #ff0000; +} + +.terminal .xterm-color-197 { + color: #ff005f; +} + +.terminal .xterm-bg-color-197 { + background-color: #ff005f; +} + +.terminal .xterm-color-198 { + color: #ff0087; +} + +.terminal .xterm-bg-color-198 { + background-color: #ff0087; +} + +.terminal .xterm-color-199 { + color: #ff00af; +} + +.terminal .xterm-bg-color-199 { + background-color: #ff00af; +} + +.terminal .xterm-color-200 { + color: #ff00d7; +} + +.terminal .xterm-bg-color-200 { + background-color: #ff00d7; +} + +.terminal .xterm-color-201 { + color: #ff00ff; +} + +.terminal .xterm-bg-color-201 { + background-color: #ff00ff; +} + +.terminal .xterm-color-202 { + color: #ff5f00; +} + +.terminal .xterm-bg-color-202 { + background-color: #ff5f00; +} + +.terminal .xterm-color-203 { + color: #ff5f5f; +} + +.terminal .xterm-bg-color-203 { + background-color: #ff5f5f; +} + +.terminal .xterm-color-204 { + color: #ff5f87; +} + +.terminal .xterm-bg-color-204 { + background-color: #ff5f87; +} + +.terminal .xterm-color-205 { + color: #ff5faf; +} + +.terminal .xterm-bg-color-205 { + background-color: #ff5faf; +} + +.terminal .xterm-color-206 { + color: #ff5fd7; +} + +.terminal .xterm-bg-color-206 { + background-color: #ff5fd7; +} + +.terminal .xterm-color-207 { + color: #ff5fff; +} + +.terminal .xterm-bg-color-207 { + background-color: #ff5fff; +} + +.terminal .xterm-color-208 { + color: #ff8700; +} + +.terminal .xterm-bg-color-208 { + background-color: #ff8700; +} + +.terminal .xterm-color-209 { + color: #ff875f; +} + +.terminal .xterm-bg-color-209 { + background-color: #ff875f; +} + +.terminal .xterm-color-210 { + color: #ff8787; +} + +.terminal .xterm-bg-color-210 { + background-color: #ff8787; +} + +.terminal .xterm-color-211 { + color: #ff87af; +} + +.terminal .xterm-bg-color-211 { + background-color: #ff87af; +} + +.terminal .xterm-color-212 { + color: #ff87d7; +} + +.terminal .xterm-bg-color-212 { + background-color: #ff87d7; +} + +.terminal .xterm-color-213 { + color: #ff87ff; +} + +.terminal .xterm-bg-color-213 { + background-color: #ff87ff; +} + +.terminal .xterm-color-214 { + color: #ffaf00; +} + +.terminal .xterm-bg-color-214 { + background-color: #ffaf00; +} + +.terminal .xterm-color-215 { + color: #ffaf5f; +} + +.terminal .xterm-bg-color-215 { + background-color: #ffaf5f; +} + +.terminal .xterm-color-216 { + color: #ffaf87; +} + +.terminal .xterm-bg-color-216 { + background-color: #ffaf87; +} + +.terminal .xterm-color-217 { + color: #ffafaf; +} + +.terminal .xterm-bg-color-217 { + background-color: #ffafaf; +} + +.terminal .xterm-color-218 { + color: #ffafd7; +} + +.terminal .xterm-bg-color-218 { + background-color: #ffafd7; +} + +.terminal .xterm-color-219 { + color: #ffafff; +} + +.terminal .xterm-bg-color-219 { + background-color: #ffafff; +} + +.terminal .xterm-color-220 { + color: #ffd700; +} + +.terminal .xterm-bg-color-220 { + background-color: #ffd700; +} + +.terminal .xterm-color-221 { + color: #ffd75f; +} + +.terminal .xterm-bg-color-221 { + background-color: #ffd75f; +} + +.terminal .xterm-color-222 { + color: #ffd787; +} + +.terminal .xterm-bg-color-222 { + background-color: #ffd787; +} + +.terminal .xterm-color-223 { + color: #ffd7af; +} + +.terminal .xterm-bg-color-223 { + background-color: #ffd7af; +} + +.terminal .xterm-color-224 { + color: #ffd7d7; +} + +.terminal .xterm-bg-color-224 { + background-color: #ffd7d7; +} + +.terminal .xterm-color-225 { + color: #ffd7ff; +} + +.terminal .xterm-bg-color-225 { + background-color: #ffd7ff; +} + +.terminal .xterm-color-226 { + color: #ffff00; +} + +.terminal .xterm-bg-color-226 { + background-color: #ffff00; +} + +.terminal .xterm-color-227 { + color: #ffff5f; +} + +.terminal .xterm-bg-color-227 { + background-color: #ffff5f; +} + +.terminal .xterm-color-228 { + color: #ffff87; +} + +.terminal .xterm-bg-color-228 { + background-color: #ffff87; +} + +.terminal .xterm-color-229 { + color: #ffffaf; +} + +.terminal .xterm-bg-color-229 { + background-color: #ffffaf; +} + +.terminal .xterm-color-230 { + color: #ffffd7; +} + +.terminal .xterm-bg-color-230 { + background-color: #ffffd7; +} + +.terminal .xterm-color-231 { + color: #ffffff; +} + +.terminal .xterm-bg-color-231 { + background-color: #ffffff; +} + +.terminal .xterm-color-232 { + color: #080808; +} + +.terminal .xterm-bg-color-232 { + background-color: #080808; +} + +.terminal .xterm-color-233 { + color: #121212; +} + +.terminal .xterm-bg-color-233 { + background-color: #121212; +} + +.terminal .xterm-color-234 { + color: #1c1c1c; +} + +.terminal .xterm-bg-color-234 { + background-color: #1c1c1c; +} + +.terminal .xterm-color-235 { + color: #262626; +} + +.terminal .xterm-bg-color-235 { + background-color: #262626; +} + +.terminal .xterm-color-236 { + color: #303030; +} + +.terminal .xterm-bg-color-236 { + background-color: #303030; +} + +.terminal .xterm-color-237 { + color: #3a3a3a; +} + +.terminal .xterm-bg-color-237 { + background-color: #3a3a3a; +} + +.terminal .xterm-color-238 { + color: #444444; +} + +.terminal .xterm-bg-color-238 { + background-color: #444444; +} + +.terminal .xterm-color-239 { + color: #4e4e4e; +} + +.terminal .xterm-bg-color-239 { + background-color: #4e4e4e; +} + +.terminal .xterm-color-240 { + color: #585858; +} + +.terminal .xterm-bg-color-240 { + background-color: #585858; +} + +.terminal .xterm-color-241 { + color: #626262; +} + +.terminal .xterm-bg-color-241 { + background-color: #626262; +} + +.terminal .xterm-color-242 { + color: #6c6c6c; +} + +.terminal .xterm-bg-color-242 { + background-color: #6c6c6c; +} + +.terminal .xterm-color-243 { + color: #767676; +} + +.terminal .xterm-bg-color-243 { + background-color: #767676; +} + +.terminal .xterm-color-244 { + color: #808080; +} + +.terminal .xterm-bg-color-244 { + background-color: #808080; +} + +.terminal .xterm-color-245 { + color: #8a8a8a; +} + +.terminal .xterm-bg-color-245 { + background-color: #8a8a8a; +} + +.terminal .xterm-color-246 { + color: #949494; +} + +.terminal .xterm-bg-color-246 { + background-color: #949494; +} + +.terminal .xterm-color-247 { + color: #9e9e9e; +} + +.terminal .xterm-bg-color-247 { + background-color: #9e9e9e; +} + +.terminal .xterm-color-248 { + color: #a8a8a8; +} + +.terminal .xterm-bg-color-248 { + background-color: #a8a8a8; +} + +.terminal .xterm-color-249 { + color: #b2b2b2; +} + +.terminal .xterm-bg-color-249 { + background-color: #b2b2b2; +} + +.terminal .xterm-color-250 { + color: #bcbcbc; +} + +.terminal .xterm-bg-color-250 { + background-color: #bcbcbc; +} + +.terminal .xterm-color-251 { + color: #c6c6c6; +} + +.terminal .xterm-bg-color-251 { + background-color: #c6c6c6; +} + +.terminal .xterm-color-252 { + color: #d0d0d0; +} + +.terminal .xterm-bg-color-252 { + background-color: #d0d0d0; +} + +.terminal .xterm-color-253 { + color: #dadada; +} + +.terminal .xterm-bg-color-253 { + background-color: #dadada; +} + +.terminal .xterm-color-254 { + color: #e4e4e4; +} + +.terminal .xterm-bg-color-254 { + background-color: #e4e4e4; +} + +.terminal .xterm-color-255 { + color: #eeeeee; +} + +.terminal .xterm-bg-color-255 { + background-color: #eeeeee; +} \ No newline at end of file diff --git a/src/main/webapp/static/image/backgrounds/1.jpg b/src/main/webapp/static/image/backgrounds/1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e43067cce342404d1dcbd8bcc70c1f363ba84824 GIT binary patch literal 72128 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

878IF$GHzkes%IwnohgPQZ>AqdVBK8KO|j}- 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

#gou(BF&&LQ+HkmoL!mRde0 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 zm_bPb8IYEW{(i-71!<78qfgI>s43Iz^SLzAe8AIIb&MO!W;ZiQ(1zfptWxRn4H>@>bLy&|WA(-$V zae#JwkF=zgRx*i+8^7cw6Wadz`uerSb?kC2fr>V z37icei?hQ`&W<#o&DqS>hd95af6yO;xXDL&2W@&$_2aSyu`P~3 zLJY=-@IzeobCZu;(^D+~RLPhrFNZNR*Rovw?$-Y_tm#XD0RZk{{R30{Qv*}-2eap-~a#stN;K24gdfE z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z000000000000000000002mk;8rvLx|{{R30>Hq)$I{*LxDF6TfHvj+t8~^|S6aWAK z1poj5000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000Z~y=R{Qv*}>;M1&bpQYWssI20-v9sr;{X5v%>V!Zg8%>k z6951J000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000082|tPrT_o{?f?J)`~Uy|{{R30{{R30{{R30{{R30`~Uy|X8-^I z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z0000000000000003;+NCnE(I){{R30{Qv*}=>Px#i_@%wEzGB>Hq)$_W%F@`~Uy|_W%F@>;M1& z&j0`bjsO4vE&u=k00000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z5C8xGu>b%7{r~^~ZvX%QXaE2J>;M1&{{R30{{R30{{R30{{R30{{R30{{R30{{R30 z{{R30$^ZZWBLDyZ00000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 zDgXcgi2wiqa{vGU+yDRo{{R30{r~^~;s5{ux&QzGwg3PC&;S4c>i_@%_y7O^{r~^~ z>i_@%KmY&$0000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000dH?_b{{R30{{R30wEzGBG5`Po4FCWD3;+NC6#xJLGynhqTL1t6YybcNRR910 z3;+NC000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z0000000000000000000000000000000000000000000000000000000000003IG5A zt^fc4{{R30-T(jqE&u=k00000000001poj53IG5A2><{91poj5000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000006951J$p8QV z{{R30qW}N^2LJ#7761SMWdHyGp#T5?u>b%7vH$=8ssI20l>h($cK`qYO#lD@AOHXW z1poj5000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z000000000000000000000000000000000000000000000000005C8xGz5oCK{{R30 zivR!sM*si-zW@LL`2YX_{{R30{{R30{{R30{{R30{{R30{r~^~^Z)<=*#H0liU0rr zBLDyZ000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z0000000000000000000000000000000000000000000000RR91YXATM=Kufz*#H0l z>i_@%{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30z5oCK z9{>OV000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z0000000000000000000000000000000000000000000001^@s6KL7v#;{X5v{{R30 z{{R30`Tzg`-v9sryZ`_Ivj6}9&j0`b=Kufz@&Et;`Tzg`{r~^~{{R30@c;k-PXGV_ z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z000000000000000000000000000000000000000000000IRF3v?EnA({{R30?f?J) ze*gdgBLDyZ4gdfE3;+NC6#xJLDF6TfNdN!Px#)c^nht^fc4e*gdgLI3~&5dZ)H0RR91000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000RR91WdHyG?*IS*`2YX_{{R30{{R30{{R30{{R30 z{{R30{{R30{{R30{{R30{{R30{r~^~@Bjb+zW@LLW&i*H7ytkO0RR910000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z000000000000000000007XSbNxBvhE{{R30{{R30_5c6?-v9sr!~g&QvH$=8vj6}9 z#sB~S-2eap?*IS*`~Uy|{{R30{{R30{{R30_y7O^#Q*>RN&o-=0RR910000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z000000000000000TL1t6_y7O^{{R30#Q*>RSpWb4AOHXW5C8xG3jhEB3;+NC5&!@I z8~^|SJpcdza{vGUwg3PC>;M1&{r~^~{{R30{{R30;{X5vT>t<80ssI20000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z0000000000eEb%7v;Y7A#sB~Swg3PCn*aa+YybcNHUIzs5C8xG z0RR9100000000000ssI29smFUWB>pFumAu6#{d8TP5=M^00000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z000000000000000000000000000000000000000000000000008UO$Qa{vGU&;S4c z`2YX_{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30`~Uy|>i_@%z5oCKbpQYW zD*ylh1poj500000000000RR913jhEB6951J2LJ#70000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z0000000000000000000000000000000000000000IRF3vzW@LL`v3p{{{R30{{R30 z{{R30`~Uy|_5c6?^Z)<=`Tzg`{Qv*}{{R30{{R30{{R30{{R30{{R30`~Uy|-~a#s zl>h($IRF3v1poj500000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z000000000000000000000000000000BLDyZ$^ZZW{{R30{{R30^#A|>zyJUMg8%>k zX8-^IRR910PXGV_VE_OCZU6uPi2wiqsQ>@~)c^nh@c;k-{r~^~{{R30{{R30{{R30 z<^TWyi~s-tCjbBd0RR91000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z0000000000000000000000000ZvX%Q{r~^~{{R30-T(jqVgLXD5dZ)H0000000000 z000000000000000000000RR912><{982|tPL;wH)g8%>k$p8QV_5c6?{{R30{{R30 z{r~^~)c^nhY5)KL5C8xG000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000e*gdg@c;k-y#N3JJpcdz0RR9100000000000000000000 z000000000000000000000000000000000000RR916#xJLR{#J2u>b%7^Z)<={{R30 z{{R30_W%F@uK)l5H2?qr0RR910000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000ssI2CjbBdTL1t6MgRZ+JpcdzO#lD@KL7v#GynhqCIA2c8vpkpKVyy#N3J+yDRo=l}o!?*IS*^8f$i_@%=Kufz;Q#;t&;S4c!T1^@s6000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000HUIzst^fc4@c;k- z{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30 z`v3p{>i_@%$^ZZWm;e9(S^xk5AOHXW1poj5000000000000000000002><{9LI3~& zg#Z8mR{#J20ssI200000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z0000000000000000000000000000000000000000IRF3v)&Kwi{{R30{{R30`~Uy| z@Bjb+<^TWy*#H0l$^ZZW%m4rY-T(jq;{X5v<^TWy?*IS*`v3p{{{R30{{R30{{R30 z{{R30{{R30{{R30_W%F@*#H0llK=n!N&o-=6aWAK0ssI200000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z0000000000000000000000000000001poj5ng9R*{{R30{Qv*}$p8QVbpQYWK>z>% zBLDyZ7ytkO6aWAK6aWAK8UO$Q8~^|SBLDyZJ^%m!X8-^IqW}N^-2eap^Z)<={r~^~ z{{R30{{R30{{R30{{R30{{R30@&Et;#sB~Sf&c&jHUIzs2mk;8000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000004gdfEx&QzG{{R30!T(^b000000000000000 z00000000000000000000000000000000000000002LJ#7ApigXO#lD@c>n+arvLx| z*#H0l^#A|>{{R30{{R30{{R30{{R30{r~^~=KufzqyPW_LI3~&2mk;80000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z000000000000000000002mk;8rT_o{{{R30cmMzZ000001^@s63jhEB3;+NC3;+NC z3jhEB1^@s60ssI200000000000000000000000000000000000000002mk;89RL6T zQ~&?~od5s;-T(jq`Tzg`{{R30{{R30{{R30{{R30>;M1&l>h($DgXcg0RR9100000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000{{R36#xJLng9R*{{R30$N&HUmjD0&qW}N^u>b%7vH$=8vH$=8umAu6 zpa1{>lK=n!e*gdgWB>pFMF0Q*CIA2c5&!@I1^@s60000000000000000000000000 z1^@s6CIA2cX#fBKw*UYD?EnA({r~^~{{R30{{R30{r~^~)BpegRR9101ONa400000 z00000000000000000000000000000000000000000000000000000000000000000 z0000000000000000000000000000000000000000000000000000000000002><{9 zJOBUyivR!s%m4rY^8f$<{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30 z{{R30{{R30`v3p{@c;k-z>%82|tP2mk;80000000000 z00000000004gdfEKmY&$lK=n!-T(jq`~Uy|{{R30{{R30=>Px#NdN!<0000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000CIA2cng9R*>i_@% z{r~^~{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30{{R30 z{{R30{{R30{{R30{{R30{{R30{{R30{Qv*}@Bjb+)c^nhr2qf`Z~y=RH~;_u6951J z0{{R300000000001poj5C;$KebpQYW!vFvP?EnA(^8f$b%7o&W#;M1&#{d8Ti~s-t zPyhe`9RL6T1poj5000000RR915&!@II{*LxNB{r;5dZ)H00000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z0000000000000000000000000000002LJ#7nE(I){{R30{{R30{r~^~(EtDdegFUf zK>z>%8vp z)&KwioB#j-SpWb48UO$Q0ssI20000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000007XSbN)c^nh{{R30{{R30yZ`_ICIA2c0000000000 z00000000000000000000000000000000000000001poj55C8xG7XSbN82|tP6951J z4*&oF2><{92><{9761SMLjV8(dH?_bvH$=8;s5{u`2YX_{{R30{{R30{{R30{{R30 z{{R30_W%F@&;S4cf&c&jE&u=k1ONa400000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000761SM(*OVf{{R30{{R30hyVZp00000000000000000000 z00000000000000000000000001poj5NdN!{{R30{{R30 z{{R30{{R30{r~^~-~a#sg8%>kAOHXW00000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z0000000000000001^@s6od5s;{{R30{{R30*#H0lJ^%m!00000000000000000000 z00000000000000000000IsgCwh($(*OVf_W%F@ z{{R30{{R30{{R30{Qv*}$p8QVM*si-0ssI2000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z000000000000000K>z>%=Kufz{{R30{{R30(*OVfO8@`>1ONa4000000000000000 z000000000000000IRF3v>Hq)${{R30{{R30{Qv*}x&QzGYybcNPyhe`VgLXDoB#j- z+5i9m?*IS*b^rhX0000000000000000000000000000001ONa48vpPx#fB*mh8~^|S000000000000000 z00000000002mk;8lK=n!{r~^~{{R30`~Uy|eE<{9IRF3v zoB#j-?*IS*{{R30{{R30^8f$b%7{r~^~{{R30>;M1&kpKVyIRF3v4*&oF000000000000000 z0000000000000000000000000000000000000000000000000000000000002><{9 zOaK4?xc~qF`2YX_{{R30@c;k-T>t<800000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000ssI2JOBUyuK)l5^#A|>{{R30{{R30^#A|>wEzGBSpWb46951J00000 z0000000000AOHXWtpET3`~Uy|{{R30{r~^~>;M1&x&QzGa{vGUH~;_u6aWAK1poj5 z00000000000000000000000000000000000000000000000000000000000000000 z5&!@Iod5s;{{R30{{R30zW@LL5C8xG00000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z0000000000000004*&oFS^xk5yZ`_I^8f$<{{R30{{R30_5c6?zyJUMX#fBK9{>OV z0ssI2000006#xJLga7~l<^TWy{{R30{{R30{{R30`~Uy|>;M1&$^ZZWoB#j-a{vGU zPyhe`G5`Po9RL6T6#xJL5dZ)H3;+NC3;+NC2><{91ONa40{{R30{{R3000000RR91 zd;kCd{{R30{{R30-2eap8UO$Q0000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z000000000000000000005dZ)HPXGV_s{jB1>;M1&{{R30{{R30`Tzg`(*OVfg#Z8m zF#rGn1^@s61poj5IRF3vk^lez+5i9m`Tzg`{{R30{{R30{{R30{{R30{Qv*}^Z)<= z>i_@%;s5{u&;S4c!TOVSpWb4lmGw#y#N3J&Hw-as{jB1 z7XSbN000000000000000000002mk;8A^-pYQvd(}g#Z8mt^fc4)c^nh=>Px#@c;k- z_y7O^{Qv*}{{R30{{R30{{R30{{R30{{R30{Qv*}`2YX_@Bjb+*#H0ll>h($HUIzs z0ssI2000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000001ONa44*&oF6aWAK4gdfE0RR91 z000000000000000000000000000000000000RR913IG5A7XSbNEC2uiM*si-TmS$7 zZU6uPegFUffB*mhe*gdgfB*mhegFUfZU6uPT>t<8KmY&$8~^|S1^@s60000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000000000000000000000000000000000000000000 z00000000000000000000000000RR90|NsC0|NsC0|NsC0|NsC0|NsC0|NsC0|NsC0 z|NsB^KmY&#|NsB+AOHXV|NsBsAOHXV|NsBs2mk;7|NsB+1ONa3|NsB+1ONa3|NsB^ z1ONa3|NsB^1ONa3|NsB|AP@il|NsB|00aO3|NsB|00aO3|NsB~00aO3|NsC0fCK;k z|NsC01P}lJ|NsC000960|NsC0004jg|NsC0002M#|NsC0fB--L|NsC0zyLr0|NsC0 z!T#01v2mk;8 literal 0 KcmV+b0RR6000031 diff --git a/src/main/webapp/static/image/progress.gif b/src/main/webapp/static/image/progress.gif new file mode 100644 index 0000000000000000000000000000000000000000..f3e45e0569c02ae3fe7114691e5b5aba892e9ef4 GIT binary patch literal 2608 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 Hu0mjfagVuN literal 0 KcmV+b0RR6000031 diff --git a/src/main/webapp/static/js/base64.js b/src/main/webapp/static/js/base64.js new file mode 100644 index 0000000..c4df1c0 --- /dev/null +++ b/src/main/webapp/static/js/base64.js @@ -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; + } +} \ No newline at end of file diff --git a/src/main/webapp/static/js/formvalid.js b/src/main/webapp/static/js/formvalid.js new file mode 100644 index 0000000..4106442 --- /dev/null +++ b/src/main/webapp/static/js/formvalid.js @@ -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(" " + warnInfo + " "); + 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("correct"); + 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); diff --git a/src/main/webapp/static/js/jquerymin.js b/src/main/webapp/static/js/jquerymin.js new file mode 100644 index 0000000..8e16b0b --- /dev/null +++ b/src/main/webapp/static/js/jquerymin.js @@ -0,0 +1,5 @@ +/*! jQuery v2.1.2 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.2",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=hb(),z=hb(),A=hb(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},eb=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fb){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function gb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+rb(o[l]);w=ab.test(a)&&pb(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function hb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ib(a){return a[u]=!0,a}function jb(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function kb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function lb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function nb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function ob(a){return ib(function(b){return b=+b,ib(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pb(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=gb.support={},f=gb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=gb.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",eb,!1):e.attachEvent&&e.attachEvent("onunload",eb)),p=!f(g),c.attributes=jb(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=jb(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=jb(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(jb(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),jb(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&jb(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return lb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?lb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},gb.matches=function(a,b){return gb(a,null,null,b)},gb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return gb(b,n,null,[a]).length>0},gb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},gb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},gb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},gb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=gb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=gb.selectors={cacheLength:50,createPseudo:ib,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||gb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&gb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=gb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||gb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ib(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ib(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ib(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ib(function(a){return function(b){return gb(a,b).length>0}}),contains:ib(function(a){return a=a.replace(cb,db),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ib(function(a){return W.test(a||"")||gb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:ob(function(){return[0]}),last:ob(function(a,b){return[b-1]}),eq:ob(function(a,b,c){return[0>c?c+b:c]}),even:ob(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:ob(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:ob(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:ob(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function tb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ub(a,b,c){for(var d=0,e=b.length;e>d;d++)gb(a,b[d],c);return c}function vb(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wb(a,b,c,d,e,f){return d&&!d[u]&&(d=wb(d)),e&&!e[u]&&(e=wb(e,f)),ib(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ub(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:vb(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=vb(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=vb(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sb(function(a){return a===b},h,!0),l=sb(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sb(tb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wb(i>1&&tb(m),i>1&&rb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xb(a.slice(i,e)),f>e&&xb(a=a.slice(e)),f>e&&rb(a))}m.push(c)}return tb(m)}function yb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=vb(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&gb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ib(f):f}return h=gb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,yb(e,d)),f.selector=a}return f},i=gb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&pb(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&rb(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&pb(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=jb(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),jb(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||kb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&jb(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||kb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),jb(function(a){return null==a.getAttribute("disabled")})||kb(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),gb}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+K.uid++}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){return M.access(a,b,c) +},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthx",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,bb=/<([\w:]+)/,cb=/<|&#?\w+;/,db=/<(?:script|style|link)/i,eb=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/^$|\/(?:java|ecma)script/i,gb=/^true\/(.*)/,hb=/^\s*\s*$/g,ib={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(ob(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(ob(c,"script"),kb),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(hb,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("