All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jruby.javasupport.ext.JavaUtilRegex Maven / Gradle / Ivy

There is a newer version: 9.4.12.0
Show newest version
/***** BEGIN LICENSE BLOCK *****
 * Version: EPL 1.0/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Eclipse Public
 * License Version 1.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.eclipse.org/legal/epl-v10.html
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * Copyright (C) 2016 The JRuby Team
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the EPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the EPL, the GPL or the LGPL.
 ***** END LICENSE BLOCK *****/
package org.jruby.javasupport.ext;

import org.jruby.*;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.javasupport.Java;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import static org.jruby.javasupport.JavaUtil.convertJavaToUsableRubyObject;
import static org.jruby.javasupport.JavaUtil.unwrapJavaObject;

/**
 * Java::JavaLangReflect package extensions.
 *
 * @author kares
 */
public abstract class JavaUtilRegex {

    public static void define(final Ruby runtime) {
        Pattern.define(runtime);
        Matcher.define(runtime);
    }

    @JRubyClass(name = "Java::JavaUtilRegex::Pattern")
    public static class Pattern {

        static RubyClass define(final Ruby runtime) {
            final RubyModule Pattern = Java.getProxyClass(runtime, java.util.regex.Pattern.class);
            Pattern.defineAnnotatedMethods(Pattern.class);
            return (RubyClass) Pattern;
        }

        @JRubyMethod(name = "=~", required = 1)
        public static IRubyObject op_match(final ThreadContext context, final IRubyObject self, IRubyObject str) {
            final java.util.regex.Matcher matcher = matcher(self, str);
            return matcher.find() ? context.runtime.newFixnum(matcher.start()) : context.nil;
        }

        @JRubyMethod(name = "match", required = 1)
        public static IRubyObject match(final ThreadContext context, final IRubyObject self, IRubyObject str) {
            final java.util.regex.Matcher matcher = matcher(self, str);
            if ( ! matcher.find() ) return context.nil;
            final RubyObject matcherProxy = (RubyObject) convertJavaToUsableRubyObject(context.runtime, matcher);
            matcherProxy.setInternalVariable("str", str); // matcher.str = str
            return matcherProxy;
        }

        @JRubyMethod(name = "===", required = 1)
        public static IRubyObject eqq(final ThreadContext context, final IRubyObject self, IRubyObject str) {
            return context.runtime.newBoolean( matcher(self, str).find() );
        }

        @JRubyMethod(name = "casefold?")
        public static IRubyObject casefold_p(final ThreadContext context, final IRubyObject self) {
            final java.util.regex.Pattern regex = unwrapJavaObject(self);
            boolean i = ( regex.flags() & java.util.regex.Pattern.CASE_INSENSITIVE ) != 0;
            return context.runtime.newBoolean(i);
        }

        private static java.util.regex.Matcher matcher(final IRubyObject self, final IRubyObject str) {
            final java.util.regex.Pattern regex = unwrapJavaObject(self);
            return regex.matcher((CharSequence) str.toJava(CharSequence.class));
        }

    }

    @JRubyClass(name = "Java::JavaUtilRegex::Matcher")
    public static class Matcher {

        static RubyClass define(final Ruby runtime) {
            final RubyModule Matcher = Java.getProxyClass(runtime, java.util.regex.Matcher.class);
            Matcher.defineAnnotatedMethods(Matcher.class);
            return (RubyClass) Matcher;
        }

        @JRubyMethod
        public static IRubyObject regexp(final ThreadContext context, final IRubyObject self) {
            final java.util.regex.Matcher matcher = unwrapJavaObject(self);
            return convertJavaToUsableRubyObject(context.runtime, matcher.pattern());
        }

        @JRubyMethod
        public static IRubyObject begin(final ThreadContext context, final IRubyObject self, final IRubyObject idx) {
            final java.util.regex.Matcher matcher = unwrapJavaObject(self);
            if ( idx instanceof RubySymbol ) {
                return context.runtime.newFixnum( matcherStart(matcher, idx.toString()) );
            }
            final int group = idx.convertToInteger().getIntValue();
            return context.runtime.newFixnum(matcher.start(group));
        }

        @JRubyMethod
        public static IRubyObject end(final ThreadContext context, final IRubyObject self, final IRubyObject idx) {
            final java.util.regex.Matcher matcher = unwrapJavaObject(self);
            if ( idx instanceof RubySymbol ) {
                return context.runtime.newFixnum( matcherEnd(matcher, idx.toString()) );
            }
            final int group = idx.convertToInteger().getIntValue();
            return context.runtime.newFixnum(matcher.end(group));
        }

        @JRubyMethod
        public static IRubyObject offset(final ThreadContext context, final IRubyObject self, final IRubyObject idx) {
            final java.util.regex.Matcher matcher = unwrapJavaObject(self);
            final IRubyObject beg; final IRubyObject end;
            if ( idx instanceof RubySymbol ) {
                beg = context.runtime.newFixnum( matcherStart(matcher, idx.toString()) );
                end = context.runtime.newFixnum( matcherEnd(matcher, idx.toString()) );
            }
            else {
                final int group = idx.convertToInteger().getIntValue();
                beg = context.runtime.newFixnum( matcher.start(group) );
                end = context.runtime.newFixnum( matcher.end(group) );
            }
            return RubyArray.newArray(context.runtime, beg, end);
        }

