org.jruby.util.RegexpOptions Maven / Gradle / Ivy
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.jruby.util;
import org.jcodings.Encoding;
import org.jcodings.specific.ASCIIEncoding;
import org.jcodings.specific.EUCJPEncoding;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.Ruby;
import org.jruby.RubyRegexp;
public class RegexpOptions implements Cloneable {
private static ByteList WINDOWS31J = new ByteList(new byte[] {'W', 'i', 'n', 'd', 'o', 'w', 's', '-', '3', '1', 'J'});
public static final RegexpOptions NULL_OPTIONS = new RegexpOptions(KCode.NONE, true);
public RegexpOptions() {
this(KCode.NONE, true);
}
public RegexpOptions(KCode kcode, boolean isKCodeDefault) {
this.kcode = kcode;
this.kcodeDefault = isKCodeDefault;
assert kcode != null : "kcode must always be set to something";
}
public boolean isExtended() {
return extended;
}
public void setExtended(boolean extended) {
this.extended = extended;
}
public boolean isIgnorecase() {
return ignorecase;
}
public void setIgnorecase(boolean ignorecase) {
this.ignorecase = ignorecase;
}
public boolean isFixed() {
return fixed;
}
public void setFixed(boolean fixed) {
this.fixed = fixed;
}
public KCode getKCode() {
return kcode;
}
public String getKCodeName() {
return isKcodeDefault() ? null : getKCode().name().toLowerCase();
}
/**
* This regexp has an explicit encoding flag or 'nesu' letter associated
* with it.
*
* @param kcode to be set
*/
public void setExplicitKCode(KCode kcode) {
this.kcode = kcode;
kcodeDefault = false;
}
private KCode getExplicitKCode() {
if (kcodeDefault == true) return null;
return kcode;
}
/**
* Whether the kcode associated with this regexp is implicit (aka
* default) or is specified explicitly (via 'nesu' syntax postscript or
* flags to Regexp.new.
*/
public boolean isKcodeDefault() {
return kcodeDefault;
}
public boolean isMultiline() {
return multiline;
}
public void setMultiline(boolean multiline) {
this.multiline = multiline;
}
public boolean isOnce() {
return once;
}
public void setOnce(boolean once) {
this.once = once;
}
public boolean isJava() {
return java;
}
public void setJava(boolean java) {
this.java = java;
}
public boolean isEncodingNone() {
return encodingNone;
}
public void setEncodingNone(boolean encodingNone) {
this.encodingNone = encodingNone;
}
public boolean isLiteral() {
return literal;
}
public void setLiteral(boolean literal) {
this.literal = literal;
}
public boolean isEmbeddable() {
return multiline && ignorecase && extended;
}
/**
* Calculate the encoding based on kcode option set via 'nesu'. Also as
* side-effects:
* 1.set whether this marks the soon to be made regexp as 'fixed'.
* 2.kcode.none will set 'none' option
* @return null if no explicit encoding is specified.
*/
public Encoding setup19(Ruby runtime) {
KCode explicitKCode = getExplicitKCode();
// None will not set fixed
if (explicitKCode == KCode.NONE) {
setEncodingNone(true);
return ASCIIEncoding.INSTANCE;
}
if (explicitKCode == KCode.EUC) {
setFixed(true);
return EUCJPEncoding.INSTANCE;
} else if (explicitKCode == KCode.SJIS) {
setFixed(true);
return runtime.getEncodingService().loadEncoding(WINDOWS31J);
} else if (explicitKCode == KCode.UTF8) {
setFixed(true);
return UTF8Encoding.INSTANCE;
}
return null;
}
/**
* This int value can be used by compiler or any place where we want
* an integer representation of the state of this object.
*
* Note: This is for full representation of state in the JIT. It is not
* to be confused with state of marshalled regexp data.
*/
public int toEmbeddedOptions() {
int options = toJoniOptions();
if (once) options |= RubyRegexp.RE_OPTION_ONCE;
if (literal) options |= RubyRegexp.RE_LITERAL;
if (kcodeDefault) options |= RubyRegexp.RE_DEFAULT;
if (fixed) options |= RubyRegexp.RE_FIXED;
return options;
}
/**
* This int value is meant to only be used when dealing directly with
* the joni regular expression library. It differs from embeddedOptions
* in that it only contains bit values which Joni cares about.
*/
public int toJoniOptions() {
int options = 0;
// Note: once is not an option that is pertinent to Joni so we exclude it.
if (multiline) options |= RubyRegexp.RE_OPTION_MULTILINE;
if (ignorecase) options |= RubyRegexp.RE_OPTION_IGNORECASE;
if (extended) options |= RubyRegexp.RE_OPTION_EXTENDED;
if (!isKcodeDefault()) options |= kcode.bits();
return options;
}
/**
* This int value is used by Regex#options
*/
public int toOptions() {
int options = 0;
if (multiline) options |= RubyRegexp.RE_OPTION_MULTILINE;
if (ignorecase) options |= RubyRegexp.RE_OPTION_IGNORECASE;
if (extended) options |= RubyRegexp.RE_OPTION_EXTENDED;
if (fixed) options |= RubyRegexp.RE_FIXED;
if (encodingNone) options |= RubyRegexp.ARG_ENCODING_NONE;
return options;
}
public static RegexpOptions fromEmbeddedOptions(int embeddedOptions) {
RegexpOptions options = fromJoniOptions(embeddedOptions);
options.kcodeDefault = (embeddedOptions & RubyRegexp.RE_DEFAULT) != 0;
options.setOnce((embeddedOptions & RubyRegexp.RE_OPTION_ONCE) != 0);
options.setLiteral((embeddedOptions & RubyRegexp.RE_LITERAL) != 0);
options.setFixed((embeddedOptions & RubyRegexp.RE_FIXED) != 0);
return options;
}
public static RegexpOptions fromJoniOptions(int joniOptions) {
KCode kcode = KCode.fromBits(joniOptions);
RegexpOptions options = new RegexpOptions(kcode, kcode == KCode.NONE);
options.setMultiline((joniOptions & RubyRegexp.RE_OPTION_MULTILINE) != 0);
options.setIgnorecase((joniOptions & RubyRegexp.RE_OPTION_IGNORECASE) != 0);
options.setExtended((joniOptions & RubyRegexp.RE_OPTION_EXTENDED) != 0);
options.setFixed((joniOptions & RubyRegexp.RE_FIXED) != 0);
options.setOnce((joniOptions & RubyRegexp.RE_OPTION_ONCE) != 0);
return options;
}
public RegexpOptions withoutOnce() {
RegexpOptions options = (RegexpOptions)clone();
options.setOnce(false);
return options;
}
@Override
public int hashCode() {
int hash = 7;
hash = 11 * hash + (this.kcode != null ? this.kcode.hashCode() : 0);
hash = 11 * hash + (this.fixed ? 1 : 0);
hash = 11 * hash + (this.once ? 1 : 0);
hash = 11 * hash + (this.extended ? 1 : 0);
hash = 11 * hash + (this.multiline ? 1 : 0);
hash = 11 * hash + (this.ignorecase ? 1 : 0);
hash = 11 * hash + (this.java ? 1 : 0);
hash = 11 * hash + (this.encodingNone ? 1 : 0);
hash = 11 * hash + (this.kcodeDefault ? 1 : 0);
hash = 11 * hash + (this.literal ? 1 : 0);
return hash;
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException cnse) {throw new RuntimeException(cnse);}
}
@Override
public boolean equals(Object other) {
if (!(other instanceof RegexpOptions)) return false;
// Note: literal and once can be different in this object but for the
// sake of equality we ignore those two fields since those flags do
// not affect Ruby equality.
RegexpOptions o = (RegexpOptions)other;
boolean equality = o.extended == extended &&
o.fixed == fixed &&
o.ignorecase == ignorecase &&
o.java == java &&
o.multiline == multiline;
if(encodingNone || o.encodingNone) {
return equality && o.kcode == kcode;
} else {
return equality &&
o.encodingNone == encodingNone &&
o.kcode == kcode &&
o.kcodeDefault == kcodeDefault;
}
}
@Override
public String toString() {
return "RegexpOptions(kcode: " + kcode +
(encodingNone == true ? ", encodingNone" : "") +
(extended == true ? ", extended" : "") +
(fixed == true ? ", fixed" : "") +
(ignorecase == true ? ", ignorecase" : "") +
(java == true ? ", java" : "") +
(kcodeDefault == true ? ", kcodeDefault" : "") +
(literal == true ? ", literal" : "") +
(multiline == true ? ", multiline" : "") +
(once == true ? ", once" : "") +
")";
}
private KCode kcode;
private boolean fixed;
private boolean once;
private boolean extended;
private boolean multiline;
private boolean ignorecase;
private boolean java;
private boolean encodingNone;
private boolean kcodeDefault;
private boolean literal;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy