使用ANTLR4,用于代码镜像和基于Web的DSL的Primefaces扩展

DSL是很酷的东西,但是我不清楚它们有什么用。

然后我意识到它们对以下方面有好处:

  • 摆脱复杂的UI

意思是

  • 更快的做事方式

而已。 当我阅读此博客时,我得出了这个结论。

如果您的用户是技术人员,并且不惧怕类似SQL的语法处理方式,则DSL特别适合

  • 你有语法高亮
  • 你有代码补全

否则,DSL有点烂。

因此,我不得不向客户提出一些概念证明。 他有模糊的要求,要准确地提取团队需要的东西并不容易(他们需要很多东西,而且他很忙),因此DSL在此过程中可以提供很多帮助,因为人们在被迫时必须认真考虑他们的需求面对一种语法(甚至很小的一种)。

因此,我采用了以下技术:

  • 用于代码镜像的JSF库Primefaces扩展
  • ANTLR4 (与ANTLR3相比有很大的改进,并且本书很棒)

不幸的是,我无法在两个工具中重用语法。 实际上,我找不到能做到这一点的解决方案。 至少对于基于Web的JSF解决方案。 而且没有时间学习。 因此,我不得不稍作修改。

首先,我们需要语法。 ANTLR4比ANTLR3更好,因为现在接线代码是通过访问者和侦听器完成的。 语法内没有更多的Java代码。 那很棒,而且更容易使用。

所以你可以有一个这样的语法

grammar Grammar;
options
{language = Java;
}
@lexer::header {package parsers;
}@parser::header {package parsers;
}
eval     :     expr EOF;
expr    :    'JOB' (jobName)? type 'TARGET' targetList ('START' startExpr)?
startExpr   :    'AT' cronTerm|    'AFTER' timeAmount timeUnits;
timeAmount: INT;   
jobName: STRING;   
targetList: STRING (',' STRING)*;   
type    :    deleteUser|    createUser;deleteUser: opDelete userName;
createUser: opCreate userName;
opDelete: 'DELETE';
opCreate: 'CREATE';
userName: STRING;cronTerm: '!'? (INT | '-' | '/' | '*' | '>' | '<')+;timeUnits:    'MINUTES'|    'HOURS'|    'DAYS'|    'WEEKS'|    'MONTHS';WS  :   [ \t\r\n]+ -> skip;STRING:  '"' ( ESC_SEQ | ~('\\'|'"') )* '"';fragment
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;fragment
ESC_SEQ:   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')|   UNICODE_ESC|   OCTAL_ESC;fragment
OCTAL_ESC:   '\\' ('0'..'3') ('0'..'7') ('0'..'7')|   '\\' ('0'..'7') ('0'..'7')|   '\\' ('0'..'7');fragment
UNICODE_ESC:   '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT;ID  :    ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;INT :    '0'..'9'+;

要编译语法,请尝试

public static void main(String[] args) {String[] arg0 = {"-visitor","/pathto/Grammar.g4"};org.antlr.v4.Tool.main(arg0);}

然后,ANTLR将为您生成类。

