org.gramar.ast.SourceRegion Maven / Gradle / Ivy
package org.gramar.ast;
import java.util.HashMap;
import java.util.Stack;
import org.gramar.ITagHandler;
import org.gramar.exception.IllFormedTemplateException;
import org.gramar.model.DocumentHelper;
import org.gramar.model.ModelAccess;
import org.gramar.tag.StaticTextTag;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
/**
* A SourceRegion represents a substring of a template's source. When the parser parses
* a template, the parser produces an array of SourceRegions which together represent the
* entire template source. The information within a SourceRegion is consumed by compression
* and DOM construction. It is also intended to be used by colorization logic in template
* editors
*
* @author chrisgerken
*
*/
public class SourceRegion {
public static int TYPE_TEXT = 0;
public static int TYPE_TAG = 1;
public static int TYPE_END_TAG = 2;
public static int TYPE_EMPTY_TAG = 3;
public static int TYPE_DIRECTIVE = 4;
public static int TYPE_COMMENT = 5;
private String content;
private int start;
private int end;
private int type;
private boolean error = false;
private String errorText = "";
private int linenum;
private int col;
private String production;
private HashMap attrs = null;
private TagInfo tagInfo;
public SourceRegion(String content, int start, int end, int type) {
super();
this.content = content;
this.start = start;
this.end = end;
this.type = type;
}
public String getContent() {
return content;
}
public int getStart() {
return start;
}
public int getEnd() {
return end;
}
public int getType() {
return type;
}
public void setStart(int linenum, int col) {
this.linenum = linenum;
this.col = col;
}
public String getProduction() {
return production;
}
public void setProduction(String production) {
this.production = production;
}
public int getLinenum() {
return linenum;
}
public int getCol() {
return col;
}
public String toString() {
String result = "Text";
if (type == TYPE_DIRECTIVE) { result = "Directive"; }
if (type == TYPE_TAG) { result = "Tag"; }
if (type == TYPE_END_TAG) { result = "End Tag"; }
if (type == TYPE_EMPTY_TAG) { result = "Empty Tag"; }
String buf = content;
if (buf.length() > 20) { buf = buf.substring(0,20) + "...";}
return result + ": "+buf;
}
/*
* Return the attributes from the region. Formats include:
*
* TYPE_DIRECTIVE: <%@taglib id="org.eclipse.jet.controlTags" prefix="cc"%>
* TYPE_TEXT: (none)
* TYPE_EMPTY_TAG:
* TYPE_TAG:
* TYPE_END_TAG:
*
*/
public HashMap getAttributes() throws IllFormedTemplateException {
if (isEndTag() | isText() | isComment()) {
return new HashMap();
}
if (attrs == null) {
if (isDirective()) {
String buf = content.substring(3,content.length()-2);
attrs = attributesFrom(buf);
}
if (isTag() | isEmptyTag()) {
int index = 0;
while ((index -1) {
String buf = content.substring(index);
index = Math.max(buf.lastIndexOf("\""), buf.lastIndexOf("'")) + 1;
buf = buf.substring(0,index);
attrs = attributesFrom(buf);
} else {
attrs = new HashMap();
}
}
}
return attrs;
}
/*
* given a String of the form: a="" b='' c=""
* return a hashmap of attribute names and literal values
*/
private HashMap attributesFrom(String innards) throws IllFormedTemplateException {
HashMap map = new HashMap();
try {
String tag = " ";
Document document = DocumentHelper.buildModel(tag);
Node[] node = ModelAccess.getDefault().getNodes(document, "root/tag/attribute::*", false, null);
for (Node n: node) {
map.put(n.getNodeName(), String.valueOf(n.getNodeValue()));
}
} catch (Exception e) {
throw new IllFormedTemplateException(this, "Invalid tag syntax");
}
return map;
}
public boolean isText() {
return type == TYPE_TEXT;
}
public boolean isTag() {
return type == TYPE_TAG;
}
public boolean isEndTag() {
return type == TYPE_END_TAG;
}
public boolean isEmptyTag() {
return type == TYPE_EMPTY_TAG;
}
public boolean isDirective() {
return type == TYPE_DIRECTIVE;
}
public boolean isComment() {
return type == TYPE_COMMENT;
}
public boolean closes(SourceRegion region) {
return (type == TYPE_TAG) &&
(region.getType() == TYPE_END_TAG) &&
(region.getTagInfo().getTagName().equalsIgnoreCase(getTagInfo().getTagName()));
}
public SourceRegion append(SourceRegion sr) {
return new SourceRegion(content+sr.content, start, sr.end, type);
}
public TagInfo getTagInfo() {
return tagInfo;
}
public void setTagInfo(TagInfo tagInfo) {
this.tagInfo = tagInfo;
}
/*
* Answers whether all characters up to the first new line are all whitespace.
* Answers false if the region does not contain a new line char.
*/
public boolean firstLineEntirelyWhitespace() {
int index = content.indexOf('\n');
if (index == -1) { return false; }
for (int i = 0; i < index; i++) {
if (!Character.isWhitespace(content.charAt(i))) {
return false;
}
}
return true;
}
/*
* Answers whether all characters after the last new line are all whitespace.
* Answers false if the region does not contain a new line char.
*/
public boolean lastLineEntirelyWhitespace() {
int index = content.lastIndexOf('\n');
if (index == -1) { return false; }
for (int i = index; i < content.length(); i++) {
if (!Character.isWhitespace(content.charAt(i))) {
return false;
}
}
return true;
}
public boolean isControlTag() {
return (tagInfo!=null) && (tagInfo.getTagDef()!= null) && (tagInfo.getTagDef().isControlTag());
}
/*
* Truncate trailing whitespace after the last new line char, but not the newline itself
*/
public void truncateTrailingWhitespace() {
int index = content.lastIndexOf('\n');
content = content.substring(0,index+1);
}
/*
* Truncate leading whitespace up to and including the first new line char
*/
public void truncateLeadingWhitespace() {
int index = content.indexOf('\n');
content = content.substring(index+1);
}
public String getErrorText() {
return errorText;
}
public void setErrorText(String errorText) {
this.errorText = errorText;
error = true;
}
public boolean isError() {
return error;
}
public static void validateNesting(SourceRegion region[]) {
Stack stack = new Stack();
for (SourceRegion sr: region) {
if (sr.isTag()) {
stack.push(sr);
} else if (sr.isEndTag()) {
SourceRegion current = stack.peek();
if (sr.getTagInfo().getTagName().equalsIgnoreCase(current.getTagInfo().getTagName())) {
stack.pop();
} else {
current.setErrorText("Missing close tag");
sr.setErrorText("Mismatched close tag");
}
} else if (sr.isEmptyTag()) {
} else if (sr.isText()) {
} else if (sr.isDirective()) {
} else if (sr.isComment()) {
}
}
while (!stack.isEmpty()) {
stack.pop().setErrorText("Missing close tag");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy