001package com.killcoding.jsplus; 002 003import java.io.File; 004import java.io.InputStream; 005import java.io.FileInputStream; 006import com.killcoding.file.BaseFile; 007import java.util.Scanner; 008import java.io.IOException; 009import java.nio.charset.Charset; 010import java.io.ByteArrayInputStream; 011import com.killcoding.jsplus.JsPlusContext; 012import com.killcoding.jsplus.JsContext; 013import com.killcoding.jsplus.GraalVmContext; 014import java.util.regex.Pattern; 015import java.util.regex.Matcher; 016import com.killcoding.log.LoggerFactory; 017import com.killcoding.log.Logger; 018import com.killcoding.log.LogRecord; 019import java.util.Map; 020import java.util.HashMap; 021import com.killcoding.tool.CommonTools; 022import java.util.Set; 023import com.killcoding.file.DiskFile; 024import java.util.concurrent.ConcurrentHashMap; 025 026public class TextScript { 027 028 public static String DEFAULT_ENGINE_NAME = "nashorn"; 029 030 protected boolean allowAllAccess = true; 031 032 protected final static Map<String,JsPlusContext> CONTEXT_MAPPING = new ConcurrentHashMap<String,JsPlusContext>(); 033 034 protected String prefix = "#"; 035 036 protected String varName = "theContent"; 037 038 protected JsPlusContext context = null; 039 040 protected Scanner scanner = null; 041 042 public TextScript() throws Exception{ 043 super(); 044 } 045 046 public TextScript(File scriptPath) throws Exception{ 047 super(); 048 049 if(scriptPath == null) throw new NullPointerException("The 'scriptPath' is null."); 050 051 byte[] scriptBytes = new DiskFile(scriptPath).readAllBytes(); 052 scanner = new Scanner(new ByteArrayInputStream(scriptBytes), BaseFile.CHARSET); 053 } 054 055 public TextScript(String script) throws Exception{ 056 super(); 057 058 if(script == null) throw new NullPointerException("The 'script' is null."); 059 060 byte[] scriptBytes = script.getBytes(BaseFile.CHARSET); 061 scanner = new Scanner(new ByteArrayInputStream(scriptBytes), BaseFile.CHARSET); 062 } 063 064 protected boolean isEnd(String line){ 065 return isMatched(getEndRegex(),line); 066 } 067 068 protected String parseEnd(String line){ 069 Pattern pattern = Pattern.compile(getEndRegex()); 070 Matcher matcher = pattern.matcher(line); 071 Map<String,String> fullMapping = new HashMap<String,String>(); 072 Map<String,String> scriptMapping = new HashMap<String,String>(); 073 StringBuffer sb = new StringBuffer(); 074 while(matcher.find()){ 075 String full = matcher.group(1); 076 String script = matcher.group(2); 077 String replaceToken = "#" + CommonTools.generateId(16) + "#"; 078 matcher.appendReplacement(sb,replaceToken); 079 fullMapping.put(replaceToken,full); 080 scriptMapping.put(replaceToken,script); 081 } 082 matcher.appendTail(sb); 083 String quoteLine = sb.toString(); 084 Set<String> tokens = fullMapping.keySet(); 085 for(String token : tokens){ 086 quoteLine = quoteLine.replaceAll(token," } "); 087 } 088 return quoteLine; 089 } 090 091 protected boolean isBlock(String line){ 092 return isMatched(getBlockRegex(),line); 093 } 094 095 protected boolean isDefault(String line){ 096 return isMatched(getDefaultRegex(),line); 097 } 098 099 protected String parseDefault(String line){ 100 Pattern pattern = Pattern.compile(getDefaultRegex()); 101 Matcher matcher = pattern.matcher(line); 102 Map<String,String> fullMapping = new HashMap<String,String>(); 103 Map<String,String> scriptMapping = new HashMap<String,String>(); 104 StringBuffer sb = new StringBuffer(); 105 while(matcher.find()){ 106 String full = matcher.group(1); 107 String script = matcher.group(2); 108 String replaceToken = "#" + CommonTools.generateId(16) + "#"; 109 matcher.appendReplacement(sb,replaceToken); 110 fullMapping.put(replaceToken,full); 111 scriptMapping.put(replaceToken,script); 112 } 113 matcher.appendTail(sb); 114 String quoteLine = sb.toString(); 115 Set<String> tokens = fullMapping.keySet(); 116 for(String token : tokens){ 117 quoteLine = quoteLine.replaceAll(token, scriptMapping.get(token).replaceAll(":","").trim() + ": "); 118 } 119 return quoteLine; 120 } 121 122 protected boolean isCase(String line){ 123 return isMatched(getCaseRegex(),line); 124 } 125 126 protected String parseCase(String line){ 127 Pattern pattern = Pattern.compile(getCaseRegex()); 128 Matcher matcher = pattern.matcher(line); 129 Map<String,String> fullMapping = new HashMap<String,String>(); 130 Map<String,String> scriptMapping = new HashMap<String,String>(); 131 StringBuffer sb = new StringBuffer(); 132 while(matcher.find()){ 133 String full = matcher.group(1); 134 String script = matcher.group(2); 135 String replaceToken = "#" + CommonTools.generateId(16) + "#"; 136 matcher.appendReplacement(sb,replaceToken); 137 fullMapping.put(replaceToken,full); 138 scriptMapping.put(replaceToken,script); 139 } 140 matcher.appendTail(sb); 141 String quoteLine = sb.toString(); 142 Set<String> tokens = fullMapping.keySet(); 143 for(String token : tokens){ 144 quoteLine = quoteLine.replaceAll(token, scriptMapping.get(token)); 145 } 146 return quoteLine; 147 } 148 149 protected boolean isCode(String line){ 150 return isMatched(getCodeRegex(),line); 151 } 152 153 protected String parseCode(String line){ 154 Pattern pattern = Pattern.compile(getCodeRegex()); 155 Matcher matcher = pattern.matcher(line); 156 Map<String,String> fullMapping = new HashMap<String,String>(); 157 Map<String,String> scriptMapping = new HashMap<String,String>(); 158 StringBuffer sb = new StringBuffer(); 159 while(matcher.find()){ 160 String full = matcher.group(1); 161 String script = matcher.group(2); 162 String replaceToken = "#" + CommonTools.generateId(16) + "#"; 163 matcher.appendReplacement(sb,replaceToken); 164 fullMapping.put(replaceToken,full); 165 scriptMapping.put(replaceToken,script); 166 } 167 matcher.appendTail(sb); 168 String quoteLine = sb.toString(); 169 Set<String> tokens = fullMapping.keySet(); 170 for(String token : tokens){ 171 quoteLine = quoteLine.replaceAll(token, scriptMapping.get(token)); 172 } 173 return quoteLine; 174 } 175 176 177 protected boolean isJump(String line){ 178 return isMatched(getJumpRegex(),line); 179 } 180 181 protected String parseJump(String line){ 182 Pattern pattern = Pattern.compile(getJumpRegex()); 183 Matcher matcher = pattern.matcher(line); 184 Map<String,String> fullMapping = new HashMap<String,String>(); 185 Map<String,String> scriptMapping = new HashMap<String,String>(); 186 StringBuffer sb = new StringBuffer(); 187 while(matcher.find()){ 188 String full = matcher.group(1); 189 String script = matcher.group(2); 190 String replaceToken = "#" + CommonTools.generateId(16) + "#"; 191 matcher.appendReplacement(sb,replaceToken); 192 fullMapping.put(replaceToken,full); 193 scriptMapping.put(replaceToken,script); 194 } 195 matcher.appendTail(sb); 196 String quoteLine = sb.toString(); 197 Set<String> tokens = fullMapping.keySet(); 198 for(String token : tokens){ 199 quoteLine = quoteLine.replaceAll(token, scriptMapping.get(token) + ";"); 200 } 201 return quoteLine; 202 } 203 204 protected boolean isElseIf(String line){ 205 return isMatched(getElseIfRegex(),line); 206 } 207 208 protected String parseElseIf(String line){ 209 Pattern pattern = Pattern.compile(getElseIfRegex()); 210 Matcher matcher = pattern.matcher(line); 211 Map<String,String> fullMapping = new HashMap<String,String>(); 212 Map<String,String> scriptMapping = new HashMap<String,String>(); 213 StringBuffer sb = new StringBuffer(); 214 while(matcher.find()){ 215 String full = matcher.group(1); 216 String script = matcher.group(2); 217 String replaceToken = "#" + CommonTools.generateId(16) + "#"; 218 matcher.appendReplacement(sb,replaceToken); 219 fullMapping.put(replaceToken,full); 220 scriptMapping.put(replaceToken,script); 221 } 222 matcher.appendTail(sb); 223 String quoteLine = sb.toString(); 224 Set<String> tokens = fullMapping.keySet(); 225 for(String token : tokens){ 226 quoteLine = quoteLine.replaceAll(token, " } " + scriptMapping.get(token) + " { "); 227 } 228 return quoteLine; 229 } 230 231 protected boolean isElse(String line){ 232 return isMatched(getElseRegex(),line); 233 } 234 235 protected String parseElse(String line){ 236 Pattern pattern = Pattern.compile(getElseRegex()); 237 Matcher matcher = pattern.matcher(line); 238 Map<String,String> fullMapping = new HashMap<String,String>(); 239 Map<String,String> scriptMapping = new HashMap<String,String>(); 240 StringBuffer sb = new StringBuffer(); 241 while(matcher.find()){ 242 String full = matcher.group(1); 243 String script = matcher.group(2); 244 String replaceToken = "#" + CommonTools.generateId(16) + "#"; 245 matcher.appendReplacement(sb,replaceToken); 246 fullMapping.put(replaceToken,full); 247 scriptMapping.put(replaceToken,script); 248 } 249 matcher.appendTail(sb); 250 String quoteLine = sb.toString(); 251 Set<String> tokens = fullMapping.keySet(); 252 for(String token : tokens){ 253 quoteLine = quoteLine.replaceAll(token, " } " + scriptMapping.get(token) + " { "); 254 } 255 return quoteLine; 256 } 257 258 protected boolean isComment(String line){ 259 return isMatched(getCommentRegex(),line); 260 } 261 262 protected boolean isLogic(String line){ 263 return isMatched(getLogicRegex(),line); 264 } 265 266 protected String parseLogic(String line){ 267 Pattern pattern = Pattern.compile(getLogicRegex()); 268 Matcher matcher = pattern.matcher(line); 269 Map<String,String> fullMapping = new HashMap<String,String>(); 270 Map<String,String> scriptMapping = new HashMap<String,String>(); 271 StringBuffer sb = new StringBuffer(); 272 while(matcher.find()){ 273 String full = matcher.group(1); 274 String script = matcher.group(2); 275 String replaceToken = "#" + CommonTools.generateId(16) + "#"; 276 matcher.appendReplacement(sb,replaceToken); 277 fullMapping.put(replaceToken,full); 278 scriptMapping.put(replaceToken,script); 279 } 280 matcher.appendTail(sb); 281 String quoteLine = sb.toString(); 282 Set<String> tokens = fullMapping.keySet(); 283 for(String token : tokens){ 284 quoteLine = quoteLine.replaceAll(token, scriptMapping.get(token) + " { "); 285 } 286 return quoteLine; 287 } 288 289 protected boolean isExpression(String line){ 290 return isMatched(getExpressionRegex(),line); 291 } 292 293 protected boolean isMatched(String regex,String line){ 294 Pattern pattern = Pattern.compile(regex); 295 Matcher matcher = pattern.matcher(line); 296 return matcher.find(); 297 } 298 299 protected String parseExpression(String line){ 300 Pattern pattern = Pattern.compile(getExpressionRegex()); 301 Matcher matcher = pattern.matcher(line); 302 Map<String,String> fullMapping = new HashMap<String,String>(); 303 Map<String,String> scriptMapping = new HashMap<String,String>(); 304 StringBuffer sb = new StringBuffer(); 305 while(matcher.find()){ 306 String full = matcher.group(1); 307 String script = matcher.group(2); 308 String replaceToken = "#" + CommonTools.generateId(16) + "#"; 309 matcher.appendReplacement(sb,replaceToken); 310 fullMapping.put(replaceToken,full); 311 scriptMapping.put(replaceToken,script); 312 } 313 matcher.appendTail(sb); 314 String quoteLine = parseRawText(sb.toString()); 315 Set<String> tokens = fullMapping.keySet(); 316 for(String token : tokens){ 317 quoteLine = quoteLine.replaceAll(token,"' + " + scriptMapping.get(token) + " + '"); 318 } 319 return quoteLine; 320 } 321 322 protected String parseRawText(String line){ 323 String quoteLine = line.replaceAll("'","\\\\'"); 324 return "'" + quoteLine + "'"; 325 } 326 327 public String parse() throws Exception { 328 return parse(false); 329 } 330 331 public String parse(boolean showScript) throws Exception { 332 boolean isWin = System.getProperty("os.name").toUpperCase().contains("WINDOWS"); 333 StringBuffer blockScript = new StringBuffer(); 334 StringBuffer all = new StringBuffer(); 335 boolean haveBlock = false; 336 try{ 337 while (scanner.hasNextLine()) { 338 boolean needlineBreak = false; 339 String line = scanner.nextLine(); 340 341 if(!haveBlock && isBlock(line)){ 342 haveBlock = true; 343 } 344 345 if(haveBlock && !isBlock(line) && !isEnd(line)){ 346 blockScript.append(line); 347 348 if(isWin) blockScript.append("\r\n"); 349 350 if(!isWin) blockScript.append("\n"); 351 352 continue; 353 354 } 355 356 if(haveBlock) { 357 haveBlock = !isEnd(line); 358 359 if(!haveBlock) continue; 360 } 361 362 if(isBlock(line)) continue; 363 364 if(isComment(line)) continue; 365 366 if(isExpression(line)){ 367 String parseLine = parseExpression(line); 368 all.append(varName + " += " + parseLine + ";"); 369 needlineBreak = true; 370 }else if(isLogic(line)){ 371 all.append(parseLogic(line)); 372 needlineBreak = false; 373 }else if(isElse(line)){ 374 all.append(parseElse(line)); 375 needlineBreak = false; 376 }else if(isElseIf(line)){ 377 all.append(parseElseIf(line)); 378 needlineBreak = false; 379 }else if(isJump(line)){ 380 all.append(parseJump(line)); 381 needlineBreak = false; 382 }else if(isCode(line)){ 383 all.append(parseCode(line)); 384 needlineBreak = false; 385 }else if(isCase(line)){ 386 all.append(parseCase(line)); 387 needlineBreak = false; 388 }else if(isDefault(line)){ 389 all.append(parseDefault(line)); 390 needlineBreak = false; 391 }else if(isEnd(line)){ 392 all.append(parseEnd(line)); 393 needlineBreak = false; 394 }else{ 395 all.append(varName + " += " + parseRawText(line) + ";"); 396 needlineBreak = true; 397 } 398 399 if(!needlineBreak){ 400 if(isWin) all.append("\r\n"); 401 402 if(!isWin) all.append("\n"); 403 } 404 405 if(needlineBreak && scanner.hasNextLine()){ 406 if(isWin) all.append(varName + " += '\\r\\n';\r\n"); 407 408 if(!isWin) all.append(varName + " += '\\n';\n"); 409 } 410 } 411 }finally{ 412 scanner.close(); 413 } 414 String allStr = all.toString(); 415 String functionScript = String.format("(function(){%s var %s = '';%s return %s;})()",blockScript.toString(),varName,allStr,varName); 416 if(showScript){ 417 return functionScript; 418 }else{ 419 if(context == null){ 420 useEngine(DEFAULT_ENGINE_NAME); 421 } 422 return context.evalScript(functionScript).toString(); 423 } 424 } 425 426 public Object evalScript(String script) throws Exception { 427 if(context == null){ 428 useEngine(DEFAULT_ENGINE_NAME); 429 } 430 return context.evalScript(script); 431 } 432 433 public Object eval(File scriptPath) throws Exception { 434 if(context == null){ 435 useEngine(DEFAULT_ENGINE_NAME); 436 } 437 return context.eval(scriptPath); 438 } 439 440 public TextScript addParams(Map<String,Object> params) throws Exception { 441 if(context == null){ 442 useEngine(DEFAULT_ENGINE_NAME); 443 } 444 Set<String> keys = params.keySet(); 445 for(String key : keys){ 446 this.context.put(key,params.get(key)); 447 } 448 return this; 449 } 450 451 public TextScript useEngine(String engineName) throws Exception { 452 this.context = CONTEXT_MAPPING.get(engineName); 453 if(this.context == null){ 454 TextScript.initEngine(allowAllAccess,engineName); 455 this.context = CONTEXT_MAPPING.get(engineName); 456 if(this.context == null){ 457 TextScript.initEngine(allowAllAccess,engineName); 458 throw new Exception("The context is null, please init engine (e.g. TextScript.initEngine(engineName))."); 459 } 460 } 461 462 return this; 463 } 464 465 public static boolean initEngine(String engineName) throws Exception { 466 return initEngine(true,engineName); 467 } 468 469 public static boolean initEngine(boolean allowAllAccess,String engineName) throws Exception { 470 JsPlusContext _context = CONTEXT_MAPPING.get(engineName); 471 if(_context == null){ 472 boolean isGraalJs = JsPlusContext.isGraalJs(engineName); 473 boolean isNashornJs = JsPlusContext.isNashornJs(engineName); 474 if(isGraalJs){ 475 _context = new GraalVmContext(engineName); 476 }else if(isNashornJs){ 477 _context = new NashornContext(engineName); 478 }else{ 479 _context = new JsContext(engineName); 480 } 481 _context.setAllowAllAccess(allowAllAccess); 482 _context.initScriptEngineManager(); 483 CONTEXT_MAPPING.put(engineName,_context); 484 return true; 485 }else{ 486 Logger.systemDebug(TextScript.class,"This engine '{}' already exists.",engineName); 487 return false; 488 } 489 } 490 491 public JsPlusContext getContext() throws Exception { 492 if(context == null){ 493 useEngine(DEFAULT_ENGINE_NAME); 494 } 495 return context; 496 } 497 498 public void setPrefix(String prefix){ 499 this.prefix = prefix; 500 } 501 502 public String getPrefix(){ 503 return prefix; 504 } 505 506 public void setVarName(String varName){ 507 this.varName = varName; 508 } 509 510 public String getVarName(){ 511 return varName; 512 } 513 514 protected String getExpressionRegex(){ 515 return "(" + getPrefix() + "\\{[^\\S]*([^\\}]*)[^\\S]*\\})"; 516 } 517 518 protected String getLogicRegex(){ 519 return "([^\\S]*" + getPrefix() +"((if|switch|for|do|while){1}[^\\S]*\\([^\\S]*(.*))[^\\S]*)"; 520 } 521 522 protected String getElseRegex(){ 523 return "([^\\S]*" + getPrefix() + "(else){1}[^\\S]*)$"; 524 } 525 526 protected String getElseIfRegex(){ 527 return "([^\\S]*" + getPrefix() + "((else[^\\S]+if){1}[^\\S]*\\([^\\S]*(.*))[^\\S]*)"; 528 } 529 530 protected String getEndRegex(){ 531 return "([^\\S]*" + getPrefix() +"(end){1}[^\\S]*)$"; 532 } 533 534 protected String getJumpRegex(){ 535 return "([^\\S]*" + getPrefix() + "(break|continue){1}[^\\S]*)$"; 536 } 537 538 protected String getCaseRegex(){ 539 return "([^\\S]*" + getPrefix() + "(case[^\\S]+.*))"; 540 } 541 542 protected String getDefaultRegex(){ 543 return "([^\\S]*" + getPrefix() + "(default[^\\S]*[:]?)[^\\S|$]?)"; 544 } 545 546 protected String getCodeRegex(){ 547 return "([^\\S]*" + getPrefix() + "code[^\\S]+(.*))"; 548 } 549 550 protected String getCommentRegex(){ 551 return "([^\\S]*" + getPrefix() + "comment[^\\S]+(.*))"; 552 } 553 554 protected String getBlockRegex(){ 555 return "([^\\S]*" + getPrefix() +"(block){1}[^\\S]*)$"; 556 } 557 558 public void setAllowAllAccess(boolean allowAllAccess){ 559 this.allowAllAccess = allowAllAccess; 560 } 561 562 public boolean isAllowAllAccess(){ 563 return this.allowAllAccess; 564 } 565 566}