提交 17072af5 编写于 作者: N Nicolas De Loof

utility methods to manage canonical name (fullname from jenkins root)

and item renaming in relative item names
上级 129e76bf
......@@ -33,13 +33,11 @@ import hudson.matrix.Axis;
import hudson.util.DescriptorList;
import hudson.util.XStream2;
import jenkins.model.Jenkins;
import org.apache.commons.lang.StringUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.StringTokenizer;
import java.util.*;
/**
* Convenience methods related to {@link Item}.
......@@ -104,6 +102,102 @@ public class Items {
return r;
}
/**
* Computes the canonical full name of a relative path in an {@link ItemGroup} context, handling relative
* positions ".." and "." as absolute path starting with "/". The resulting name is the item fullName from Jenkins
* root.
*/
public static String getCanonicalName(ItemGroup context, String path) {
String[] c = context.getFullName().split("/");
String[] p = path.split("/");
Stack name = new Stack();
for (int i=0; i<c.length;i++) {
if (i==0 && c[i].equals("")) continue;
name.push(c[i]);
}
for (int i=0; i<p.length;i++) {
if (i==0 && p[i].equals("")) {
// Absolute path starting with a "/"
name.clear();
continue;
}
if (p[i].equals("..")) {
name.pop();
continue;
}
if (p[i].equals(".")) {
continue;
}
name.push(p[i]);
}
return StringUtils.join(name, '/');
}
/**
* Compute the relative name of an item after a rename occurred. Used to manage job references as names in
* plugins to support {@link hudson.model.listeners.ItemListener#onRenamed(hudson.model.Item, String, String)}.
* <p>
* In a hierarchical context, when a plugin has a reference to a job as <code>../foo/bar</code> this method will
* handle the relative path as "foo" is renamed to "zot" to compute <code>../zot/bar</code>
*
* @param oldFullName the old full name of the item
* @param newFullName the new full name of the item
* @param relativeNames coma separated list of Item relative names
* @param context the {link ItemGroup} relative names refer to
* @return relative name for the renamed item, based on the same ItemGroup context
*/
public static String rename(String oldFullName, String newFullName, String relativeNames, ItemGroup context) {
StringTokenizer tokens = new StringTokenizer(relativeNames,",");
List<String> newValue = new ArrayList<String>();
while(tokens.hasMoreTokens()) {
String relativeName = tokens.nextToken().trim();
String canonicalName = getCanonicalName(context, relativeName);
if (canonicalName.startsWith(oldFullName)) {
String newCanonicalName = newFullName + canonicalName.substring(oldFullName.length());
// relative name points to the renamed item, let's compute the new relative name
newValue.add( rename(canonicalName, newCanonicalName, relativeName) );
} else {
newValue.add(relativeName);
}
}
return StringUtils.join(newValue, ",");
}
/**
* Compute the relative name of an Item after renaming
*/
private static String rename(String oldFullName, String newFullName, String relativeName) {
String[] a = oldFullName.split("/");
String[] n = newFullName.split("/");
assert a.length == n.length;
String[] r = relativeName.split("/");
int j = a.length-1;
for(int i=r.length-1;i>=0;i--) {
String part = r[i];
if (part.equals("") && i==0) {
continue;
}
if (part.equals(".")) {
continue;
}
if (part.equals("..")) {
j--;
continue;
}
if (part.equals(a[j])) {
r[i] = n[j];
j--;
continue;
}
}
return StringUtils.join(r, '/');
}
/**
* Loads a {@link Item} from a config file.
*
......
package hudson.model;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
/**
* @author: <a hef="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
*/
public class ItemsTest {
private static ItemGroup root;
private static ItemGroup foo;
private static ItemGroup foo_bar;
@BeforeClass
public static void itemGroups() {
root = mock(ItemGroup.class);
when(root.getFullName()).thenReturn("");
foo = mock(ItemGroup.class);
when(foo.getFullName()).thenReturn("foo");
foo_bar = mock(ItemGroup.class);
when(foo_bar.getFullName()).thenReturn("foo/bar");
}
@Test
public void getCanonicalName() {
assertEquals("foo", Items.getCanonicalName(root, "foo"));
assertEquals("foo", Items.getCanonicalName(root, "/foo"));
assertEquals("foo/bar", Items.getCanonicalName(root, "foo/bar"));
assertEquals("foo/bar", Items.getCanonicalName(foo, "bar"));
assertEquals("bar", Items.getCanonicalName(foo, "/bar"));
assertEquals("bar", Items.getCanonicalName(foo, "../bar"));
assertEquals("foo/bar", Items.getCanonicalName(foo, "./bar"));
assertEquals("foo/bar/baz/qux", Items.getCanonicalName(foo_bar, "baz/qux"));
assertEquals("foo/baz/qux", Items.getCanonicalName(foo_bar, "../baz/qux"));
}
@Test
public void rename() {
assertEquals("meu,bu,zo", Items.rename("ga", "meu", "ga,bu,zo", root ));
assertEquals("ga,bu,zo", Items.rename("ga", "meu", "ga,bu,zo", foo_bar ));
assertEquals("meu,bu,zo", Items.rename("foo/ga", "foo/meu", "ga,bu,zo", foo ));
assertEquals("/meu,/bu,/zo", Items.rename("ga", "meu", "/ga,/bu,/zo", root ));
assertEquals("/meu,/bu,/zo", Items.rename("ga", "meu", "/ga,/bu,/zo", foo_bar ));
assertEquals("../meu,../bu,../zo", Items.rename("ga", "meu", "../ga,../bu,../zo", foo ));
assertEquals("../qux/ga,bu,zo", Items.rename("foo/baz", "foo/qux", "../baz/ga,bu,zo", foo_bar ));
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册