/******************************************************************************* * Copyright (c) 2007 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.common.el.core.parser; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import org.jboss.tools.common.el.internal.core.parser.rule.BasicStates; /** * * @author V. Kabanovich * */ public class Tokenizer { public static int LITERAL = -10; private Map<Integer, ITokenDescription> tokenDescriptions = new HashMap<Integer, ITokenDescription>(); private Map<Integer, List<IRule>> rules = new HashMap<Integer, List<IRule>>(); private String sourceString; private int index = 0; private LexicalToken start; private LexicalToken last; private int state; private Properties context = new Properties(); List<SyntaxError> errors = new ArrayList<SyntaxError>(); public Tokenizer() {} /** * Prepares this tokenizer to be reused with the same token descriptions and rules. * */ public void dispose() { sourceString = null; index = 0; start = null; last = null; context.clear(); errors.clear(); } public void setTokenDescriptions(ITokenDescription[] ds) { tokenDescriptions.clear(); for (int i = 0; i < ds.length; i++) { int type = ds[i].getType(); if(tokenDescriptions.containsKey(type)) { throw new IllegalArgumentException("Token type " + type + " is not unique."); //$NON-NLS-1$ //$NON-NLS-2$ } tokenDescriptions.put(type, ds[i]); } } public ITokenDescription getTokenDescription(int id) { return tokenDescriptions.get(id); } public void setRules(IRule[] rules) { this.rules.clear(); for (int i = 0; i < rules.length; i++) { int[] ss = rules[i].getStartStates(); for (int j = 0; j < ss.length; j++) { List<IRule> rs = this.rules.get(ss[j]); if(rs == null) { rs = new ArrayList<IRule>(); this.rules.put(ss[j], rs); } rs.add(rules[i]); } } } public LexicalToken parse(String sourceString) { return parse(sourceString, 0, sourceString.length()); } public LexicalToken parse(String sourceString, int initialOffset, int length) { this.sourceString = sourceString; errors.clear(); index = initialOffset; start = new LexicalToken(initialOffset, 0, "", -1000); //$NON-NLS-1$ last = start; state = BasicStates.STATE_EXPECTING_EL; int lastIndex = initialOffset + length; if(lastIndex > sourceString.length()) lastIndex = sourceString.length(); while(index < lastIndex) { boolean done = false; List<IRule> rs = rules.get(state); for (IRule rule : rs) { int[] ts = rule.getTokenTypes(state); for (int j = 0; !done && j < ts.length; j++) { ITokenDescription td = tokenDescriptions.get(ts[j]); if (td != null && td.isStart(this, index)) { td.read(this, index); state = rule.getFinalState(state, ts[j]); done = true; } } } if(!done) { if(state == BasicStates.STATE_EXPECTING_EL || state == BasicStates.STATE_ERROR) { char ch = readNextChar(); if(ch == '\0') break; } else { SyntaxError error = new SyntaxError(index, state); String problem = null; for (IRule rule : rs) { problem = rule.getProblem(state, this); if(problem != null) break; } error.setProblem(problem); errors.add(error); state = BasicStates.STATE_ERROR; } } } LexicalToken result = start.getNextToken(); if(result != null) { result.makeItFirst(); } if(last != null && last.getStart() + last.getLength() < sourceString.length() && last.getType() != LITERAL) { int lastEnd = last.getStart() + last.getLength(); LexicalToken t = new LexicalToken(lastEnd, length, getCharSequence(lastEnd, sourceString.length()), LITERAL); last.setNextToken(t); last = t; } return result; } private static List<SyntaxError> EMPTY = new ArrayList<SyntaxError>(); /** * Copies errors in order to reuse this object. * @return */ public List<SyntaxError> getErrors() { if(errors.size() == 0) return EMPTY; List<SyntaxError> copy = new ArrayList<SyntaxError>(); copy.addAll(errors); return copy; } public void addSyntaxError(SyntaxError error) { errors.add(error); } public void addToken(int type, int start, int end) { if(end < 0) return; int lastEnd = last.getStart() + last.getLength(); if(start > lastEnd) { int length = start - lastEnd; LexicalToken t = new LexicalToken(lastEnd, length, getCharSequence(lastEnd, start), LITERAL); last.setNextToken(t); last = t; index = start; } LexicalToken t = new LexicalToken(start, end - start, getCharSequence(start, end), type); last.setNextToken(t); last = t; index = end; } public Properties getContext() { return context; } private CharSequence getCharSequence(int start, int end) { String text = sourceString.substring(start, end); return text.subSequence(0, text.length()); } public char readNextChar() { char c = '\0'; if (index < sourceString.length()) { c = sourceString.charAt(index); } index++; return c; } public char lookUpChar(int i) { return (i < 0 || sourceString.length() <= i) ? '\0' : sourceString.charAt(i); } /* * returns the character to the document */ public void releaseChar() { if (index > 0) { index--; } } public boolean startsWith(String s) { int l = index + s.length(); if(l > sourceString.length()) return false; for (int i = index; i < l; i++) { if(lookUpChar(i) != s.charAt(i - index)) { return false; } } return true; } public LexicalToken getLastToken() { return last; } public int getState() { return state; } public int getCurrentIndex() { return index; } }