package com.github.fabeclipse.textedgrep.internal.core; import java.util.ArrayList; import java.util.Formatter; import com.github.fabeclipse.textedgrep.IGrepContext; import com.github.fabeclipse.textedgrep.IGrepTarget; /** * @since 3.0 */ public class GrepContext implements IGrepContext { protected static class GrowingIntArray { private int chunkSize = 1; // to prevent /0 exceptions in getAt() private ArrayList<int[]> data = new ArrayList<int[]>(1000); public GrowingIntArray addChunk(int[] chunk) { if (data.size() == 0) chunkSize = chunk.length; else if (chunk.length != chunkSize) throw new IllegalArgumentException("wrong chunksize (" + chunk.length + "), should be " + chunkSize); data.add(chunk); return this; } public int getAt(int position) { return data.get(position / chunkSize)[position % chunkSize]; } public int size() { return data.size() * chunkSize; } } private final IGrepTarget target; private final StringBuilder grep; private final Formatter formatter; private int numberOfMatches; private int numGrepLines; private final GrowingIntArray lineMap = new GrowingIntArray(); private final GrowingIntArray matchBegin = new GrowingIntArray(); private final GrowingIntArray matchEnd = new GrowingIntArray(); private final GrowingIntArray matchMap = new GrowingIntArray(); private final GrowingIntArray colorMap = new GrowingIntArray(); private boolean linesDone; private boolean matchDone; public GrepContext(IGrepTarget target) { this.target = target; grep = new StringBuilder(); formatter = new Formatter(grep); } /* (non-Javadoc) * @see com.github.fabeclipse.textedgrep.IGrepContext#getOriginalLine(int) */ @Override public int getOriginalLine(int grepLine) { int originalLine = 0; // if the grep context is empty, it is // OK to ask for line 0 even if it // is beyond the array size if (numGrepLines > 0) originalLine = lineMap.getAt(grepLine); return originalLine; } /* (non-Javadoc) * @see com.github.fabeclipse.textedgrep.IGrepContext#getMatchBeginForGrepLine(int, int) */ @Override public int getMatchBeginForGrepLine(int grepLine, int index) { return matchBegin.getAt(matchMap.getAt(grepLine) + index); } /* (non-Javadoc) * @see com.github.fabeclipse.textedgrep.IGrepContext#getMatchEndForGrepLine(int, int) */ @Override public int getMatchEndForGrepLine(int grepLine, int index) { return matchEnd.getAt(matchMap.getAt(grepLine) + index); } /* (non-Javadoc) * @see com.github.fabeclipse.textedgrep.IGrepContext#getColorForGrepLine(int) */ @Override public int getColorForGrepLine(int grepLine) { return colorMap.getAt(grepLine); } /* (non-Javadoc) * @see com.github.fabeclipse.textedgrep.IGrepContext#getNumberOfMatches() */ @Override public int getNumberOfMatches() { return numberOfMatches; } /* (non-Javadoc) * @see com.github.fabeclipse.textedgrep.IGrepContext#getNumberOfMatchesForGrepLine(int) */ @Override public int getNumberOfMatchesForGrepLine(int grepLine) { int n; // a b c d e <- matchBegin/End array // ^ ^ ^ ^ // 0 1 2 3 <- matchMap index into above array // line 0 has 1 match // line 1 has 2 matches // line 2 has 1 match // number of matches for 1 is 3 - 1 = 2 if (grepLine + 1 < numGrepLines) n = matchMap.getAt(grepLine + 1) - matchMap.getAt(grepLine); else n = numberOfMatches - matchMap.getAt(grepLine); return n; } /* (non-Javadoc) * @see com.github.fabeclipse.textedgrep.IGrepContext#getMaxOriginalLine() */ @Override public int getMaxOriginalLine() { // if the grep context is empty // return 0 as max line if (numGrepLines > 0) return lineMap.getAt(numGrepLines - 1); else return 0; } /* (non-Javadoc) * @see com.github.fabeclipse.textedgrep.IGrepContext#getText() */ @Override public String getText() { return grep.toString(); } /* (non-Javadoc) * @see com.github.fabeclipse.textedgrep.IGrepContext#getTarget() */ @Override public IGrepTarget getTarget() { return target; } public void addLineChunks(int[] lineMapChunk, int[] matchMapChunk, int[] colorMapChunk) { addLineChunks(lineMapChunk, matchMapChunk, colorMapChunk, lineMapChunk.length); } public void addMatchChunks(int[] matchBeginChunk, int[] matchEndChunk) { addMatchChunks(matchBeginChunk, matchEndChunk, matchBeginChunk.length); } public void addMatchChunks(int[] matchBeginChunk, int[] matchEndChunk, int len) { if (len != matchBeginChunk.length) matchDone = true; else if (matchDone) throw new IllegalArgumentException("Incomplete match chunk already submitted."); numberOfMatches = matchBegin.size() + len; matchBegin.addChunk(matchBeginChunk); matchEnd.addChunk(matchEndChunk); } public void addLineChunks(int[] lineMapChunk, int[] matchMapChunk, int[] colorMapChunk, int len) { if (len != lineMapChunk.length) linesDone = true; else if (linesDone) throw new IllegalArgumentException("Incomplete line chunk already submitted."); numGrepLines = lineMap.size() + len; lineMap.addChunk(lineMapChunk); matchMap.addChunk(matchMapChunk); colorMap.addChunk(colorMapChunk); } public void addLine(String line) { formatter.format("%s\n", line); } public void seal() { // remove trailing newline, as the StyledText Document // returns one line more if the last line has a line terminator if (grep.length() > 0) { char lastChar = grep.charAt(grep.length() - 1); if (lastChar == '\n') { grep.deleteCharAt(grep.length() - 1); } } formatter.close(); linesDone = true; matchDone = true; } }