/* * codjo.net * * Common Apache License 2.0 */ package net.codjo.segmentation.gui.editor; import net.codjo.expression.help.FunctionHelp; import java.awt.Color; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import org.apache.log4j.Logger; import org.apache.regexp.RE; /** * Classe qui herite de DefaultStyledDocument et qui formate le style du text en fontion des �lements donn�es: * mot cl�s (ex : + , - = < ( ...), colonnes (ex : PortfolioCode ...), fontions (ex : iif substring ...), * String (ex : "rrr"), error (tout ce qui est autre) * * @author Lajmi */ class HighlightingStyledDocument extends DefaultStyledDocument { private static final Logger LOG = Logger.getLogger(HighlightingStyledDocument.class); /* style des operations et les autres mot cles */ //private SimpleAttributeSet operationStyle = new SimpleAttributeSet(); /* style des colonnes */ //private SimpleAttributeSet columnStyle = new SimpleAttributeSet(); private List<StringsStyle> stringsStyleList = new ArrayList<StringsStyle>(); /* style des fontions */ private SimpleAttributeSet functionStyle = new SimpleAttributeSet(); /* Style des String en erreur */ private SimpleAttributeSet stringStyle = new SimpleAttributeSet(); /* Style des digits */ private SimpleAttributeSet digitStyle = new SimpleAttributeSet(); /* Error Style */ private SimpleAttributeSet errorStyle = new SimpleAttributeSet(); private RE functionRegExp = new RE(""); private Map<String, FunctionHelp> functionMap = new HashMap<String, FunctionHelp>(); private UpdateHighlightRunnable postedUpdate = new UpdateHighlightRunnable(); HighlightingStyledDocument() { initDefaultAttributeSet(); } public SimpleAttributeSet getFunctionStyle() { return functionStyle; } public SimpleAttributeSet getStringStyle() { return stringStyle; } public SimpleAttributeSet getDigitStyle() { return digitStyle; } public SimpleAttributeSet getErrorStyle() { return errorStyle; } public void setFunctionStyle(SimpleAttributeSet functionStyle) { this.functionStyle = functionStyle; } public void setStringStyle(SimpleAttributeSet stringStyle) { this.stringStyle = stringStyle; } public void setDigitStyle(SimpleAttributeSet digitStyle) { this.digitStyle = digitStyle; } public void setErrorStyle(SimpleAttributeSet errorStyle) { this.errorStyle = errorStyle; } public void addStringsStyle(StringsStyle stringsStyle) { stringsStyleList.add(stringsStyle); } public void setColumns(Collection columns) { SimpleAttributeSet columnStyle = new SimpleAttributeSet(); setDefaultStyle(columnStyle); StyleConstants.setForeground(columnStyle, Color.blue); StyleConstants.setBackground(columnStyle, new Color(204, 204, 255, 70)); StringsStyle stringsStyle = new StringsStyle(columnStyle, columns); addStringsStyle(stringsStyle); } public void setOperations(Collection<OperatorHelper.OperatorHelp> operators) { List<String> operatorList = new ArrayList<String>(); for (OperatorHelper.OperatorHelp operator : operators) { operatorList.add(operator.toString()); } SimpleAttributeSet operationStyle = new SimpleAttributeSet(); setDefaultStyle(operationStyle); StyleConstants.setForeground(operationStyle, Color.black); StyleConstants.setBackground(operationStyle, Color.white); StringsStyle stringsStyle = new StringsStyle(operationStyle, operatorList); addStringsStyle(stringsStyle); } public void setFunctions(Collection functions) { List<String> functionNameList = new ArrayList<String>(); for (Object function1 : functions) { FunctionHelp function = (FunctionHelp)function1; functionNameList.add(function.getFunctionName()); functionMap.put(function.getFunctionName(), function); } functionRegExp = new RE(getRegularExpression(functionNameList)); } /** * getRegularExpression(List values) Pour une liste de valeur possible returne l'expression r�guli�re pour * rechercher l'un de ces mots. the method replace the meta-character <b>\ | ( ) [ { ^ $ + ? . < * ></b> \meta-carachter<p>Example: pour la liste : value1, value2 le retour est : * "value1|value2".</p> * * @param values la liste des valeurs possible <b>String</b>s * * @return L'expression r�guli�re */ String getRegularExpression(Collection values) { StringBuffer exp = new StringBuffer(""); int idx = 0; for (Object value1 : values) { String value = (String)value1; value = replaceMetaCaracters(value); if (idx == 0) { exp.append(value.trim()); } exp.append("|").append(value.trim()); idx++; } exp.append(""); return exp.toString(); } /** * the method replace the meta-character <b>\ | ( ) [ { ^ $ + ? . < ></b> \meta-carachter * * @param value * * @return metaCatarters replaced */ String replaceMetaCaracters(String value) { RE re = new RE("\\(|\\)|\\[|\\{|\\^|\\$|\\*|\\+|\\?|\\.|\\<|\\>|\\|"); re.match(value); StringBuffer sbf = new StringBuffer(value); re.match(sbf.toString()); int beginColSearch = re.getParenEnd(0); while (beginColSearch != -1) { // String theChar = re.getParen(0); sbf.insert(beginColSearch - 1, "\\"); re.match(sbf.toString(), beginColSearch + 1); beginColSearch = re.getParenEnd(0) != beginColSearch ? re.getParenEnd(0) : -1; } return sbf.toString(); } /** * insertString Overrides the default method from DefaultStyledDocument. Calls appropriate syntax * highlighting code and then class super. * * @param offs the starting offset >= 0 * @param str the string to insert; does nothing with null/empty strings * @param att the attributes for the inserted content * * @throws BadLocationException the given insert position is not a valid position within the document */ @Override public void insertString(int offs, String str, AttributeSet att) throws BadLocationException { super.insertString(offs, str, att); postUpdateHighlight(); } private void postUpdateHighlight() { postedUpdate.cancel(); postedUpdate = new UpdateHighlightRunnable(); SwingUtilities.invokeLater(postedUpdate); } /** * fireRemoveUpdate Overrides the default method from DefaultStyledDocument. Calls appropriate syntax * highlighting code and then class super. * * @param event the DocumentEvent */ @Override protected void fireRemoveUpdate(DocumentEvent event) { super.fireRemoveUpdate(event); postUpdateHighlight(); } /** * Method: updateHighlightingInRange */ public void updateHighlightingInRange() { try { int start = getStartPosition().getOffset(); int end = getEndPosition().getOffset(); String text = getText(start, end - start); if (text.length() == 0) { return; } setCharacterAttributes(start, end - start, errorStyle, true); //Highlighting functions // et V�rifie le nombre de parametre functionRegExp.match(text); int beginColSearch = functionRegExp.getParenEnd(0); while (beginColSearch != -1) { String functionNameFound = functionRegExp.getParen(0); FunctionHelp functionFound = functionMap.get(functionNameFound); if (functionFound.getParameterNumber() == -1 || doesParameterMatch(functionFound, text, beginColSearch)) { int parenStart = functionRegExp.getParenStart(0); setCharacterAttributes(parenStart, beginColSearch - parenStart, functionStyle, true); } functionRegExp.match(text, beginColSearch); beginColSearch = functionRegExp.getParenEnd(0); } for (StringsStyle stringsStyle : stringsStyleList) { highlightStandard(text, stringsStyle); } //Highlighting Strings RE stringMatcher = new RE("\"[^\n\"]*\""); stringMatcher.match(text); beginColSearch = stringMatcher.getParenEnd(0); while (beginColSearch != -1) { final int parenStart = stringMatcher.getParenStart(0); setCharacterAttributes(parenStart, beginColSearch - parenStart, stringStyle, true); stringMatcher.match(text, beginColSearch); int parenEnd = stringMatcher.getParenEnd(0); beginColSearch = (parenEnd != beginColSearch) ? parenEnd : -1; } } catch (Exception error) { LOG.error(error); } } private boolean doesParameterMatch(FunctionHelp functionFound, String text, int beginColSearch) { return functionFound.getParameterNumber() == getParamFounds(text, beginColSearch); } private void highlightStandard(String text, StringsStyle stringsStyle) { if (!stringsStyle.getStringStyle().isEmpty()) { RE regExpStyle = new RE(getRegularExpression(stringsStyle.getStringStyle())); int beginColSearch; regExpStyle.match(text); beginColSearch = regExpStyle.getParenEnd(0); while (beginColSearch != -1) { setCharacterAttributes(regExpStyle.getParenStart(0), beginColSearch - regExpStyle.getParenStart(0), stringsStyle.getAttributeStyle(), true); regExpStyle.match(text, beginColSearch); beginColSearch = regExpStyle.getParenEnd(0); } } } /** * returne le nombre de parametres utilis� par la m�thode * * @param text le text * @param beginColSearch l'index de fin de la methode * * @return int nombre de param dans les parenth�ses */ int getParamFounds(String text, int beginColSearch) { String textAfter = text.substring(beginColSearch, text.length()).trim(); if (!textAfter.startsWith("(")) { return -2; } RE params = new RE("\\(.*\\)"); params.match(textAfter); // si c'est pas les parenth�ses de ma m�thode en question if (0 != params.getParenStart(0)) { return -3; } StringTokenizer tokenizer = new StringTokenizer(params.getParen(0).substring(1, params.getParen(0).length() - 1), ","); int count = 0; while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (!(token.trim().length() == 0)) { count++; } } return count; } protected void initDefaultAttributeSet() { setDefaultStyle(errorStyle); StyleConstants.setItalic(errorStyle, true); StyleConstants.setForeground(errorStyle, Color.red); StyleConstants.setBackground(errorStyle, Color.white); setDefaultStyle(functionStyle); StyleConstants.setBold(functionStyle, true); StyleConstants.setForeground(functionStyle, new Color(153, 0, 51)); StyleConstants.setBackground(functionStyle, Color.white); setDefaultStyle(stringStyle); StyleConstants.setBold(stringStyle, true); StyleConstants.setForeground(stringStyle, new Color(51, 153, 0)); StyleConstants.setBackground(stringStyle, Color.white); } private void setDefaultStyle(SimpleAttributeSet attributeSet) { StyleConstants.setFontFamily(attributeSet, "Monospaced"); StyleConstants.setFontSize(attributeSet, 12); StyleConstants.setItalic(attributeSet, false); StyleConstants.setBold(attributeSet, false); } private class UpdateHighlightRunnable implements Runnable { private boolean canceled = false; public void cancel() { canceled = true; } public void run() { if (canceled) { return; } updateHighlightingInRange(); } } }