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

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

  1. antlr4 代码 语法树_使用ANTLR4,用于代码镜像和基于Web的DSL的Primefaces扩展

    antlr4 代码 语法树 DSL是很酷的东西,但我不清楚它们有什么用. 然后我意识到它们对以下方面有好处: 摆脱复杂的UI 意思是 更快的做事方式 而已. 当我阅读此博客时,我得出了这个结论. 如果 ...

  2. 转:一个简单的基于WEB的QTP自动化测试框架-SAFFRON

    来源: http://www.itestware.com/ctest/index.php?option=com_content&view=article&id=62:webqtp-sa ...

  3. 【ASP.NET教程-WP教程15】ASP.NET Web Pages - C# 和 VB 实例简单而强大的开发框架,可用于构建动态的、基于Web的应用程序。它提供了一种轻量级的方式来创建和管理网页

    ASP.NET Web Pages - C# 和 VB 实例 ASP.NET Web Pages 是一种简单而强大的开发框架,可用于构建动态的.基于Web的应用程序.它提供了一种轻量级的方式来创建和管 ...

  4. 用户信息填写web代码_基于web的自定义表单引擎

    所谓表单引擎 表单引擎,又可称之为表单流程.流程表单.工作流表单,是基于Web界面上可视化编辑的表单设计系统,可设置数据库的字段.属性,设置模块的配置.目前市面上的地带吗无代码表单引擎,可以为企业信息 ...

  5. 一个快速实现彩屏应用的跨平台快速原型开发工具平台,最重要的是还免费!8ms.xyz平台原以为是单片机版墨刀,今天上去玩了才知道平台厉害的很,基于WEB端免搭建开发环境,跑的还是C代码编译出来的程序!

    哈哈哈哈,最近发现一个好用的在线编译.下载.烧录的跨平台快速原型开发工具平台,名字好记–8ms,单看名字是真的不知道干嘛的,不知道为啥叫这个?不多想了,好用就得分享给大家,独乐乐不如众乐乐呀-- 好用 ...

  6. Webmin--一个用于Linux基于Web的系统管理工具

    Webmin是一个用于Linux系统管理的开源的基于web的系统管理配置工具.有了这个工具的帮助,我们可以管理内部的系统配置,诸如设置用户账户,磁盘配额,像Apache, DNS, PHP, MySQ ...

  7. 基于web的通用文本标注工具MarkTool in NLP

    向AI转型的程序员都关注了这个号???????????? 机器学习AI算法工程   公众号:datayx 由于市面上的文本标注工具无法满足实际项目的标注场景需求,因此本项目自主开发了基于web的文本标 ...

  8. springboot基于web的摩托车销售系统的设计与实现毕业设计源码031706

    基于web的摩托车销售系统的设计与实现 摘 要 信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难 ...

  9. ssm+mysql+ssm基于web的酒店预订及个性化服务系统 毕业设计-附源码241822

    酒店预订及个性化服务系统 摘 要 信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克的课题. ...

最新文章

  1. Bert时代的创新:Bert应用模式比较及其它 | 技术头条
  2. R语言偏相关或者部分相关性系数计算实战:使用psych包计算(Partial Correlation)偏相关或者部分相关性系数并用建设检验检验相关性的显著性
  3. 用SCCM2007 R2分发软件,SCCM系列之五
  4. Python种reverse和reversed反转列表的操作方法
  5. 《Linux菜鸟入门》Linux网络管理
  6. ubuntu 升级python3_ubuntu16.04安装python3
  7. 最近参加一个公司的入职培训
  8. linux redhat命令大全
  9. 设计模式 代理模式(Proxy Pattern)
  10. python+selenium自动创建随笔
  11. echart重新加载数据_在asp.net core中动态加载最新的配置信息
  12. jQuery EasyUI/TopJUI创建日期输入框
  13. 计算机研究生复试常见面试题——计算机网络部分
  14. python入门区块链技术_区块链教程
  15. mac显示所有文件后缀名
  16. android 手机内存uri_[Android]Android数据的四种存储方式 - Ron Ngai - 博客园
  17. 【从零开始学深度学习编译器】九,TVM的CodeGen流程
  18. Axure实战——实现登录注册功能
  19. 荣耀笔试(8.16)
  20. Tita:2021年的绩效考核(上)

热门文章

  1. 龙芯linux内核,龙芯的linux kernel,内核开发与编译
  2. android 监听安装来源_Flutter插件开发之APK自动安装
  3. BigDecimal保留两位小数,不足两位补0
  4. IDEA导入Eclipse项目
  5. layui 单独使用日期组件
  6. android url回调json,【求助】本地页面如何取某个URL返回的json
  7. oracle中闪回和回滚,oracle闪回操作详解
  8. js 时间戳转换成时间_JavaScript 时间戳转成日期格式
  9. 使用SpringBoot搭建一个简单的webSocket服务
  10. java ee7帮助文档_帮助推动Java EE向前发展