You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-dev@jackrabbit.apache.org by "gianluca.soffredini@metaframe.it" <gi...@metaframe.it> on 2016/03/30 18:54:19 UTC
Problems with OAK restrictions
Good evening,
I still write for the problem I found in the management of the rep:glob
restriction set to empty string.
I made more tests and I also used the ReadTest class of OAK.
In effect the restriction rep:glob set to empty string works on nodes.
If I apply the restriction on path//my_folder /by adding the permission
of READ, both with the call to Session.getNode ("/ my_folder") that with
the call to
Session.getRootNode (). getNodes () the system can access to the path.
The problem is when I run the query "/select F. * from [nt: folder] as
F/". The query result is empty.
It seems to me weird because if I access to the node I should also have
access to the folder that the node represents.
Analyzing the OAK code in version 1.4.0 I have found the GlobPattern class.
In the case of restriction rep:glob set to empty string that class
always uses the PathPattern class and always makes the call
/path.equals(toMatch)/.
This call works when the path//my_folder/ is used in nodes and it works
even when the path is evaluated in the query "/select * from F. [nt:
folder] as F/".
The problem is the following: during query execution is also rightly
evaluated
the jcr: primaryType node. In this case, the primary type is nt: folder.
It is called again the following method of GlobPattern class:
/*@Override*//*
*//* public boolean matches(@Nonnull Tree tree, @Nullable
PropertyState property) {*//*
*//* String itemPath = (property == null) ? tree.getPath() :
PathUtils.concat(tree.getPath(), property.getName());*//*
*//* return matches(itemPath);*//*
*//* }
*/and then the matches method of the class PathPattern:
/*@Override*//*
*//* boolean matches(@Nonnull String toMatch) {*//*
*//* if (patternStr.isEmpty()) {*//*
*//* return path.equals(toMatch);*//*
*//* } else {*//*
*//* // no wildcard contained in restriction: use path
defined*//*
*//* // by path + restriction to calculate the match*//*
*//* return Text.isDescendantOrEqual(patternStr,
toMatch);*//*
*//* }*//*
*//* }
*/This call can not works because in the case in which
/patternStr.isEmpty()/ is called the method /path.equals(toMatch)/.
But the path class attribute is set to /*/my_folder*/ and the toMatch
variable is set to /*/my_folder/jrc: primaryType*/. It 's clear that the
call to the equals method will return false.
To solve the problem I changed the class OAK GlobPattern as follows, and
now the query "/select * from F. [nt: folder] as F/" correctly returns
in its results the folder//my_folder/.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.oak.security.authorization.restriction;
import static com.google.common.base.Preconditions.checkNotNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.jcr.security.AccessControlException;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.commons.PathUtils;
import
org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionPattern;
import org.apache.jackrabbit.util.Text;
import com.google.common.base.Objects;
/**
* {@code GlobPattern} defines a simplistic pattern matching. It consists
* of a mandatory (leading) path and an optional "glob" that may
contain one or
* more wildcard characters ("{@code *}") according to the glob matching
* defined by {@link javax.jcr.Node#getNodes(String[])}. In contrast to
that
* method the {@code GlobPattern} operates on path (not only names).
* <p>
*
* <p>
* Please note the following special cases:
* <pre>
* NodePath | Restriction | Matches
*
-----------------------------------------------------------------------------
* /foo | null | matches /foo and all children of /foo
* /foo | "" | matches /foo only
* </pre>
* </p>
*
* <p>
* Examples without wildcard char:
* <pre>
* NodePath = "/foo"
* Restriction | Matches
*
-----------------------------------------------------------------------------
* /cat | the node /foo/cat and all it's children
* /cat/ | the descendants of the node /foo/cat
* cat | the node /foocat and all it's children
* cat/ | all descendants of the node /foocat
* </pre>
* </p>
*
* <p>
* Examples including wildcard char:
* <pre>
* NodePath = "/foo"
* Restriction | Matches
*
-----------------------------------------------------------------------------
* * | foo, all siblings of foo and their descendants
* /*cat | all children of /foo whose path ends with "cat"
* /*/cat | all non-direct descendants of /foo named "cat"
* /cat* | all descendant path of /foo that have the direct
foo-descendant segment starting with "cat"
* *cat | all siblings and descendants of foo that have a
name ending with cat
* */cat | all descendants of /foo and foo's siblings that
have a name segment "cat"
* cat/* | all descendants of '/foocat'
* /cat/* | all descendants of '/foo/cat'
* *cat/* | all siblings and descendants of foo that have
an intermediate segment ending with 'cat'
* /*cat/* | all descendants of /foo that have an
intermediate segment ending with 'cat'
* </pre>
* </p>
*/
final class GlobPattern implements RestrictionPattern {
private static final char WILDCARD_CHAR = '*';
private static final int MAX_WILDCARD = 20;
private final String path;
private final String restriction;
* private Pattern pattern;*
*private GlobPattern(@Nonnull String path, @Nonnull String restriction) {**
** this.path = checkNotNull(path);**
** this.restriction = restriction;**
** }*
static GlobPattern create(@Nonnull String nodePath, @Nonnull String
restrictions) {
return new GlobPattern(nodePath, restrictions);
}
static void validate(@Nonnull String restriction) throws
AccessControlException {
int cnt = 0;
for (int i = 0; i < restriction.length(); i++) {
if (WILDCARD_CHAR == restriction.charAt(i)) {
cnt++;
}
if (cnt > MAX_WILDCARD) {
throw new AccessControlException("Number of wildcards
in rep:glob exceeds allowed complexity.");
}
}
}
*private Pattern getPattern(@Nullable PropertyState property){**
** if (!restriction.isEmpty()) {**
** StringBuilder b = new StringBuilder(path);**
** b.append(restriction);**
**
** int lastPos = restriction.lastIndexOf(WILDCARD_CHAR);**
** if (lastPos >= 0) {**
** String end;**
** if (lastPos != restriction.length()-1) {**
** end = restriction.substring(lastPos + 1);**
** } else {**
** end = null;**
** }**
** pattern = new WildcardPattern(b.toString(), end);**
** } else {**
** pattern = new PathPattern(b.toString());**
** }**
** } else {**
** if(property == null){**
** pattern = new PathPattern(restriction);**
** }**
** else{**
** pattern = new PathPropertyPattern(restriction,
property.getName());**
** }**
** }**
** return pattern;**
** }*
//-------------------------------------------------<
RestrictionPattern >---
@Override
public boolean matches(@Nonnull Tree tree, @Nullable PropertyState
property) {
*pattern = getPattern(property);*
String itemPath = (property == null) ? tree.getPath() :
PathUtils.concat(tree.getPath(), property.getName());
return matches(itemPath);
}
@Override
public boolean matches(@Nonnull String path) {
return pattern.matches(path);
}
@Override
public boolean matches() {
// repository level permissions never match any glob pattern
return false;
}
//-------------------------------------------------------------< Object >---
/**
* @see Object#hashCode()
*/
@Override
public int hashCode() {
return Objects.hashCode(path, restriction);
}
/**
* @see Object#toString()
*/
@Override
public String toString() {
return path + " : " + restriction;
}
/**
* @see Object#equals(Object)
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof GlobPattern) {
GlobPattern other = (GlobPattern) obj;
return path.equals(other.path) &&
restriction.equals(other.restriction);
}
return false;
}
//------------------------------------------------------< inner
classes >---
/**
* Base for PathPattern and WildcardPattern
*/
private abstract class Pattern {
abstract boolean matches(@Nonnull String toMatch);
}
/**
* Path pattern: The restriction is missing or doesn't contain any
wildcard character.
*/
private final class PathPattern extends Pattern {
private final String patternStr;
private PathPattern(@Nonnull String patternStr) {
this.patternStr = patternStr;
}
@Override
boolean matches(@Nonnull String toMatch) {
if (patternStr.isEmpty()) {
return path.equals(toMatch);
} else {
// no wildcard contained in restriction: use path defined
// by path + restriction to calculate the match
return Text.isDescendantOrEqual(patternStr, toMatch);
}
}
}
*/****
** * Path pattern: The restriction is missing or doesn't contain any
wildcard character and manage PropertyState case.**
** */**
** private final class PathPropertyPattern extends Pattern {**
**
** private final String patternStr;**
****
** private final String propertyName;**
**
** private PathPropertyPattern(@Nonnull String patternStr,
@Nonnull String propertyName) {**
** this.patternStr = patternStr;**
** this.propertyName = propertyName;**
** }**
**
** @Override**
** boolean matches(@Nonnull String toMatch) {**
** if (patternStr.isEmpty()) {**
** //return path.equals(toMatch);**
** return evalutatePathAndProperty().equals(toMatch);**
** } else {**
** // no wildcard contained in restriction: use path
defined**
** // by path + restriction to calculate the match**
** return Text.isDescendantOrEqual(patternStr, toMatch);**
** }**
** }**
****
** private String evalutatePathAndProperty(){**
** final StringBuilder builder = new StringBuilder();**
** builder.append(path);**
** if(!path.endsWith("/")){**
** builder.append("/");**
** }**
** builder.append(propertyName);**
** return builder.toString();**
** }**
** }*
/**
* Wildcard pattern: The specified restriction contains one or more
wildcard character(s).
*/
private final class WildcardPattern extends Pattern {
private final String patternEnd;
private final char[] patternChars;
private WildcardPattern(@Nonnull String patternStr, @Nullable
String patternEnd) {
patternChars = patternStr.toCharArray();
this.patternEnd = patternEnd;
}
@Override
boolean matches(@Nonnull String toMatch) {
if (patternEnd != null && !toMatch.endsWith(patternEnd)) {
// shortcut: verify if end of pattern matches end of
toMatch
return false;
}
char[] tm = (toMatch.endsWith("/")) ? toMatch.substring(0,
toMatch.length()-1).toCharArray() : toMatch.toCharArray();
// shortcut didn't reveal mismatch -> need to process the
internal match method.
return matches(patternChars, 0, tm, 0, MAX_WILDCARD);
}
/**
*
* @param pattern The pattern
* @param pOff
* @param s
* @param sOff
* @return {@code true} if matches, {@code false} otherwise
*/
private boolean matches(char[] pattern, int pOff,
char[] s, int sOff, int cnt) {
if (cnt <= 0) {
throw new IllegalArgumentException("Illegal glob
pattern " + GlobPattern.this);
}
int pLength = pattern.length;
int sLength = s.length;
while (true) {
// end of pattern reached: matches only if sOff points
at the end
// of the string to match.
if (pOff >= pLength) {
return sOff >= sLength;
}
// the end of the string to match has been reached but
pattern
// doesn't have '*' at patternIndex -> no match
if (sOff >= sLength && pattern[pOff] != WILDCARD_CHAR) {
return false;
}
// the next character of the pattern is '*'
// -> recursively test if the rest of the specified
string matches
if (pattern[pOff] == WILDCARD_CHAR) {
if (++pOff >= pLength) {
return true;
}
cnt--;
while (true) {
if (matches(pattern, pOff, s, sOff, cnt)) {
return true;
}
if (sOff >= sLength) {
return false;
}
sOff++;
}
}
// not yet reached end of patter nor string and not
wildcard character.
// the 2 strings don't match in case the characters at
the current
// position are not the same.
if (pOff < pLength && sOff < sLength) {
if (pattern[pOff] != s[sOff]) {
return false;
}
}
pOff++;
sOff++;
}
}
}
}*
*My changes make sense in your opinion or behavior of which I spoke
about is wanted?
Thanks in advance.
Gianluca Soffredini
Project Manager
Metaframe SPS S.r.l.
Via Toniolo, 13
30030 Vigonovo(VE)
mobile: +39 3342235291
email: gianluca.soffredini@metaframe.it
<ma...@metaframe.it>
SKYPE ID: gianlucas72
Logo Metaframe SPS S.r.l.
Re: Problems with OAK restrictions
Posted by Angela Schreiber <an...@adobe.com>.
hi gianluca
the glob pattern doesn't distinguish between nodes and properties and
just verifies if the path of a given item matches the specified pattern.
so, having the pattern value set to empty string limits the effect of the
ace to that very node. the properties of that node are not accessible.
so, the outcome is expected.
i guess OAK-2437<https://issues.apache.org/jira/browse/OAK-2437> (https://issues.apache.org/jira/browse/OAK-2437) would
be the what you are looking for.
until this is resolved you can work around by adding an additional entry that
grants rep:readProperties privilege to you principal just for the 'jcr:primaryType'
property.
hope that helps
angela
From: "gianluca.soffredini@metaframe.it<ma...@metaframe.it>" <gi...@metaframe.it>>
Reply-To: "oak-dev@jackrabbit.apache.org<ma...@jackrabbit.apache.org>" <oa...@jackrabbit.apache.org>>
Date: Wednesday 30 March 2016 18:54
To: "oak-dev@jackrabbit.apache.org<ma...@jackrabbit.apache.org>" <oa...@jackrabbit.apache.org>>
Subject: Problems with OAK restrictions
Good evening,
I still write for the problem I found in the management of the rep:glob restriction set to empty string.
I made more tests and I also used the ReadTest class of OAK.
In effect the restriction rep:glob set to empty string works on nodes.
If I apply the restriction on path /my_folder by adding the permission of READ, both with the call to Session.getNode ("/ my_folder") that with the call to
Session.getRootNode (). getNodes () the system can access to the path.
The problem is when I run the query "select F. * from [nt: folder] as F". The query result is empty.
It seems to me weird because if I access to the node I should also have access to the folder that the node represents.
Analyzing the OAK code in version 1.4.0 I have found the GlobPattern class.
In the case of restriction rep:glob set to empty string that class always uses the PathPattern class and always makes the call path.equals(toMatch).
This call works when the path /my_folder is used in nodes and it works
even when the path is evaluated in the query "select * from F. [nt: folder] as F".
The problem is the following: during query execution is also rightly evaluated
the jcr: primaryType node. In this case, the primary type is nt: folder.
It is called again the following method of GlobPattern class:
@Override
public boolean matches(@Nonnull Tree tree, @Nullable PropertyState property) {
String itemPath = (property == null) ? tree.getPath() : PathUtils.concat(tree.getPath(), property.getName());
return matches(itemPath);
}
and then the matches method of the class PathPattern:
@Override
boolean matches(@Nonnull String toMatch) {
if (patternStr.isEmpty()) {
return path.equals(toMatch);
} else {
// no wildcard contained in restriction: use path defined
// by path + restriction to calculate the match
return Text.isDescendantOrEqual(patternStr, toMatch);
}
}
This call can not works because in the case in which patternStr.isEmpty() is called the method path.equals(toMatch).
But the path class attribute is set to /my_folder and the toMatch variable is set to /my_folder/jrc: primaryType. It 's clear that the call to the equals method will return false.
To solve the problem I changed the class OAK GlobPattern as follows, and now the query "select * from F. [nt: folder] as F" correctly returns in its results the folder /my_folder.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.oak.security.authorization.restriction;
import static com.google.common.base.Preconditions.checkNotNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.jcr.security.AccessControlException;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionPattern;
import org.apache.jackrabbit.util.Text;
import com.google.common.base.Objects;
/**
* {@code GlobPattern} defines a simplistic pattern matching. It consists
* of a mandatory (leading) path and an optional "glob" that may contain one or
* more wildcard characters ("{@code *}") according to the glob matching
* defined by {@link javax.jcr.Node#getNodes(String[])}. In contrast to that
* method the {@code GlobPattern} operates on path (not only names).
* <p>
*
* <p>
* Please note the following special cases:
* <pre>
* NodePath | Restriction | Matches
* -----------------------------------------------------------------------------
* /foo | null | matches /foo and all children of /foo
* /foo | "" | matches /foo only
* </pre>
* </p>
*
* <p>
* Examples without wildcard char:
* <pre>
* NodePath = "/foo"
* Restriction | Matches
* -----------------------------------------------------------------------------
* /cat | the node /foo/cat and all it's children
* /cat/ | the descendants of the node /foo/cat
* cat | the node /foocat and all it's children
* cat/ | all descendants of the node /foocat
* </pre>
* </p>
*
* <p>
* Examples including wildcard char:
* <pre>
* NodePath = "/foo"
* Restriction | Matches
* -----------------------------------------------------------------------------
* * | foo, all siblings of foo and their descendants
* /*cat | all children of /foo whose path ends with "cat"
* /*/cat | all non-direct descendants of /foo named "cat"
* /cat* | all descendant path of /foo that have the direct foo-descendant segment starting with "cat"
* *cat | all siblings and descendants of foo that have a name ending with cat
* */cat | all descendants of /foo and foo's siblings that have a name segment "cat"
* cat/* | all descendants of '/foocat'
* /cat/* | all descendants of '/foo/cat'
* *cat/* | all siblings and descendants of foo that have an intermediate segment ending with 'cat'
* /*cat/* | all descendants of /foo that have an intermediate segment ending with 'cat'
* </pre>
* </p>
*/
final class GlobPattern implements RestrictionPattern {
private static final char WILDCARD_CHAR = '*';
private static final int MAX_WILDCARD = 20;
private final String path;
private final String restriction;
private Pattern pattern;
private GlobPattern(@Nonnull String path, @Nonnull String restriction) {
this.path = checkNotNull(path);
this.restriction = restriction;
}
static GlobPattern create(@Nonnull String nodePath, @Nonnull String restrictions) {
return new GlobPattern(nodePath, restrictions);
}
static void validate(@Nonnull String restriction) throws AccessControlException {
int cnt = 0;
for (int i = 0; i < restriction.length(); i++) {
if (WILDCARD_CHAR == restriction.charAt(i)) {
cnt++;
}
if (cnt > MAX_WILDCARD) {
throw new AccessControlException("Number of wildcards in rep:glob exceeds allowed complexity.");
}
}
}
private Pattern getPattern(@Nullable PropertyState property){
if (!restriction.isEmpty()) {
StringBuilder b = new StringBuilder(path);
b.append(restriction);
int lastPos = restriction.lastIndexOf(WILDCARD_CHAR);
if (lastPos >= 0) {
String end;
if (lastPos != restriction.length()-1) {
end = restriction.substring(lastPos + 1);
} else {
end = null;
}
pattern = new WildcardPattern(b.toString(), end);
} else {
pattern = new PathPattern(b.toString());
}
} else {
if(property == null){
pattern = new PathPattern(restriction);
}
else{
pattern = new PathPropertyPattern(restriction, property.getName());
}
}
return pattern;
}
//-------------------------------------------------< RestrictionPattern >---
@Override
public boolean matches(@Nonnull Tree tree, @Nullable PropertyState property) {
pattern = getPattern(property);
String itemPath = (property == null) ? tree.getPath() : PathUtils.concat(tree.getPath(), property.getName());
return matches(itemPath);
}
@Override
public boolean matches(@Nonnull String path) {
return pattern.matches(path);
}
@Override
public boolean matches() {
// repository level permissions never match any glob pattern
return false;
}
//-------------------------------------------------------------< Object >---
/**
* @see Object#hashCode()
*/
@Override
public int hashCode() {
return Objects.hashCode(path, restriction);
}
/**
* @see Object#toString()
*/
@Override
public String toString() {
return path + " : " + restriction;
}
/**
* @see Object#equals(Object)
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof GlobPattern) {
GlobPattern other = (GlobPattern) obj;
return path.equals(other.path) && restriction.equals(other.restriction);
}
return false;
}
//------------------------------------------------------< inner classes >---
/**
* Base for PathPattern and WildcardPattern
*/
private abstract class Pattern {
abstract boolean matches(@Nonnull String toMatch);
}
/**
* Path pattern: The restriction is missing or doesn't contain any wildcard character.
*/
private final class PathPattern extends Pattern {
private final String patternStr;
private PathPattern(@Nonnull String patternStr) {
this.patternStr = patternStr;
}
@Override
boolean matches(@Nonnull String toMatch) {
if (patternStr.isEmpty()) {
return path.equals(toMatch);
} else {
// no wildcard contained in restriction: use path defined
// by path + restriction to calculate the match
return Text.isDescendantOrEqual(patternStr, toMatch);
}
}
}
/**
* Path pattern: The restriction is missing or doesn't contain any wildcard character and manage PropertyState case.
*/
private final class PathPropertyPattern extends Pattern {
private final String patternStr;
private final String propertyName;
private PathPropertyPattern(@Nonnull String patternStr, @Nonnull String propertyName) {
this.patternStr = patternStr;
this.propertyName = propertyName;
}
@Override
boolean matches(@Nonnull String toMatch) {
if (patternStr.isEmpty()) {
//return path.equals(toMatch);
return evalutatePathAndProperty().equals(toMatch);
} else {
// no wildcard contained in restriction: use path defined
// by path + restriction to calculate the match
return Text.isDescendantOrEqual(patternStr, toMatch);
}
}
private String evalutatePathAndProperty(){
final StringBuilder builder = new StringBuilder();
builder.append(path);
if(!path.endsWith("/")){
builder.append("/");
}
builder.append(propertyName);
return builder.toString();
}
}
/**
* Wildcard pattern: The specified restriction contains one or more wildcard character(s).
*/
private final class WildcardPattern extends Pattern {
private final String patternEnd;
private final char[] patternChars;
private WildcardPattern(@Nonnull String patternStr, @Nullable String patternEnd) {
patternChars = patternStr.toCharArray();
this.patternEnd = patternEnd;
}
@Override
boolean matches(@Nonnull String toMatch) {
if (patternEnd != null && !toMatch.endsWith(patternEnd)) {
// shortcut: verify if end of pattern matches end of toMatch
return false;
}
char[] tm = (toMatch.endsWith("/")) ? toMatch.substring(0, toMatch.length()-1).toCharArray() : toMatch.toCharArray();
// shortcut didn't reveal mismatch -> need to process the internal match method.
return matches(patternChars, 0, tm, 0, MAX_WILDCARD);
}
/**
*
* @param pattern The pattern
* @param pOff
* @param s
* @param sOff
* @return {@code true} if matches, {@code false} otherwise
*/
private boolean matches(char[] pattern, int pOff,
char[] s, int sOff, int cnt) {
if (cnt <= 0) {
throw new IllegalArgumentException("Illegal glob pattern " + GlobPattern.this);
}
int pLength = pattern.length;
int sLength = s.length;
while (true) {
// end of pattern reached: matches only if sOff points at the end
// of the string to match.
if (pOff >= pLength) {
return sOff >= sLength;
}
// the end of the string to match has been reached but pattern
// doesn't have '*' at patternIndex -> no match
if (sOff >= sLength && pattern[pOff] != WILDCARD_CHAR) {
return false;
}
// the next character of the pattern is '*'
// -> recursively test if the rest of the specified string matches
if (pattern[pOff] == WILDCARD_CHAR) {
if (++pOff >= pLength) {
return true;
}
cnt--;
while (true) {
if (matches(pattern, pOff, s, sOff, cnt)) {
return true;
}
if (sOff >= sLength) {
return false;
}
sOff++;
}
}
// not yet reached end of patter nor string and not wildcard character.
// the 2 strings don't match in case the characters at the current
// position are not the same.
if (pOff < pLength && sOff < sLength) {
if (pattern[pOff] != s[sOff]) {
return false;
}
}
pOff++;
sOff++;
}
}
}
}
My changes make sense in your opinion or behavior of which I spoke about is wanted?
Thanks in advance.
Gianluca Soffredini
Project Manager
Metaframe SPS S.r.l.
Via Toniolo, 13
30030 Vigonovo(VE)
mobile: +39 3342235291
email: gianluca.soffredini@metaframe.it<ma...@metaframe.it>
SKYPE ID: gianlucas72
[Logo Metaframe SPS S.r.l.]