package templates.util; import static com.google.common.collect.Sets.newHashSet; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.antlr.runtime.ANTLRStringStream; import org.antlr.runtime.CommonToken; import org.antlr.runtime.Token; import org.eclipse.xtext.xdoc.generator.util.lexer.Common; import org.eclipse.xtext.xdoc.xdoc.Code; import org.eclipse.xtext.xdoc.xdoc.CodeBlock; import org.eclipse.xtext.xdoc.xdoc.LangDef; import org.eclipse.xtext.xdoc.xdoc.Link; import com.google.common.collect.Maps; public class StringFormatter { public static final String SL_COMMENT_DEFAULT = "//"; @SuppressWarnings("unused") static private HashMap<String, Set<String>> languages = new HashMap<String, Set<String>>(); static private Set<String> links = new HashSet<String>(); private static String slComment = "//"; private static String doubleQuote = "\""; private static String singleQuote = "'"; private static String mlCommentEnd = "*/"; private static String mlCommentStart = "/*"; static Map<String, Pattern> patterns; static { patterns = new HashMap<String, Pattern>(); patterns.put(slComment, makeHighlightRegionPattern(slComment)); patterns.put(mlCommentStart, makeHighlightRegionPattern(mlCommentStart)); patterns.put(mlCommentEnd, makeHighlightRegionPattern(mlCommentEnd)); patterns.put(singleQuote, makeHighlightRegionPattern(singleQuote)); patterns.put(doubleQuote, makeHighlightRegionPattern(doubleQuote)); patterns.put("\n", makeHighlightRegionPattern("\n")); } private static Pattern makeHighlightRegionPattern(String regionStarter) { return Pattern.compile("(?<![^\\\\]\\\\(\\\\\\\\){0," + (Short.MAX_VALUE) + "})\\Q" + regionStarter + "\\E"); } static public final CodeBlock removeIndent(CodeBlock cb) { if (cb.getContents().size() > 0 && cb.getContents().get(0) instanceof Code) { String code = ((Code) cb.getContents().get(0)).getContents(); int indent = code.length(); indent -= code.replaceAll("^(\n*)\\s*", "$1").length(); String string = "\n\\s{" + indent + "}"; for (int i = 0; i < cb.getContents().size(); i++) { if (cb.getContents().get(i) instanceof Code) { code = ((Code) cb.getContents().get(i)).getContents(); if (i == 0) { code = code.replaceAll("^\n*", "").replaceAll( "^\\s{" + indent + "}", ""); } if (i == cb.getContents().size() - 1) { code = code.replaceAll("(\\s|\n)*$", ""); } code = code.replaceAll(string, "\n"); ((Code) cb.getContents().get(i)).setContents(code); } } } return cb; } static public final CodeBlock removeXdocEscapeFromCode(CodeBlock cb){ if (cb.getContents().size() > 0 && cb.getContents().get(0) instanceof Code) { String code = ((Code) cb.getContents().get(0)).getContents(); code = unescapeXdocChars(code); for (int i = 0; i < cb.getContents().size(); i++) { ((Code) cb.getContents().get(i)).setContents(code); } } return cb; } static public final String unescapeXdocChars(String string){ return string.replaceAll("\\\\\\[", "[").replaceAll("\\\\\\]", "]"); } static public final String escapeChars(String string) { Map<String, String> toEscape = Maps.newHashMap(); toEscape.put("\\", "\\PYZbs{}"); toEscape.put("_", "\\PYZus{}"); toEscape.put("{", "\\PYZob{}"); toEscape.put("}", "\\PYZcb{}"); toEscape.put("^", "\\PYZca{}"); toEscape.put("&", "\\PYZam{}"); toEscape.put("<", "\\PYZlt{}"); toEscape.put(">", "\\PYZgt{}"); toEscape.put("#", "\\PYZsh{}"); toEscape.put("%", "\\PYZpc{}"); toEscape.put("$", "\\PYZdl{}"); toEscape.put("~", "\\PYZti{}"); StringBuffer result = new StringBuffer(); for (int i = 0; i < string.length(); i++) { char charAt = string.charAt(i); if (toEscape.containsKey("" + charAt)) { result.append(toEscape.get("" + charAt)); } else { result.append(charAt); } } return result.toString(); } static public final CodeBlock highlightCodeBlock(CodeBlock cb) { if (cb.getContents().size() > 0 && cb.getContents().get(0) instanceof Code) { LangDef lang = cb.getLanguage(); for (int i = 0; i < cb.getContents().size(); i++) { if (cb.getContents().get(i) instanceof Code) { String code = ((Code) cb.getContents().get(i)) .getContents(); Set<String> keywords = newHashSet(); if (lang != null && !lang.eIsProxy()) { keywords.addAll(lang.getKeywords()); } StringBuffer strBuffer = new StringBuffer(); Common lexer = new Common(); lexer.setCharStream(new ANTLRStringStream(code)); CommonToken t = (CommonToken) lexer.nextToken(); while (t.getType() != Token.EOF) { String text = t.getText(); text = escapeChars(text); switch (t.getType()) { case Common.SL_COMMENT: strBuffer.append(lineBreakAware(text, "cm")); break; case Common.ML_COMMENT: strBuffer.append(lineBreakAware(text, "cm")); break; case Common.COMMENT_RICH_TEXT_INBETWEEN: strBuffer.append(lineBreakAware(text, "st")); break; case Common.COMMENT_RICH_TEXT_END: strBuffer.append(lineBreakAware(text, "st")); break; case Common.ID: if (keywords.contains(text)) { strBuffer.append("\\PY{kw}{" + text + "}"); break; } else { strBuffer.append(text); break; } case Common.STRING: strBuffer.append("\\PY{st}{" + text + "}"); break; case Common.RICH_TEXT: strBuffer.append(lineBreakAware(text, "st")); break; case Common.RICH_TEXT_END: strBuffer.append(lineBreakAware(text, "st")); break; case Common.RICH_TEXT_START: strBuffer.append(lineBreakAware(text, "st")); break; case Common.RICH_TEXT_INBETWEEN: strBuffer.append(lineBreakAware(text, "st")); break; default: strBuffer.append(text); break; } t = (CommonToken) lexer.nextToken(); } ((Code) cb.getContents().get(i)).setContents(strBuffer.toString()); } } } return cb; } private static String lineBreakAware(String text,String signal) { String rest = text; StringBuilder result = new StringBuilder(); while(!rest.equals("")){ int lineBreak = rest.indexOf("\n"); int tab = rest.indexOf("\t"); if(lineBreak > 0){ String beginn = rest.substring(0,lineBreak); if(rest.length() > lineBreak + 1) rest = rest.substring(lineBreak + 1); else rest = ""; result.append(escapeString(beginn,signal) + " \n"); } else if(tab > 0){ String beginn = rest.substring(0,tab); if(rest.length() > lineBreak + 1) rest = rest.substring(lineBreak + 1); else rest = ""; result.append(escapeString(beginn, signal) + " \t"); } else { result.append(escapeString(rest, signal)); rest = ""; } } String string = result.toString(); return string; } /** * @param text * @return */ private static String escapeString(String text, String signal) { return "\\PY{" + signal + "}{" + text + "}"; } static public String encode(String s) { try { return URLEncoder.encode(s, "ISO-8859-1"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return ""; } } public static String urlDecode(String url) throws UnsupportedEncodingException { return URLDecoder.decode(url, "ISO-8859-1"); } static public void storeLink(Link link) { links.add(link.getUrl()); } static public Set<String> getStoredLinks() { return links; } /** * Highlight keywords in a text. * * @param text * a piece of source code * @param langDef * the language * @return the string with keywords highlighted */ static public String highlightKeywords(String text, final LangDef lang) { if (lang != null && text != null) { List<String> keywords = lang.getKeywords(); StringBuilder result = new StringBuilder(); String[] toks; do { toks = splitToNext(text); result.append(addHighlighting(keywords, toks[0])); switch (toks.length) { case 3: text = toks[2]; case 2: result.append(toks[1]); break; default: // do nothing ; } } while (toks.length == 3); return result.toString(); } return text; } public static String[] splitToNext(String input) { int slC = -1, mlCStart = -1, doubleQuoteStart = -1, singleQuoteStart = -1; Matcher matcher; if (mlCommentEnd != null) { matcher = patterns.get(mlCommentStart).matcher(input); if (matcher.find(0)) mlCStart = matcher.start(); } matcher = patterns.get(slComment).matcher(input); if (matcher.find(0)) slC = matcher.start(); matcher = patterns.get(doubleQuote).matcher(input); if (matcher.find(0)) doubleQuoteStart = matcher.start(); matcher = patterns.get(singleQuote).matcher(input); if (matcher.find(0)) singleQuoteStart = matcher.start(); int min = Integer.MAX_VALUE; if (slComment != "" && slC >= 0 && slC < min) { min = slC; } if (mlCommentStart != "" && mlCStart >= 0 && mlCStart < min) { min = mlCStart; } if (doubleQuoteStart >= 0 && doubleQuoteStart < min) { min = doubleQuoteStart; } if (singleQuoteStart >= 0 && singleQuoteStart < min) { min = singleQuoteStart; } String[] res; if (min < Integer.MAX_VALUE) { if (min == slC) { res = breakApart(input, slC, slComment.length(), "\n"); res[1] = "<span class=\"comment\" >" + res[1] + "</span>"; return res; } else if (min == mlCStart) { res = breakApart(input, min, mlCommentStart.length(), mlCommentEnd); res[1] = "<span class=\"comment\" >" + res[1] + "</span>"; return res; } else if (min == doubleQuoteStart) { res = breakApart(input, min, doubleQuote.length(), doubleQuote); res[1] = "<span class=\"string\" >" + res[1] + "</span>"; return res; } else if (min == singleQuoteStart) { res = breakApart(input, min, singleQuote.length(), singleQuote); res[1] = "<span class=\"string\" >" + res[1] + "</span>"; return res; } } return new String[] { input }; } public static String[] breakApart(String input, int startIndex, int len, String end) { String[] ret = { "", "", "" }; ret[0] = input.substring(0, startIndex); ret[1] = input.substring(startIndex); Matcher matcher = patterns.get(end).matcher(ret[1]); if (matcher.find(len)) { String rest = input.substring(startIndex); // split up and stuff ret[1] = rest.substring(0, matcher.end()); // rest.indexOf(end, len) // + end.length()); ret[2] = rest.substring(matcher.end()); // rest.indexOf(end, len) + // end.length()); return ret; } return new String[] { ret[0], ret[1] }; } private static String addHighlighting(List<String> keywords, String text) { for (String keyword : keywords) { if (keyword.trim().equals("class")) { text = text .replaceAll( "(?<!<span )" + makeKeywordRegex(keyword.trim()) + "(?!\\=\"keyword\">)", "<span class=\"keyword\">" + keyword.trim() + "</span>"); } else if (keyword.trim().equals("span")) { text = text .replaceAll("((?<!<)" + makeKeywordRegex(keyword.trim()) + "(?!class\\=\"keyword\">)|(?<!</)span(?!>))", "<span class=\"keyword\">" + keyword.trim() + "</span>"); } else if (keyword.trim().equals("keyword")) { text = text .replaceAll("(?<!<span class=\")" + makeKeywordRegex(keyword.trim()) + "(?!\">)", "<span class=\"keyword\">" + keyword.trim() + "</span>"); } else { text = text .replaceAll(makeKeywordRegex(keyword.trim()), "<span class=\"keyword\">" + keyword.trim() + "</span>"); } } return text; } public static String percentToFloat(String percent) { return (Float.parseFloat(percent.replaceAll("%", "")) / 100) + ""; } private static String makeKeywordRegex(String keyword) { return "(?<![\\w])" + keyword + "(?!\\w)"; } public static void clearStoredLinks() { links.clear(); } }