package org.eclipse.dltk.ruby.internal.parser; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.dltk.ast.ASTNode; import org.eclipse.dltk.ast.ASTVisitor; import org.eclipse.dltk.ast.declarations.Declaration; import org.eclipse.dltk.ast.declarations.ModuleDeclaration; import org.eclipse.dltk.ruby.core.RubyPlugin; public class RubySourceFixer { private static final Pattern DOT_FIXER = Pattern .compile("\\.(?=[\\s,\\)\\]\\}]|$)"); //$NON-NLS-1$ private static final Pattern DOLLAR_FIXER = Pattern .compile("\\$(?=[\\s,\\)\\]\\}]|$)"); //$NON-NLS-1$ private static final Pattern AT_FIXER = Pattern .compile("@(?=[\\s,\\)\\]\\}]|$)"); //$NON-NLS-1$ private static final Pattern COLON_FIXER1 = Pattern .compile("::(?=[\\s,\\)\\]\\}]|$)"); //$NON-NLS-1$ private static final Pattern COLON_FIXER2 = Pattern.compile( "(?:=>.*,[\\s]*)(:)(?=[\\s]*(?=[,}\\)]))", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COLON_FIXER3 = Pattern.compile( ":(?=[\\s]*(?=[,}\\)]))", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COLON_FIXER_UNSAFE1 = Pattern.compile( "(?:=>.*,[\\s\\\\]*)(:)(?=[\\s]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COLON_FIXER_UNSAFE2 = Pattern.compile( ":(?=[\\s]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern INST_BRACK_FIXER = Pattern.compile("@(])"); //$NON-NLS-1$ private static final Pattern GLOB_BRACK_FIXER = Pattern.compile("\\$(])"); //$NON-NLS-1$ private static final Pattern COMMA_FIXER1 = Pattern.compile( "(?:=>.*)(,)(?=[\\s)]*do)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER2 = Pattern.compile( ",(?=[\\s)]*do)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER3A = Pattern.compile( "(?:=>.*,[^=>\r\n]*'[^']*)(')(?=[\\s)]*do)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER3B = Pattern.compile( "(?:=>.*,[^=>\r\n]*\"[^\"]*)(\")(?=[\\s)]*do)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER3C = Pattern .compile( "(?:=>.*,[^=>\r\n]*)([\\:a-zA-Z0-9_!?])(?=[\\s)]*do)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER4 = Pattern.compile( "(?:=>.*)(,)(?=[\\s]*[)][\\s]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER5 = Pattern.compile( ",(?=[\\s]*[)][\\s]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER6A = Pattern .compile( "(?:=>.*,[^=>\r\n]*'[^']*)(')(?=[\\s]*[)][\\s]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER6B = Pattern .compile( "(?:=>.*,[^=>\r\n]*\"[^\"]*)(\")(?=[\\s]*[)][\\s]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER6C = Pattern .compile( "(?:=>.*,[^=>\r\n]*)([\\:a-zA-Z0-9_!?])(?=[\\s]*[)][\\s]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER_UNSAFE1A = Pattern .compile( "(?:^[\\s]*[^\\s(\\:]+[\\s]*'[^']*'[\\s]*=>.*)(,)(?=[\\s)]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER_UNSAFE1B = Pattern .compile( "(?:^[\\s]*[^\\s(\\:]+[\\s]*\"[^\"]*\"[\\s]*=>.*)(,)(?=[\\s)]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER_UNSAFE1C = Pattern .compile( "(?:^[\\s]*[^\\s(\\:]+[\\s]*[\\:a-zA-Z0-9_!?]+[\\s]*=>.*)(,)(?=[\\s)]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER_UNSAFE2A = Pattern .compile( "(?:^[\\s]*[^\\s(\\:]+[\\s]*'[^']*'[\\s]*)(,)(?=[\\s)]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER_UNSAFE2B = Pattern .compile( "(?:^[\\s]*[^\\s(\\:]+[\\s]*\"[^\"]*\"[\\s]*)(,)(?=[\\s)]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER_UNSAFE2C = Pattern .compile( "(?:^[\\s]*[^\\s(\\:]+[\\s]*[\\:a-zA-Z0-9_!?]*[\\s]*)(,)(?=[\\s)]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER_UNSAFE3A = Pattern.compile( "(?:=>.*,[^=>\r\n]*'[^']*)(')(?=[\\s)]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER_UNSAFE3B = Pattern.compile( "(?:=>.*,[^=>\r\n]*\"[^\"]*)(\")(?=[\\s)]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern COMMA_FIXER_UNSAFE3C = Pattern .compile( "(?:=>.*,[^=>\r\n]*)([\\:a-zA-Z0-9_!?])(?=[\\s)]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern HASH_FIXER1 = Pattern.compile( "=>(?=[\\s)]*do)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern HASH_FIXER2 = Pattern.compile( "=>(?=[\\s]*[,}\\)])", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern HASH_FIXER_UNSAFE1 = Pattern.compile( "=>(?=[\\s)]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern HASH_FIXER_UNSAFE2A = Pattern .compile( "(?:^[\\s]*)(\\s)(?:'[^']*'[\\s]*=>.*[^\\s,)]+[\\s]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern HASH_FIXER_UNSAFE2B = Pattern .compile( "(?:^[\\s]*)(\\s)(?:\"[^\"]*\"[\\s]*=>.*[^\\s,)]+[\\s]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final Pattern HASH_FIXER_UNSAFE2C = Pattern .compile( "(?:^[\\s]*)(\\s)(?:[\\:a-zA-Z0-9_]+[\\s]*=>.*[^\\s,)]+[\\s]*$)", Pattern.MULTILINE); //$NON-NLS-1$ private static final String missingKey = "_m_key_"; //$NON-NLS-1$ private static final String missingValue = "_m_value__"; //$NON-NLS-1$ private static final String missingName = "_missing_method_name_"; //$NON-NLS-1$ private static final String missingName2 = "NoConstant___________"; //$NON-NLS-1$ private static final String missingName3 = "_missing_param_name__"; //$NON-NLS-1$ private static final String missingName4 = missingKey + " => " + missingValue; //$NON-NLS-1$ private static final String missingName4a = " => " + missingValue; //$NON-NLS-1$ private static final int magicLength = missingName.length(); // missingName.len should == missingName2.len, etc private final class ASTPositionsCorrector extends ASTVisitor { @Override public boolean visitGeneral(ASTNode node) throws Exception { if (node.sourceStart() < 0 || node.sourceEnd() < 0) return true; int st = 0; int en = 0; int n_st = 0; int n_en = 0; for (Iterator<Integer> iterator = fixPositions.iterator(); iterator .hasNext();) { Integer pos = iterator.next(); int fixPos = pos.intValue(); // starts if (node.sourceStart() > fixPos) { st++; } if (node.sourceEnd() > fixPos) { en++; } if (node instanceof Declaration) { Declaration declaration = (Declaration) node; if (declaration.getNameStart() > fixPos) { n_st++; } if (declaration.getNameEnd() > fixPos) { n_en++; } } } node.setStart(node.sourceStart() - st * magicLength); node.setEnd(node.sourceEnd() - en * magicLength); if (node instanceof Declaration) { Declaration declaration = (Declaration) node; declaration.setNameStart(declaration.getNameStart() - n_st * magicLength); declaration.setNameEnd(declaration.getNameEnd() - n_en * magicLength); } // if (st == 0 && en == 0 && n_st == 0 && n_en == 0) // return false; return true; } } public static Set<String> FIXUP_NAMES = new HashSet<String>(); { FIXUP_NAMES.add(missingKey); FIXUP_NAMES.add(missingValue); FIXUP_NAMES.add(missingName); FIXUP_NAMES.add(missingName2); FIXUP_NAMES.add(missingName3); FIXUP_NAMES.add(missingName4); FIXUP_NAMES.add(missingName4a); } private final List<Integer> fixPositions = new ArrayList<Integer>(); private String fixBrokenThings(Pattern pattern, String content, String replacement, int delta) { Matcher matcher = pattern.matcher(content); StringBuffer result = new StringBuffer(); int regionStart = 0; while (matcher.find(regionStart)) { int offset = matcher.start(matcher.groupCount()); if (offset > regionStart) result.append(content.subSequence(regionStart, offset)); fixPositions.add(new Integer(result.length())); result.append(replacement); // fixPositions.add(new Integer(offset + fixPositions.size() * // magicLength)); regionStart = offset + delta; // 2 } if (regionStart < content.length() - 1) result.append(content.subSequence(regionStart, content.length())); if (regionStart == 0) return content; // nothing fixed else return result.toString(); } private String fixBrokenDots(String content) { return fixBrokenThings(DOT_FIXER, content, "." + missingName, 1); //$NON-NLS-1$ } private String fixBrokenColons(String content) { String content2 = fixBrokenThings(COLON_FIXER1, content, "::" + missingName2, 2); //$NON-NLS-1$ content2 = fixBrokenThings(COLON_FIXER2, content2, ":" + missingName4, 1); //$NON-NLS-1$ return fixBrokenThings(COLON_FIXER3, content2, ":" + missingName2, 1); //$NON-NLS-1$ } private String fixBrokenColonsUnsafe(String content) { String content2 = fixBrokenThings(COLON_FIXER_UNSAFE1, content, ":" + missingName4, 1); //$NON-NLS-1$ return fixBrokenThings(COLON_FIXER_UNSAFE2, content2, ":" + missingName2, 1); //$NON-NLS-1$ } private String fixBrokenDollars(String content) { return fixBrokenThings(DOLLAR_FIXER, content, "$" + missingName, 1); //$NON-NLS-1$ } private String fixBrokenAts(String content) { return fixBrokenThings(AT_FIXER, content, "@" + missingName, 1); //$NON-NLS-1$ } private String fixBrokenInstbracks(String content) { return fixBrokenThings(INST_BRACK_FIXER, content, "@" + missingName, 1); //$NON-NLS-1$ } private String fixBrokenGlobbracks(String content) { return fixBrokenThings(GLOB_BRACK_FIXER, content, "$" + missingName, 1); //$NON-NLS-1$ } private String fixBrokenUnmatched(String content) { char[] contents = content.toCharArray(); StringBuffer buffer = new StringBuffer(contents.length); int parenDepth = 0; int bracketDepth = 0; int braceDepth = 0; int start = contents.length; for (int cnt = (start - 1); cnt >= 0; cnt--) { if (contents[cnt] == '(') { parenDepth--; if (parenDepth < 0) { int eol = cnt; for (int cnt2 = cnt; cnt2 < start; cnt2++) { if ((contents[cnt2] == '\r') || (contents[cnt2] == '\n')) { eol = cnt2; break; } } buffer.insert(0, contents, eol, (start - eol)); buffer.append(')'); buffer.insert(0, contents, cnt, (eol - cnt)); start = cnt; parenDepth = 0; } } else if (contents[cnt] == '[') { bracketDepth--; if (bracketDepth < 0) { int eol = cnt; for (int cnt2 = cnt; cnt2 < start; cnt2++) { if ((contents[cnt2] == '\r') || (contents[cnt2] == '\n')) { eol = cnt2; break; } } buffer.insert(0, contents, eol, (start - eol)); buffer.append(']'); buffer.insert(0, contents, cnt, (eol - cnt)); start = cnt; bracketDepth = 0; } } else if (contents[cnt] == '{') { braceDepth--; if (braceDepth < 0) { int eol = cnt; for (int cnt2 = cnt; cnt2 < start; cnt2++) { if ((contents[cnt2] == '\r') || (contents[cnt2] == '\n')) { eol = cnt2; break; } } buffer.insert(0, contents, eol, (start - eol)); buffer.append('}'); buffer.insert(0, contents, cnt, (eol - cnt)); start = cnt; braceDepth = 0; } } else if (contents[cnt] == ')') { parenDepth++; } else if (contents[cnt] == ']') { bracketDepth++; } else if (contents[cnt] == '}') { braceDepth++; } } if (start > 0) { buffer.insert(0, contents, 0, start); } return buffer.toString(); } private String fixBrokenCommas(String content) { String content2 = content; content2 = fixBrokenThings(COMMA_FIXER1, content2, "," + missingName4 + " ", 1); //$NON-NLS-1$ //$NON-NLS-2$ content2 = fixBrokenThings(COMMA_FIXER2, content2, "," + missingName3 + " ", 1); //$NON-NLS-1$ //$NON-NLS-2$ content2 = fixBrokenThings(COMMA_FIXER3A, content2, "'" + missingName4a, 1); //$NON-NLS-1$ content2 = fixBrokenThings(COMMA_FIXER3B, content2, "\"" + missingName4a, 1); //$NON-NLS-1$ content2 = fixBrokenThings(COMMA_FIXER3C, content2, missingName4 + " ", 1); //$NON-NLS-1$ content2 = fixBrokenThings(COMMA_FIXER4, content2, "," + missingName4, 1); //$NON-NLS-1$ content2 = fixBrokenThings(COMMA_FIXER5, content2, "," + missingName3, 1); //$NON-NLS-1$ content2 = fixBrokenThings(COMMA_FIXER6A, content2, "'" + missingName4a, 1); //$NON-NLS-1$ content2 = fixBrokenThings(COMMA_FIXER6B, content2, "\"" + missingName4a, 1); //$NON-NLS-1$ return fixBrokenThings(COMMA_FIXER6C, content2, missingName4 + " ", 1); //$NON-NLS-1$ } private String fixBrokenCommasUnsafe(String content) { String content2 = content; content2 = fixBrokenThings(COMMA_FIXER_UNSAFE1A, content2, "," + missingName4, 1); //$NON-NLS-1$ content2 = fixBrokenThings(COMMA_FIXER_UNSAFE1B, content2, "," + missingName4, 1); //$NON-NLS-1$ content2 = fixBrokenThings(COMMA_FIXER_UNSAFE1C, content2, "," + missingName4, 1); //$NON-NLS-1$ content2 = fixBrokenThings(COMMA_FIXER_UNSAFE2A, content2, "," + missingName3, 1); //$NON-NLS-1$ content2 = fixBrokenThings(COMMA_FIXER_UNSAFE2B, content2, "," + missingName3, 1); //$NON-NLS-1$ content2 = fixBrokenThings(COMMA_FIXER_UNSAFE2C, content2, "," + missingName3, 1); //$NON-NLS-1$ content2 = fixBrokenThings(COMMA_FIXER_UNSAFE3A, content2, "'" + missingName4a, 1); //$NON-NLS-1$ content2 = fixBrokenThings(COMMA_FIXER_UNSAFE3B, content2, "\"" + missingName4a, 1); //$NON-NLS-1$ return fixBrokenThings(COMMA_FIXER_UNSAFE3C, content2, missingName4 + " ", 1); //$NON-NLS-1$ } private String fixBrokenHashes(String content) { String content2 = content; content2 = fixBrokenThings(HASH_FIXER1, content2, "=>" + missingName3 + " ", 2); //$NON-NLS-1$ //$NON-NLS-2$ return fixBrokenThings(HASH_FIXER2, content2, "=>" + missingName3, 2); //$NON-NLS-1$ } private String fixBrokenHashesUnsafe(String content) { String content2 = content; content2 = fixBrokenThings(HASH_FIXER_UNSAFE1, content2, "=>" + missingName3, 2); //$NON-NLS-1$ content2 = fixBrokenThings(HASH_FIXER_UNSAFE2A, content2, " " + missingName + " ", 1); //$NON-NLS-1$ //$NON-NLS-2$ content2 = fixBrokenThings(HASH_FIXER_UNSAFE2B, content2, " " + missingName + " ", 1); //$NON-NLS-1$ //$NON-NLS-2$ return fixBrokenThings(HASH_FIXER_UNSAFE2C, content2, " " + missingName + " ", 1); //$NON-NLS-1$ //$NON-NLS-2$ } public String fix1(String content) { String content2 = fixBrokenDots(content); content2 = fixBrokenColons(content2); content2 = fixBrokenDollars(content2); content2 = fixBrokenAts(content2); content2 = fixBrokenInstbracks(content2); content2 = fixBrokenGlobbracks(content2); content2 = fixBrokenUnmatched(content2); content2 = fixBrokenCommas(content2); content2 = fixBrokenHashes(content2); return content2; } public String fixUnsafe1(String content) { return fixBrokenColonsUnsafe(content); } public String fixUnsafe2(String content) { content = fixBrokenCommasUnsafe(content); content = fixBrokenHashesUnsafe(content); return content; } public void correctPositionsIfNeeded(ModuleDeclaration module) { if (!fixPositions.isEmpty()) { try { module.traverse(new ASTPositionsCorrector()); } catch (Exception e) { RubyPlugin.log(e); } } } public void clearPositions() { fixPositions.clear(); } }