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