        @JRubyMethod(name = { "length", "size" })
        public static RubyFixnum size(final ThreadContext context, final IRubyObject self) {
            final java.util.regex.Matcher matcher = unwrapJavaObject(self);
            return context.runtime.newFixnum(matcher.groupCount() + 1); // the Ruby way!
        }

        @JRubyMethod
        public static RubyString string(final ThreadContext context, final IRubyObject self) {
            return str(context, self);
        }

        private static RubyString str(final ThreadContext context, final IRubyObject self) {
            final IRubyObject str = (IRubyObject) self.getInternalVariables().getInternalVariable("str");
            return /* str == null ? string(context, self) : */ str.convertToString();
        }

        @JRubyMethod // str[ 0..start(0) ]
        public static IRubyObject pre_match(final ThreadContext context, final IRubyObject self) {
            final java.util.regex.Matcher matcher = unwrapJavaObject(self);
            return str(context, self).substr(context.runtime, 0, matcher.start(0));
        }

        @JRubyMethod // str[ end(0)..-1 ]
        public static IRubyObject post_match(final ThreadContext context, final IRubyObject self) {
            final java.util.regex.Matcher matcher = unwrapJavaObject(self);
            final RubyString str = str(context, self);
            final int offset = matcher.end(0);
            return str.substr(context.runtime, offset, str.size() - offset);
        }

        @JRubyMethod
        public static RubyArray to_a(final ThreadContext context, final IRubyObject self) {
            return RubyArray.newArrayMayCopy(context.runtime, groups(context, self, 0));
        }

        @JRubyMethod
        public static RubyArray captures(final ThreadContext context, final IRubyObject self) {
            return RubyArray.newArrayMayCopy(context.runtime, groups(context, self, 1));
        }

        private static IRubyObject[] groups(final ThreadContext context, final IRubyObject self, final int off) {
            final Ruby runtime = context.runtime;
            final java.util.regex.Matcher matcher = unwrapJavaObject(self);
            final IRubyObject[] arr = new IRubyObject[ matcher.groupCount() - off + 1 ];
            for ( int i = 0; i < arr.length; i++ ) {
                if ( matcher.start(i + off) == -1 ) {
                    arr[i] = context.nil;
                }
                else {
                    arr[i] = runtime.newString(matcher.group(i + off));
                }
            }
            return arr;
        }

        @JRubyMethod(name = "[]", required = 1)
        public static IRubyObject aref(final ThreadContext context, final IRubyObject self, final IRubyObject idx) {
            final java.util.regex.Matcher matcher = unwrapJavaObject(self);
            if ( idx instanceof RubySymbol || idx instanceof RubyString ) {
                return context.runtime.newString( matcher.group(idx.toString()) );
            }
            if ( idx instanceof RubyInteger ) {
                final int group = ((RubyInteger) idx).getIntValue();
                return context.runtime.newString( matcher.group(group) );
            }
            return to_a(context, self).aref(idx); // Range
        }

        @JRubyMethod(name = "[]", required = 2)
        public static IRubyObject aref(final ThreadContext context, final IRubyObject self,
            final IRubyObject arg0, final IRubyObject arg1) {
            return to_a(context, self).aref(arg0, arg1);
        }

        @JRubyMethod(rest = true)
        public static IRubyObject values_at(final ThreadContext context, final IRubyObject self, final IRubyObject[] args) {
            return to_a(context, self).values_at(args);
        }

        private static Integer matcherStart(final java.util.regex.Matcher matcher, final String group) {
            if ( startMethod == null ) { // only available since Java 8
                throw new UnsupportedOperationException("start(String) only works on Java 8+");
            }
            return invoke(startMethod, matcher, group);
        }

        private static Integer matcherEnd(final java.util.regex.Matcher matcher, final String group) {
            if ( endMethod == null ) { // only available since Java 8
                throw new UnsupportedOperationException("end(String) only works on Java 8+");
            }
            return invoke(endMethod, matcher, group);
        }

        private static Integer invoke(final Method method, final java.util.regex.Matcher matcher, final String group) {
            try {
                return (Integer) method.invoke(matcher, group);
            }
            catch (IllegalAccessException e) {
                Helpers.throwException(e); return null;
            }
            catch (InvocationTargetException e) {
                Helpers.throwException(e.getTargetException()); return null;
            }
        }

        private static final Method startMethod;
        private static final Method endMethod;

        static {
            Method start, end;
            try {
                start = java.util.regex.Matcher.class.getMethod("start", String.class);
                end = java.util.regex.Matcher.class.getMethod("end", String.class);
            }
            catch (NoSuchMethodException e) {
                start = end = null; // Java 7
            }
            startMethod = start; endMethod = end;
        }

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy