JoniRegExp.java revision 953:221a84ef44c0
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
26package jdk.nashorn.internal.runtime.regexp;
27
28import java.util.regex.Pattern;
29import java.util.regex.PatternSyntaxException;
30import jdk.nashorn.internal.runtime.ParserException;
31import jdk.nashorn.internal.runtime.regexp.joni.Matcher;
32import jdk.nashorn.internal.runtime.regexp.joni.Option;
33import jdk.nashorn.internal.runtime.regexp.joni.Regex;
34import jdk.nashorn.internal.runtime.regexp.joni.Region;
35import jdk.nashorn.internal.runtime.regexp.joni.Syntax;
36import jdk.nashorn.internal.runtime.regexp.joni.exception.JOniException;
37
38/**
39 * Regular expression implementation based on the Joni engine from the JRuby project.
40 */
41public class JoniRegExp extends RegExp {
42
43    /** Compiled Joni Regex */
44    private Regex regex;
45
46    /**
47     * Construct a Regular expression from the given {@code pattern} and {@code flags} strings.
48     *
49     * @param pattern RegExp pattern string
50     * @param flags RegExp flag string
51     * @throws ParserException if flags is invalid or pattern string has syntax error.
52     */
53    public JoniRegExp(final String pattern, final String flags) throws ParserException {
54        super(pattern, flags);
55
56        int option = Option.SINGLELINE;
57
58        if (this.isIgnoreCase()) {
59            option |= Option.IGNORECASE;
60        }
61        if (this.isMultiline()) {
62            option &= ~Option.SINGLELINE;
63            option |= Option.NEGATE_SINGLELINE;
64        }
65
66        try {
67            RegExpScanner parsed;
68
69            try {
70                parsed = RegExpScanner.scan(pattern);
71            } catch (final PatternSyntaxException e) {
72                // refine the exception with a better syntax error, if this
73                // passes, just rethrow what we have
74                Pattern.compile(pattern, 0);
75                throw e;
76            }
77
78            if (parsed != null) {
79                final char[] javaPattern = parsed.getJavaPattern().toCharArray();
80                this.regex = new Regex(javaPattern, 0, javaPattern.length, option, Syntax.JAVASCRIPT);
81                this.groupsInNegativeLookahead = parsed.getGroupsInNegativeLookahead();
82            }
83        } catch (final PatternSyntaxException | JOniException e2) {
84            throwParserException("syntax", e2.getMessage());
85        }
86    }
87
88    @Override
89    public RegExpMatcher match(final String input) {
90        if (regex == null) {
91            return null;
92        }
93
94        return new JoniMatcher(input);
95    }
96
97    /**
98     * RegExp Factory class for Joni regexp engine.
99     */
100    public static class Factory extends RegExpFactory {
101
102        @Override
103        public RegExp compile(final String pattern, final String flags) throws ParserException {
104            return new JoniRegExp(pattern, flags);
105        }
106
107    }
108
109    class JoniMatcher implements RegExpMatcher {
110        final String input;
111        final Matcher joniMatcher;
112
113        JoniMatcher(final String input) {
114            this.input = input;
115            this.joniMatcher = regex.matcher(input.toCharArray());
116        }
117
118        @Override
119        public boolean search(final int start) {
120            return joniMatcher.search(start, input.length(), Option.NONE) > -1;
121        }
122
123        @Override
124        public String getInput() {
125            return input;
126        }
127
128        @Override
129        public int start() {
130            return joniMatcher.getBegin();
131        }
132
133        @Override
134        public int start(final int group) {
135            return group == 0 ? start() : joniMatcher.getRegion().beg[group];
136        }
137
138        @Override
139        public int end() {
140            return joniMatcher.getEnd();
141        }
142
143        @Override
144        public int end(final int group) {
145            return group == 0 ? end() : joniMatcher.getRegion().end[group];
146        }
147
148        @Override
149        public String group() {
150            return input.substring(joniMatcher.getBegin(), joniMatcher.getEnd());
151        }
152
153        @Override
154        public String group(final int group) {
155            if (group == 0) {
156                return group();
157            }
158            final Region region = joniMatcher.getRegion();
159            return input.substring(region.beg[group], region.end[group]);
160        }
161
162        @Override
163        public int groupCount() {
164            final Region region = joniMatcher.getRegion();
165            return region == null ? 0 : region.numRegs - 1;
166        }
167    }
168}
169