/* * SmartDoc : Ultimate document format based on XML * Copyright (C) 1998-2004 ASAMI, Tomoharu (asami@xmlSmartdoc.org) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.xmlsmartdoc.SmartDoc.normalizer; import java.util.*; import org.apache.oro.text.perl.Perl5Util; // revised by asami import org.apache.oro.text.regex.MatchResult; import com.AsamiOffice.text.UString; import org.xmlsmartdoc.SmartDoc.*; import org.xmlsmartdoc.SmartDoc.normalizer.hilight.*; /** * HilightMaker * * @since Jul. 16, 2000 * @version Jan. 25, 2004 * @author SAKURAI, Masashi (m.sakurai@dream.com) */ public class HilightMaker { private List keywordList; private Perl5Util perl5Util = new Perl5Util(); private TreeSet keywordTree = new TreeSet(); private ArrayList tempList = new ArrayList(); /** Construct hilight class. Once construct this object, other routine can re-use to call [makeHilight] method. @param keywords keyword matrix [group][keywords] @param isRegex is each group Regex? [group] @param keyClasses class name in charge of tag [group] */ public HilightMaker(String [][] keywords,boolean [] isRegex, String [] keyClasses) { keywordList = new LinkedList(); for (int i=0;i<keyClasses.length;i++) { for (int j=0;j<keywords[i].length;j++) { Keyword m = new Keyword(keywords[i][j],isRegex[i], keyClasses[i]); keywordList.add(m); } } } /** Construct hilight class from hilight data. Once construct this object, other routine can re-use to call [makeHilight] method. @param syntax hilight syntax data @see org.xmlsmartdoc.SmartDoc.normalizer.hilight.Syntax */ public HilightMaker(Syntax syntax) { keywordList = new LinkedList(); for (int i=0;i<syntax.getWordCount();i++) { Word word = syntax.getWord(i); if (word.getRegex()) { for (int j=0;j<word.getExpressionCount();j++) { keywordList.add( new Keyword (word.getExpression(j), true,word.getTagClass(), word.getCssClass()) ); } } else { for (int j=0;j<word.getExpressionCount();j++) { String [] tokens = UString.getTokens(word.getExpression(j),","); for (int k=0;k<tokens.length;k++) { keywordList.add( new Keyword (tokens[k],false,word.getTagClass(), word.getCssClass()) ); } } } } } /** make syntax hilighting @param text input plain text @param list operated text list */ public void makeHilight(String text,List list) { //ready keywordTree.clear(); Iterator it = keywordList.iterator(); while(it.hasNext()) { Keyword key = (Keyword)it.next(); if (search(perl5Util,text,key)) { keywordTree.add(key); } } while( !keywordTree.isEmpty() ) { Keyword res = (Keyword)keywordTree.first(); if (res.ps == -1) break;//error? if (res.ps > 0) { list.add(new CharBlock(text.substring(0, res.ps))); } list.add(res.getContent()); if (res.end <= text.length()) { text = text.substring(res.end); } else { System.err.println("Warning : lost a text position. (HilightMaker)"); break;//error? } //update tree tempList.clear(); int slideLength = res.end;//slide length Iterator it2 = keywordTree.iterator(); while(it2.hasNext()) { Keyword key = (Keyword)it2.next(); if (key.ps < slideLength || res == key) { it2.remove(); if (search(perl5Util,text,key)) { tempList.add(key);//next match } continue; } key.ps -= slideLength; key.end -= slideLength; } keywordTree.addAll(tempList);//merge with tree } list.add(new CharBlock(text)); } /** @param util Perl5Util @param text source text @param keywordInfo keyword info set */ protected boolean search(Perl5Util util,String text,Keyword keywordInfo) { if (keywordInfo.regexSwitch) { return searchByRegex(util,text,keywordInfo); } else { return searchByIndex(text,keywordInfo); } } private boolean searchByIndex(String text,Keyword keywordInfo) { try { int length = keywordInfo.keyword.length(); int curPs = -length; while (true) { curPs = text.indexOf(keywordInfo.keyword,curPs+length); if (curPs == -1) return false; if (curPs > 0 && Character.isJavaIdentifierPart(text.charAt(curPs-1))) continue; if (curPs < (text.length()-1) && Character.isJavaIdentifierPart(text.charAt(curPs + length))) continue; break; } keywordInfo.ps = curPs; keywordInfo.end = curPs + length; keywordInfo.matchedWord = keywordInfo.keyword; return true; } catch (RuntimeException e) { System.err.println("E:"+keywordInfo.keyword+" ("+ keywordInfo.matchedWord+")"); e.printStackTrace(); } return false; } private boolean searchByRegex(Perl5Util perl5Util,String text, Keyword keywordInfo) { try { if (perl5Util.match("#" + keywordInfo.keyword + "#m", text)) { keywordInfo.matchedWord = perl5Util.toString(); MatchResult mr = perl5Util.getMatch(); keywordInfo.ps = mr.beginOffset(0); keywordInfo.end = mr.endOffset(0); return true; } } catch (RuntimeException e) { System.err.println("E:"+keywordInfo.keyword+" ("+ keywordInfo.matchedWord+")"); e.printStackTrace(); } return false; } private class Keyword implements Comparable { private int ps=-1; //matched start position private int end=-1;//matched end position private boolean regexSwitch = false;//if use regex to search the word, true private String keyClass=null;//keyword class private String keyCSSClass=null;//keyword css class private String keyword=null;//keyword string private String matchedWord=null;//matched string private Keyword(String s,boolean m,String t) { regexSwitch = m; keyword = s; int index = t.indexOf("#"); if (index == -1) { keyClass = t; } else { keyClass = t.substring(0, index); keyCSSClass = t.substring(index + 1); } } private Keyword(String s,boolean m,String t,String c) { regexSwitch = m; keyword = s; keyClass = t; keyCSSClass = c; } private Content getContent() { try { if (keyword != null) { Container ct = (Container)(Class.forName(keyClass).newInstance()); if (keyCSSClass != null) { ct.setClazz(keyCSSClass); } ct.addContent(new CharBlock(matchedWord)); return ct; } } catch(Exception e) { throw new RuntimeException("Class "+keyClass+" not found."); } return new CharBlock(matchedWord); } public int compareTo(Object o) { if (o instanceof Keyword) { Keyword t = (Keyword)o; return ps - t.ps; } throw new InternalError(); } } }