package org.jboss.windup.rules.files.condition.regex; import java.util.regex.MatchResult; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.github.rwitzel.streamflyer.core.AfterModification; import com.github.rwitzel.streamflyer.regex.MatchProcessor; import com.github.rwitzel.streamflyer.regex.MatchProcessorResult; import com.github.rwitzel.streamflyer.regex.OnStreamMatcher; import com.github.rwitzel.streamflyer.regex.RegexModifier; import com.github.rwitzel.streamflyer.regex.addons.util.DoNothingProcessor; import com.github.rwitzel.streamflyer.util.ModificationFactory; import com.github.rwitzel.streamflyer.util.statistics.LineColumnAwareModificationFactory; /** * Extends {@link RegexModifier} to support tracking the line number and column, and to fire {@link StreamRegexMatchedEvent} events when content is * matched by the regular expression. * * @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a> * */ public class StreamRegexMatcher extends RegexModifier { private final StreamRegexMatcherProcessor streamRegexMatcherProcessor; public static StreamRegexMatcher create(String regex, StreamRegexMatchListener listener) { StreamRegexMatcherProcessor streamRegexMatcherProcessor = new StreamRegexMatcherProcessor(listener); return new StreamRegexMatcher(regex, 0, streamRegexMatcherProcessor); } private StreamRegexMatcher(String regex, int flags, StreamRegexMatcherProcessor streamRegexMatcherProcessor) { super(regex, flags, streamRegexMatcherProcessor, 2048, 2048); this.streamRegexMatcherProcessor = streamRegexMatcherProcessor; } @Override public AfterModification modify(StringBuilder characterBuffer, int firstModifiableCharacterInBuffer, boolean endOfStreamHit) { this.streamRegexMatcherProcessor.setFirstCharIndex(firstModifiableCharacterInBuffer); return super.modify(characterBuffer, firstModifiableCharacterInBuffer, endOfStreamHit); } @Override protected void init(OnStreamMatcher matcher, MatchProcessor matchProcessor, int minimumLengthOfLookBehind, int newNumberOfChars) { super.init(matcher, matchProcessor, minimumLengthOfLookBehind, newNumberOfChars); ModificationFactory delegate = new ModificationFactory(minimumLengthOfLookBehind, newNumberOfChars); this.factory = new LineColumnAwareModificationFactory(delegate); this.matchProcessor = matchProcessor; this.matcher = matcher; this.newNumberOfChars = newNumberOfChars; ((StreamRegexMatcherProcessor) matchProcessor).setLineColumnAwareModificationFactory((LineColumnAwareModificationFactory) this.factory); } private static class StreamRegexMatcherProcessor extends DoNothingProcessor implements MatchProcessor { private int firstCharIndex; private final StreamRegexMatchListener listener; private LineColumnAwareModificationFactory lineColumnAwareModificationFactory; public StreamRegexMatcherProcessor(StreamRegexMatchListener listener) { this.listener = listener; } public void setLineColumnAwareModificationFactory(LineColumnAwareModificationFactory lineColumnAwareModificationFactory) { this.lineColumnAwareModificationFactory = lineColumnAwareModificationFactory; } public void setFirstCharIndex(int firstCharIndex) { this.firstCharIndex = firstCharIndex; } @Override public MatchProcessorResult process(StringBuilder characterBuffer, int firstModifiableCharacterInBuffer, MatchResult matchResult) { long unmatchedStartLine = lineColumnAwareModificationFactory.getCurrentLine(); long unmatchedStartColumn = lineColumnAwareModificationFactory.getCurrentColumn(); int unmatchedStart = firstCharIndex; int unmatchedEnd = matchResult.start(); String unmatched = characterBuffer.substring(unmatchedStart, unmatchedEnd); Matcher matcher = Pattern.compile("\r\n|\r|\n").matcher(unmatched); int numLines = 0; int endOfLastLineBreak = 0; while (matcher.find()) { numLines++; endOfLastLineBreak = matcher.end(); } long lineNumber = unmatchedStartLine + numLines; long columnNumber; if (numLines == 0) { columnNumber = unmatchedStartColumn + unmatched.length(); } else { columnNumber = unmatched.length() - endOfLastLineBreak; // length of last line in 'unmatched' } String matchText = matchResult.group(); StreamRegexMatchedEvent event = new StreamRegexMatchedEvent(matchText, lineNumber, columnNumber); listener.regexMatched(event); return super.process(characterBuffer, firstModifiableCharacterInBuffer, matchResult); } } }