在我们的例子中,我们想访问解析树并检索我们想要的值。 我们这样做扩展了生成的抽象类。

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;import org.antlr.v4.runtime.tree.ErrorNode;import bsh.EvalError;
import bsh.Interpreter;public class MyLoader2 extends GrammarBaseVisitor<Void> {private String jobName,cronTerm,timeUnits,userName,jobType;private List<String> targetList;private boolean now,errorFound;private int timeAmount;   private Interpreter bsh = new Interpreter();private String eval(String s) throws EvaluationException{try {if (!s.startsWith("\"")){return s;}bsh.eval("String s="+s);return (String)bsh.eval("s");} catch (EvalError e) {throw new EvaluationException(s);}}@Overridepublic Void visitTimeAmount(TimeAmountContext ctx) {try{this.timeAmount = Integer.parseInt(ctx.getText());}catch(java.lang.NumberFormatException nfe){throw new InvalidTimeAmountException(ctx.getText());}return super.visitTimeAmount(ctx);}@Overridepublic Void visitUserName(UserNameContext ctx) {this.userName = eval(ctx.getText());return super.visitUserName(ctx);}@Overridepublic Void visitCronTerm(CronTermContext ctx) {this.cronTerm = eval(ctx.getText());return super.visitCronTerm(ctx);}@Overridepublic Void visitTimeUnits(TimeUnitsContext ctx) {this.timeUnits = ctx.getText();return super.visitTimeUnits(ctx);}@Overridepublic Void visitTargetList(TargetListContext ctx) {this.targetList = toStringList(ctx.getText());return super.visitTargetList(ctx);}@Overridepublic Void visitJobName(JobNameContext ctx) {this.jobName = eval(ctx.getText());return super.visitJobName(ctx);}@Overridepublic Void visitOpCreate(OpCreateContext ctx) {this.jobType = ctx.getText();return super.visitOpCreate(ctx);}@Overridepublic Void visitOpDelete(OpDeleteContext ctx) {this.jobType = ctx.getText();return super.visitOpDelete(ctx);}private List<String> toStringList(String text) {List<String> l = new ArrayList<String>();StringTokenizer st = new StringTokenizer(text," ,");while(st.hasMoreElements()){l.add(eval(st.nextToken()));}return l;}private Map<String, String> toMapList(String text) throws InvalidItemsException, InvalidKeyvalException {Map<String, String> m = new HashMap<String, String>();if (text == null || text.trim().length() == 0){return m;}String[] items = text.split(",");if (items.length == 0){throw new InvalidItemsException();}for(String item:items){String[] keyval = item.split("=");if (keyval.length == 2){m.put(keyval[0], keyval[1]);}else{throw new InvalidKeyvalException(keyval.length);}}return m;}public String getJobName() {return jobName;}public String getCronTerm() {return cronTerm;}public String getTimeUnits() {return timeUnits;}public String getUserName() {return userName;}public String getJobType() {return jobType;}public List<String> getTargetList() {return targetList;}public boolean isNow() {return now;}public int getTimeAmount() {return timeAmount;}@Overridepublic Void visitOpNow(OpNowContext ctx) {this.now = ctx.getText().equals("NOW");return super.visitOpNow(ctx);}public boolean isErrorFound() {return errorFound;}@Overridepublic Void visitErrorNode(ErrorNode node) {this.errorFound = true;return super.visitErrorNode(node);}
}

请注意, beanshell解释器用于将“ xyz”之类的字符串评估为xyz。 这对于其中包含转义引号和字符的字符串特别有用。

因此,您有了语法和visiter / loader bean,然后我们可以对其进行测试:

private static MyLoader getLoader(String str){ANTLRInputStream input = new ANTLRInputStream(str);GrammarLexer lexer = new GrammarLexer(input);CommonTokenStream tokens = new CommonTokenStream(lexer);GrammarParser parser = new GrammarParser(tokens);ParseTree tree = parser.eval();MyLoader loader = new MyLoader();loader.visit(tree);return loader;
}public static void main(String[] args){MyLoader loader = getLoader("JOB \"jobName\" CREATE \"myuser\" TARGET \"site1\",\"site2\" START AFTER 1 DAY");System.out.println(loader.getJobName());System.out.println(loader.getJobType());
}

大。 现在是黑客。 Code Mirror支持自定义语法,但JSF Primefaces扩展中不存在 。 因此,我打开了resources-codemirror-1.2.0.jar,打开了/META-INF/resources/primefaces-extensions/codemirror/mode/modes.js文件,对其进行了格式化 (以便我可以阅读),然后我刚刚选择了最简单的语言作为我的新自定义sintax荧光笔!

我改名了

