1 /* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.runtime.regexp; 27 28 import jdk.nashorn.internal.runtime.ParserException; 29 import jdk.nashorn.internal.runtime.regexp.joni.Matcher; 30 import jdk.nashorn.internal.runtime.regexp.joni.Option; 31 import jdk.nashorn.internal.runtime.regexp.joni.Regex; 32 import jdk.nashorn.internal.runtime.regexp.joni.Region; 33 import jdk.nashorn.internal.runtime.regexp.joni.Syntax; 34 import jdk.nashorn.internal.runtime.regexp.joni.exception.JOniException; 35 36 import java.util.regex.Pattern; 37 import java.util.regex.PatternSyntaxException; 38 39 /** 40 * Regular expression implementation based on the Joni engine from the JRuby project. 41 */ 42 public class JoniRegExp extends RegExp { 43 44 /** Compiled Joni Regex */ 45 private Regex regex; 46 47 /** Matcher */ 48 private RegExpMatcher matcher; 49 50 /** 51 * Construct a Regular expression from the given {@code pattern} and {@code flags} strings. 52 * 53 * @param pattern RegExp pattern string 54 * @param flags RegExp flag string 55 * @throws ParserException if flags is invalid or pattern string has syntax error. 56 */ 57 public JoniRegExp(final String pattern, final String flags) throws ParserException { 58 super(pattern, flags); 59 60 int option = Option.SINGLELINE; 61 62 if (this.isIgnoreCase()) { 63 option |= Option.IGNORECASE; 64 } 65 if (this.isMultiline()) { 66 option &= ~Option.SINGLELINE; 67 option |= Option.NEGATE_SINGLELINE; 68 } 69 70 try { 71 RegExpScanner parsed; 72 73 try { 74 parsed = RegExpScanner.scan(pattern); 75 } catch (final PatternSyntaxException e) { 76 // refine the exception with a better syntax error, if this 77 // passes, just rethrow what we have 78 Pattern.compile(pattern, 0); 79 throw e; 80 } 81 82 if (parsed != null) { 83 char[] javaPattern = parsed.getJavaPattern().toCharArray(); 84 this.regex = new Regex(javaPattern, 0, javaPattern.length, option, Syntax.JAVASCRIPT); 85 this.groupsInNegativeLookahead = parsed.getGroupsInNegativeLookahead(); 86 } 87 } catch (final PatternSyntaxException e2) { 88 throwParserException("syntax", e2.getMessage()); 89 } catch (JOniException e2) { 90 throwParserException("syntax", e2.getMessage()); 91 } 92 } 93 94 @Override 95 public RegExpMatcher match(final String input) { 96 if (regex == null) { 97 return null; 98 } 99 100 RegExpMatcher matcher = this.matcher; 101 102 if (matcher == null || input != matcher.getInput()) { 103 matcher = new JoniMatcher(input); 104 this.matcher = matcher; 105 } 106 107 return matcher; 108 } 109 110 /** 111 * RegExp Factory class for Joni regexp engine. 112 */ 113 public static class Factory extends RegExpFactory { 114 115 @Override 116 protected RegExp compile(final String pattern, final String flags) throws ParserException { 117 return new JoniRegExp(pattern, flags); 118 } 119 120 @Override 121 protected String replaceToken(final String str) { 122 return str.equals("[^]") ? "[\\s\\S]" : str; 123 } 124 } 125 126 class JoniMatcher implements RegExpMatcher { 127 final String input; 128 final Matcher matcher; 129 130 JoniMatcher(final String input) { 131 this.input = input; 132 this.matcher = regex.matcher(input.toCharArray()); 133 } 134 135 @Override 136 public boolean search(final int start) { 137 return matcher.search(start, input.length(), Option.NONE) > -1; 138 } 139 140 @Override 141 public String getInput() { 142 return input; 143 } 144 145 @Override 146 public int start() { 147 return matcher.getBegin(); 148 } 149 150 @Override 151 public int start(final int group) { 152 return group == 0 ? start() : matcher.getRegion().beg[group]; 153 } 154 155 @Override 156 public int end() { 157 return matcher.getEnd(); 158 } 159 160 @Override 161 public int end(final int group) { 162 return group == 0 ? end() : matcher.getRegion().end[group]; 163 } 164 165 @Override 166 public String group() { 167 return input.substring(matcher.getBegin(), matcher.getEnd()); 168 } 169 170 @Override 171 public String group(final int group) { 172 if (group == 0) { 173 return group(); 174 } 175 final Region region = matcher.getRegion(); 176 return input.substring(region.beg[group], region.end[group]); 177 } 178 179 @Override 180 public int groupCount() { 181 final Region region = matcher.getRegion(); 182 return region == null ? 0 : region.numRegs - 1; 183 } 184 } 185 }