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.parser; 27 28 import java.util.HashSet; 29 import java.util.regex.Pattern; 30 import java.util.regex.PatternSyntaxException; 31 import jdk.nashorn.internal.runtime.BitVector; 32 import jdk.nashorn.internal.runtime.ECMAErrors; 33 import jdk.nashorn.internal.runtime.ParserException; 34 35 import jdk.nashorn.internal.joni.Option; 36 import jdk.nashorn.internal.joni.Regex; 37 import jdk.nashorn.internal.joni.Syntax; 38 import jdk.nashorn.internal.joni.exception.JOniException; 39 40 /** 41 * This class is used to represent a parsed regular expression. Accepts input 42 * pattern string and flagString. This is used by AbstractParser to validate 43 * RegExp literals as well as by NativeRegExp to parse RegExp constructor arguments. 44 */ 45 public final class RegExp { 46 /** Pattern string. */ 47 private final String source; 48 49 /** Global search flag for this regexp.*/ 50 private boolean global; 51 52 /** Case insensitive flag for this regexp */ 53 private boolean ignoreCase; 54 55 /** Multi-line flag for this regexp */ 56 private boolean multiline; 57 58 /** Java regexp pattern to use for match. We compile to one of these */ 59 private Regex regex; 60 61 /** BitVector that keeps track of groups in negative lookahead */ 62 private BitVector groupsInNegativeLookahead; 63 64 /** 65 * Creates RegExpLiteral object from given input and flagString. 66 * 67 * @param source RegExp pattern string 68 * @param flagString RegExp flags 69 * @throws ParserException if flagString is invalid or input string has syntax error. 70 */ 71 public RegExp(final String source, final String flagString) throws ParserException { 72 this.source = source; 73 final HashSet<Character> usedFlags = new HashSet<>(); 74 int option = Option.SINGLELINE; 75 76 for (final char ch : flagString.toCharArray()) { 77 if (usedFlags.contains(ch)) { 78 throwParserException("repeated.flag", Character.toString(ch)); 79 } 80 81 switch (ch) { 82 case 'g': 83 this.global = true; 84 usedFlags.add(ch); 85 break; 86 case 'i': 87 this.ignoreCase = true; 88 option |= Option.IGNORECASE; 89 usedFlags.add(ch); 90 break; 91 case 'm': 92 this.multiline = true; 93 option &= ~Option.SINGLELINE; 94 option |= Option.NEGATE_SINGLELINE; 95 usedFlags.add(ch); 96 break; 97 default: 98 throwParserException("unsupported.flag", Character.toString(ch)); 99 } 100 } 101 102 try { 103 RegExpScanner parsed; 104 105 try { 106 parsed = RegExpScanner.scan(source); 107 } catch (final PatternSyntaxException e) { 108 // refine the exception with a better syntax error, if this 109 // passes, just rethrow what we have 110 Pattern.compile(source, 0); 111 throw e; 112 } 113 114 if (parsed != null) { 115 char[] javaPattern = parsed.getJavaPattern().toCharArray(); 116 this.regex = new Regex(javaPattern, 0, javaPattern.length, option, Syntax.JAVASCRIPT); 117 this.groupsInNegativeLookahead = parsed.getGroupsInNegativeLookahead(); 118 } 119 } catch (final PatternSyntaxException e2) { 120 throwParserException("syntax", e2.getMessage()); 121 } catch (JOniException e2) { 122 throwParserException("syntax", e2.getMessage()); 123 } 124 125 } 126 127 /** 128 * @return the source string 129 */ 130 public String getSource() { 131 return source; 132 } 133 134 /** 135 * @return the global 136 */ 137 public boolean isGlobal() { 138 return global; 139 } 140 141 /** 142 * @return the ignoreCase 143 */ 144 public boolean isIgnoreCase() { 145 return ignoreCase; 146 } 147 148 /** 149 * @return the multiline 150 */ 151 public boolean isMultiline() { 152 return multiline; 153 } 154 155 /** 156 * @return the pattern 157 */ 158 public Regex getRegex() { 159 return regex; 160 } 161 162 /** 163 * @return the groupsInNegativeLookahead 164 */ 165 public BitVector getGroupsInNegativeLookahead() { 166 return groupsInNegativeLookahead; 167 } 168 169 /** 170 * Validation method for RegExp input and flagString - we don't care about the RegExp object 171 * 172 * @param input regexp input 173 * @param flagString flag string 174 * 175 * @throws ParserException if invalid regexp and flags 176 */ 177 @SuppressWarnings({"unused", "ResultOfObjectAllocationIgnored"}) 178 public static void validate(final String input, final String flagString) throws ParserException { 179 new RegExp(input, flagString); 180 } 181 182 private static void throwParserException(final String key, final String str) throws ParserException { 183 throw new ParserException(ECMAErrors.getMessage("parser.error.regex." + key, str)); 184 } 185 } --- EOF ---