(...)
}, "xml"), CodeMirror.defineMIME("text/x-markdown", "markdown"), CodeMirror.defineMode("mylanguage", function (e) {
(...)var t = e.indentUnit,n, i = r(["site", "type", "targetList"]),s = r(["AT","AFTER","CREATE","MINUTES","HOURS","TARGET","MONTHS","JOB","DAYS","DELETE","START","WEEKS" ]),
(...)
}), CodeMirror.defineMIME("text/x-mylanguage", "mylanguage"), CodeMirror.defineMode("ntriples", function () {(...)

那些在“ s = r”中用大写字母表示的标记是将被突出显示的标记,而在“ i = r”中的那些标记则是将被突出显示的标记。 为什么我们都想要? 因为第二种类型是“占位符”,我的意思是,我们将它们用于自动填充内容。

好,那么您的JSF xhtml页面将如下所示

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"xmlns:f="http://java.sun.com/jsf/core"xmlns:h="http://java.sun.com/jsf/html"xmlns:ui="http://java.sun.com/jsf/facelets"xmlns:p="http://primefaces.org/ui"xmlns:pe="http://primefaces.org/ui/extensions"><h:body>
<h:form id="form">
<pe:codeMirrorid="codeMirror"style="width:600px;" mode="myLanguage" widgetVar="myCodeMirror" theme="eclipse"value="#{myMB.script}"lineNumbers="true"             completeMethod="#{myMB.complete}" extraKeys="{ 'Ctrl-Space': function(cm) { PF('myCodeMirror').complete(); }}"/> 
<p:commandButton value="Verify" action="#{myMB.verify}" />
(...)

现在,我们需要自动完成功能。 这是无聊的部分。 您必须手动完成大多数完成工作,因为没有上下文信息(请记住,我没有时间学习……),所以快速而肮脏的方式是这样的

in myMBpublic List<String> complete(final CompleteEvent event) {try {return this.myEJB.complete(event.getToken());} catch (Exception e) {jsfUtilEJB.addErrorMessage(e,"Could not complete");return null;}} in myEJBprivate static final String    SITE    = "site_";
public List<String> complete(String token) throws Exception {if (token == null || token.trim().length() == 0){return null;}else{List<String> suggestions = new ArrayList<String>();switch(token){//first search variablescase "targetlist":for(String v:TARGETS){suggestions.add(v);   }break;case "site":List<Site> allSites = this.baseService.getSiteDAO().getAll();for(Site s:allSites){suggestions.add("\""+SITE+s.getName()+"\"");}break;case "type":suggestions.add("DELETE \"userName\"");suggestions.add("CREATE \"userName\"");break;case "AT":suggestions.add("AT \"cronExpression\"");suggestions.add("AT \"0 * * * * * * *\"");break;case "AFTER":for(int a:AMOUNTS){for(String u:UNITS){if (a == 1){suggestions.add("AFTER"+" "+a+" "+u);}else{suggestions.add("AFTER"+" "+a+" "+u+"S");}}}break;case "TARGET":for(String v:TARGETS){suggestions.add("TARGET "+v+"");   }break;case "JOB":suggestions.add("JOB \"jobName\" \ntype \nTARGET targetlist \nSTART");break;case "START":suggestions.add("START AT \"cronExpression\"");suggestions.add("START AT \"0 * * * * * * *\"");for(int a:AMOUNTS){for(String u:UNITS){if (a == 1){suggestions.add("START AFTER"+" "+a+" "+u);}else{suggestions.add("START AFTER"+" "+a+" "+u+"S");}}}suggestions.add("START NOW");break;case "DELETE":suggestions.add("DELETE \"userName\"");break;case "CREATE":suggestions.add("CREATE \"userName\"");break;default:if (token.startsWith(SITE)){List<Site> matchedSites = this.baseService.getSiteDAO().getByPattern(token.substring(SITE.length())+"*");for(Site s:matchedSites){suggestions.add("\""+SITE+s.getName()+"\"");}}else{//then search substringsfor(String kw:KEYWORDS){if (kw.toLowerCase().startsWith(token.toLowerCase())){suggestions.add(kw);}}}}//end switch//remove dups and sortSet<String> ts = new TreeSet<String>(suggestions);           return new ArrayList<String>(ts);}
}private static final int[] AMOUNTS = {1,5,10};
private static final String[] UNITS = {"MINUTE","HOUR","DAY","WEEK","MONTH"};
private static final String[] TARGETS = {"site"};/** KEYWORDS are basic suggestions*/
private static final String[] KEYWORDS = {"AT","AFTER","CREATE","MINUTES","HOURS","TARGET","MONTHS","JOB","DAYS","DELETE","START","WEEKS"};

因此,关键字的自动填充内容将仅向您显示字段和更多关键字,而“占位符”(还记得jar中的Codemirror javascript中的小写关键字吗?)用从数据库中获取的实际值来完成。 另外,您可以使用部分字符串来检索那些以子字符串开头的字符串,如下所示:

屏幕截图

当然,在JPA中类似模式的搜索可以这样执行:

public abstract class GenericDAO<E> {protected EntityManager entityManager;private Class<E> clazz;private EntityType<E> pClass;@SuppressWarnings("unchecked")public GenericDAO(EntityManager entityManager) {this.entityManager = entityManager;ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();this.clazz = (Class<E>) genericSuperclass.getActualTypeArguments()[0];EntityManagerFactory emf = this.entityManager.getEntityManagerFactory();Metamodel metamodel = emf.getMetamodel();this.pClass = metamodel.entity(clazz);}public List<E> getByPattern(String pattern) {pattern = pattern.replace("?", "_").replace("*", "%");CriteriaBuilder cb = entityManager.getCriteriaBuilder();CriteriaQuery<E> q = cb.createQuery(clazz);Root<E> entity = q.from(clazz);SingularAttribute<E, String> singularAttribute = (SingularAttribute<E, String>) pClass.getDeclaredSingularAttribute(getNameableField(clazz));Path<String> path = entity.get(singularAttribute);q.where(cb.like(path, pattern));q.select(entity);TypedQuery<E> tq = entityManager.createQuery(q);List<E> all = tq.getResultList();return all;}private String getNameableField(Class<E> clazz) {for(Field f : clazz.getDeclaredFields()) {for(Annotation a : f.getAnnotations()) {if(a.annotationType() == Nameable.class) {return f.getName();}}}return null;}
(...)

其中Nameable是您的实体类的注释:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Nameable {
}

使用它来注释实体类中的单个列,即String。 像这样:

@Entity
@Table(uniqueConstraints=@UniqueConstraint(columnNames={"name"}))
public class Site implements Serializable {/****/private static final long serialVersionUID = 8008732613898597654L;@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@Nameable@Column(nullable=false)private String name;
(...)

当然,“验证”按钮只是获取您的脚本并将其推送到加载程序中。

参考:来自Leo's Notepad博客的JCG合作伙伴 Leonardo Shikida 使用ANTLR4,用于代码镜像的Primefaces扩展和基于Web的DSL玩。

翻译自: https://www.javacodegeeks.com/2014/01/playing-with-antlr4-primefaces-extensions-for-code-mirror-and-web-based-dsls.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/364966.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

js 动态绑定事件 on click 完美解决绑定不成功

动态绑定坑了多少人..... //绑定 $("ol").on("click","li a",function(){ ... }) //解绑 $("ol").off("click","li a"); 完美解决动态绑定: ol 为动态生成html的父类. li a 为其动态生成的html, 绑定使用on…

连接mysql超时时间设置多少_怎么设置数据库的连接数和连接超时时间

如何设置数据库的连接数和连接超时时间连接数的话可以修改spfile文件来约束查看当前的连接数&#xff1a;select count(*) from v$process;–数据库允许的最大连接数&#xff1a;select value from v$parameter where name ‘processes’&#xff1b;–修改最大连接数&#xf…

【学习笔记】慕课网—Java设计模式精讲 第3章 软件设计七大原则-3-4 单一职责原则...

/** * 软件设计七大原则-单一职责原则 学习笔记 * author cnRicky * date 2018.11.10 */单一职责原则 定义&#xff1a;不要存在多于一个导致类变更的原因一个类只负责一个职责&#xff0c;如果分别有两个职责&#xff0c;那就建立两个类分别负责职责1和职责2一个类/接口/方法只…

移动架构-数据库分库和全版本升级

在项目中&#xff0c;往往涉及到数据库的版本升级&#xff0c;通常会有两种升级方式&#xff0c;一种是纯代码实现&#xff0c;一种是脚本实现&#xff0c;这里使用脚本升级&#xff0c;这样的升级方式更便于维护 思路 全版本升级&#xff0c;重点在于数据的迁移&#xff0c;这…

Java中的硬件事务性内存,或者为什么同步将再次变得很棒

总览 硬件事务内存有潜力允许多个线程同时以推测方式访问相同的数据结构&#xff0c;并使缓存一致性协议确定是否发生冲突。 HTM旨在为您提供细粒度锁定的可伸缩性&#xff0c;粗粒度锁定的简单性以及几乎没有锁定的性能。 如果JVM支持&#xff0c;则您的程序或库是使用过程粒度…

mysql5.7乱码_mysql5.7中解决中文乱码的问题

在使用mysql5.7时&#xff0c;会发现通过web端向数据库中写入中文后会出现乱码&#xff0c;但是在数据库中直接操作SQL语句插入数据后中文就显示正常&#xff0c;这个问题怎么解决呢&#xff1f;此处不对mysql的数据编码过程和原理进行讲解&#xff0c;如果有兴趣的同学可以自己…

a 链接点击下载

1. 将链接设置为.zip 结尾2.在a元素中添加download 属性,&#xff08;目前只有chrome、firefox和opera支持&#xff09; function download(src) { var $a $("<a></a>").attr("href", src).attr("download", "img.png");…

Codeforces Global Round 2 D. Frets On Fire (动态开点线段树,沙雕写法)

题目链接&#xff1a;D. Frets On Fire 思路&#xff1a;明明可以离散化二分写&#xff0c;思路硬是歪到了线段树上&#xff0c;自闭了&#xff0c;真实弟弟&#xff0c;怪不得其他人过得那么快 只和查询的区间长度有关系&#xff0c;排完序如果相邻的两个点的差值小于等于查询…

hdu 1069 Monkey and Banana 【动态规划】

题目 题意&#xff1a;研究人员要测试猴子的IQ&#xff0c;将香蕉挂到一定高度&#xff0c;给猴子一些不同大小的箱子&#xff0c;箱子数量不限&#xff0c;让猩猩通过叠长方体来够到香蕉。 现在给你N种长方体&#xff0c; 要求&#xff1a;位于上面的长方体的长和宽 要小于 …

使用带有OAuth2的Web应用程序和使用JWT来调用API – WSO2 API Manager

在这篇文章中&#xff0c;我将分享我在WSO2 API管理器&#xff08;API-M&#xff09;方面的经验和理解&#xff0c;以解决该行业中非常普遍和有用的情况。 简要地说明以下流程。 公开了一个API&#xff0c;供应用程序开发人员在API Manager&#xff08;可为API添加访问控制&a…

git仓库的推送问题

git仓库的推送问题 推送问题 先说结论。先拉取&#xff0c;pull拉取完之后再commi提交。 如果先提交&#xff0c;再拉取的话&#xff0c;git会提示项目冲突&#xff0c;此时需要先解决冲突&#xff0c;而某些时候&#xff0c;我们不一定会处理好所有的冲突&#xff0c;而导致…

scala的插值器

Scala 为我们提供了三种字符串插值的方式&#xff0c;分别是 s, f 和 raw。它们都是定义在 StringContext 中的方法。 s 字符串插值器 val a 2println(s"小明今年$a 岁了") f 字符串插值器 它除 s 的功能外(不指定格式就和 s 一样)&#xff0c;还能进行格式化输出&a…

src/main/resorces applicationContext.xml

<?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:context"http://www.springframework.org/schema/context"xmlns:aop"http://www.springframework.org/schem…

layui的checkbox示例

1.html页面: var isSkipcheckbox ;if (appOptions.isSkip ! "0") {isSkipcheckbox checked"";}<input type"checkbox" name"" lay-filter"type" lay-skin"primary" title"是否允许跳过" value&quo…

confluence 编辑器加载_Onlyoffice集成Confluence的工作原理

onlyoffice 与 confluence集成使用&#xff0c;使用onlyoffice connector for confluence 插件对接confluence插件在confluence中创建了一个新的按钮动作&#xff0c;Edit in OnlyOffice 用来处理office文档。这允许多用户实时协作并且将变更返回到confluence中。具体工作原理&…

红帽JBoss BRMS和BPMS富客户端框架展示了与GWT / Errai / UberFire和AngularJS的多语言集成...

上周&#xff0c;我发布了一个博客&#xff0c;重点介绍了我的演示文稿&#xff0c;该演示文稿展示了我们在BRMS和BPMS平台内完成的工作&#xff0c;Drools和jBPM项目的产品化版本所产生的丰富客户端平台。 该演示文稿是所有屏幕截图和视频&#xff0c;您可以在此处找到博客和幻…

AJAX的内容

第 1 章&#xff1a;原生 AJAX 1.1 AJAX 简介 AJAX 全称为 Asynchronous JavaScript And XML&#xff0c;就是异步的 JS 和 XML。 通过 AJAX 可以在浏览器中向服务器发送异步请求&#xff0c;最大的优势&#xff1a;无刷新获取数据。 AJAX 不是新的编程语言&#xff0c;而是…

洛谷P1099 树网的核

传送门 80分 $ Floyd $ 树的直径可以通过枚举求出。直径的两个端点$ maxi,maxj $ &#xff0c;由此可知对于一个点 $ k $ &#xff0c;如果满足 $ d[maxi][k]d[k][maxj]d[maxi][maxj] $ 那么 $ k $ 点一定在直径上。分别枚举位于直径上的起点 $ s $ 与终点 $ t $ 。 $ ecg $ 定…

HTML中直接写js 函数

1.在HTML中直接写JS函数: <body οnlοad"javascript:{window.location.hrefhttp://www.baidu.com/}">2.写在script标签里的话就是&#xff1a; <script>function tiao(){window.location.href"http://www.baidu.com/"}</script><!--…

选择通过更改内容类型返回的详细程度,第二部分

在上一篇文章中 &#xff0c;我们研究了使用MOXy的功能来控制特定实体的数据输出级别。 这篇文章着眼于Jersey 2.x提供的抽象&#xff0c;它允许您定义一组自定义的批注以具有相同的效果。 与之前一样&#xff0c;我们几乎没有什么琐碎的资源可以返回Jersey将为我们转换为JSON…