diff --git a/src/main/java/hudson/plugins/nested_view/NestedViewsSearch.java b/src/main/java/hudson/plugins/nested_view/NestedViewsSearch.java index cbd29cf..90f6722 100644 --- a/src/main/java/hudson/plugins/nested_view/NestedViewsSearch.java +++ b/src/main/java/hudson/plugins/nested_view/NestedViewsSearch.java @@ -4,10 +4,11 @@ import hudson.model.AbstractProject; import hudson.model.TopLevelItem; import hudson.model.View; -import hudson.plugins.nested_view.search.ExtendedSearch; +import hudson.plugins.nested_view.search.HelpItem; +import hudson.plugins.nested_view.search.NamableWithClass; +import hudson.plugins.nested_view.search.NestedViewsSearchResult; +import hudson.plugins.nested_view.search.Query; import hudson.search.Search; -import hudson.search.SearchIndex; -import hudson.search.SearchItem; import hudson.search.SearchResult; import hudson.search.SuggestedItem; import jenkins.model.Jenkins; @@ -25,355 +26,10 @@ import java.util.Collections; import java.util.Date; import java.util.List; -import java.util.Optional; import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public class NestedViewsSearch extends Search { - static class Query { - - private static final int MIN_LENGTH = 2; - - private final String original; - private final String withoutArguments; - - private boolean multiline; - private boolean searchByNvr; - private boolean finalFilter; - private boolean projectInfo; - private int stats = -1; - private int builds = -1; - private int last = -1; - - private String where = "vnj"; //v,n,j - private String how = "c"; //c,s,e,r,R,q,Q - private String bool = ""; //a,o,"" - private String part = "f"; //p,f - private boolean invert = false; - - public Query(boolean search, String ooriginal) { - if (search) { - NestedViewsSearchFactory.resetTmpSkip(); - } - this.original = ooriginal.trim(); - String query = null; - try { - Pattern getQuery = Pattern.compile("-.*:"); - Matcher m = getQuery.matcher(original); - m.find(); - query = m.group(); - } catch (Exception ex) { - //no query found is ok - } - if (query != null) { - withoutArguments = original.replace(query, "").trim(); - if (withoutArguments.contains(".*")) { - how = "r"; - } - if (query.contains("X") && search) { - String l = query.replaceAll(".*X", ""); - int n = 1; - if (l.length() > 0) { - try { - l = l.substring(0, 1); - n = Integer.parseInt(l); - } catch (Exception ex) { - //ok - } - } - NestedViewsSearchFactory.setTmpSkip(n); - } - if ((query.contains("D") || query.contains("d")) && search) { - if (query.contains("D")) { - finalFilter = true; - } - searchByNvr = true; - bool = "o"; - if (builds <= 0) { //maybe it was already set - builds = 10; - } - } - if (query.contains("m") && search) { - multiline = true; - } - if (query.contains("P") && search) { - projectInfo = true; - } - if (query.contains("S") && search) { - stats = getNumber(query, "S", 10); - } - if (query.contains("B") && search) { - builds = getNumber(query, "B", 10); - } - if (query.contains("L") && search) { - last = getNumber(query, "L", 0); - if (last == 0) { //it can be set also by user - last = 1234567; - } - } else { - last = -1; - } - if (query.contains("j") || query.contains("v") || query.contains("n") || query.contains("w")) { - where = ""; - } - if (query.contains("j")) { - where += "j"; - } - if (query.contains("v")) { - where += "v"; - } - if (query.contains("n")) { - where += "n"; - } - if (query.contains("w")) { - where += "vn"; - } - if (query.contains("c")) { - how = "c"; - } - if (query.contains("e")) { - how = "e"; - } - if (query.contains("s")) { - how = "s"; - } - if (query.contains("r")) { - how = "r"; - } - if (query.contains("R")) { - how = "R"; - } - if (query.contains("q")) { - how = "q"; - } - if (query.contains("Q")) { - how = "Q"; - } - if (query.contains("a")) { - bool = "a"; - } - if (query.contains("o")) { - bool = "o"; - } - if (query.contains("f")) { - part = "f"; - } - if (query.contains("p")) { - part = "p"; - } - if (query.contains("!")) { - invert = true; - } - } else { - withoutArguments = original; - if (withoutArguments.contains(".*")) { - how = "r"; - } - } - } - - public String getWithoutArguments() { - return withoutArguments; - } - - public String[] getWithoutArgumentsSplit() { - return withoutArguments.split("\\s+"); - } - - - private int getNumber(String query, String switcher, int n) { - String l = query.replaceAll(".*" + switcher, ""); - l = l.replaceAll("[^0-9].*", ""); - try { - n = Integer.parseInt(l); - } catch (Exception ex) { - //ok - } - return n; - } - - public boolean isMultiline() { - return multiline; - } - - public boolean isProjectInfo() { - return projectInfo; - } - - public int getStats() { - return stats; - } - - public int getBuilds() { - return builds; - } - - public int getLast() { - return last; - } - - public String getHow() { - return how; - } - - public boolean isSearchByNvr() { - return searchByNvr; - } - - public boolean isFinalFilter() { - return finalFilter; - } - - public boolean isInvert() { - return invert; - } - - public boolean isNonTrivial(boolean suggesting) { - final String loriginal; - if (original == null) { - loriginal = ""; - } else { - loriginal = original.trim(); - } - final String lwithout; - if (withoutArguments == null) { - lwithout = ""; - } else { - lwithout = withoutArguments.trim(); - } - return !loriginal.equals(".*") - && loriginal.length() >= MIN_LENGTH - && !lwithout.equals(".*") - && lwithout.length() >= MIN_LENGTH; - } - } - - static class NamableWithClass { - private final String name; - private final String fullPath; - private Object item; - - private NamableWithClass(Object item, String name, String fullPath) { - this.item = item; - this.name = name; - this.fullPath = fullPath; - } - - public String getName() { - return name; - } - - public String getFullPath() { - return fullPath; - } - - public String getUsefulName() { - if (item instanceof AbstractProject) { - return name; - } else { - if (item instanceof NestedView) { - return fullPath + "/"; - } else { - return fullPath; - } - } - } - - public String getUrl() { - String rootUrl = Jenkins.get().getRootUrl(); - if (rootUrl.endsWith("/")) { - rootUrl = rootUrl.substring(0, rootUrl.length() - 1); - } - if (item instanceof AbstractProject) { - return rootUrl + "/job/" + name; - } else { - return rootUrl + getFullPath().replace("/", "/view/"); - } - } - - public boolean matches(Query query) { - if (query.isInvert()) { - return !matchesImpl(query); - } else { - return matchesImpl(query); - } - } - - private boolean matchesImpl(Query query) { - String nameOrPath = getFullPath(); - if (query.part.equals("p")) { - nameOrPath = getName(); - } - boolean clazzPass = false; - if (query.where.contains("j") && (item instanceof AbstractProject)) { - clazzPass = true; - } - if (query.where.contains("w") && (item instanceof View || item instanceof NestedView)) { - clazzPass = true; - } - if (query.where.contains("n") && (item instanceof NestedView)) { - clazzPass = true; - } - if (query.where.contains("v") && (item instanceof View) && !(item instanceof NestedView)) { - clazzPass = true; - } - if (!clazzPass) { - return false; - } - if (query.bool.equals("a")) { - String[] parts = query.getWithoutArgumentsSplit(); - for (String part : parts) { - if (!matchSingle(nameOrPath, part, query)) { - return false; - } - } - return true; - } else if (query.bool.equals("o")) { - String[] parts = query.getWithoutArgumentsSplit(); - for (String part : parts) { - if (matchSingle(nameOrPath, part, query)) { - return true; - } - } - return false; - } else { - return matchSingle(nameOrPath, query.withoutArguments, query); - } - } - - private boolean matchSingle(String nameOrPath, String queryOrPart, Query query) { - return matchSingle(nameOrPath, queryOrPart, query.how); - } - - public static boolean matchSingle(String nameOrPath, String queryOrPart, String how) { - if (how.equals("s")) { - return nameOrPath.startsWith(queryOrPart); - } else if (how.equals("e")) { - return nameOrPath.endsWith(queryOrPart); - } else if (how.equals("r")) { - return nameOrPath.matches(queryOrPart); - } else if (how.equals("R")) { - return nameOrPath.matches(".*" + queryOrPart + ".*"); - } else if (how.equals("q")) { - return nameOrPath.equalsIgnoreCase(queryOrPart); - } else if (how.equals("Q")) { - return nameOrPath.equals(queryOrPart); - } else { - return nameOrPath.contains(queryOrPart); - } - } - - public Optional getProject() { - if (item instanceof AbstractProject) { - return Optional.of((AbstractProject) item); - } else { - return Optional.empty(); - } - } - } - private static transient volatile List allCache = new ArrayList(0); private static transient volatile int allTTL = 0; private static transient volatile Date lastRefresh = new Date(0); @@ -412,64 +68,6 @@ private void addViewsRecursively(Collection views, String s, List project, Query query) { - this.searchName = searchName; - this.searchUrl = searchUrl; - if (query != null) { - this.project = new ProjectWrapper(project, query.isMultiline(), query.isProjectInfo(), query.getStats(), query.getLast(), query.getBuilds(), query); - } else { - this.project = new ProjectWrapper(Optional.empty(), false, false, -1, -1, -1, null); - } - } - - @Override - public String toString() { - return searchName; - } - - public String toPlainOldHref() { - return "" + searchName + ""; - } - - @Override - @SuppressFBWarnings(value = {"EQ_COMPARETO_USE_OBJECT_EQUALS"}, justification = "intentional. We check the types when filling the allCached, and the classes have not much in common") - public int compareTo(Object o) { - return this.toString().length() - o.toString().length(); - } - - @Override - public ProjectWrapper getProject() { - return project; - } - } - private final static Logger LOGGER = Logger.getLogger(Search.class.getName()); private List hits = new ArrayList<>(); private Query query; @@ -511,24 +109,6 @@ public SearchResult getSuggestions(final StaplerRequest req, @QueryParameter fin return suggestedItems; } - public static class HelpItem { - private final String key; - private final String description; - - public HelpItem(String key, String description) { - this.key = key; - this.description = description; - } - - public String getKey() { - return key; - } - - public String getDescription() { - return description; - } - } - public List getSearchHelp() throws IOException { List r = new ArrayList<>(); @@ -561,7 +141,9 @@ public List getSearchHelp() throws IOException { r.add(new HelpItem("d", "will search also in DisplayName. In addition it sets `-oB` as OR and Build details are required for it to work. The OR is enforcing you to filter jobs first and name as second")); r.add(new HelpItem("D", - "Same -d, but only projects with at least one matching build will be shown")); + "Same as -d, but only projects with at least one matching build will be shown. -d/-D canbe acompanied by number - algorithm: ")); + r.add(new HelpItem("1: ", "default, what mathced project name, is not used in displayName search")); + r.add(new HelpItem("2: ", "all yor expressions are used in used in displayName search")); return r; } diff --git a/src/main/java/hudson/plugins/nested_view/ProjectWrapper.java b/src/main/java/hudson/plugins/nested_view/ProjectWrapper.java index 88ab39c..4677d1b 100644 --- a/src/main/java/hudson/plugins/nested_view/ProjectWrapper.java +++ b/src/main/java/hudson/plugins/nested_view/ProjectWrapper.java @@ -16,6 +16,8 @@ import hudson.model.Run; import hudson.plugins.nested_view.search.BuildDetails; import hudson.plugins.nested_view.search.LinkableCandidate; +import hudson.plugins.nested_view.search.NamableWithClass; +import hudson.plugins.nested_view.search.Query; public class ProjectWrapper { @@ -25,11 +27,11 @@ public class ProjectWrapper { private final int stats; private final int last; private final int builds; - private final NestedViewsSearch.Query nvrSearch; + private final Query nvrSearch; private final List details; private int matchedBuildsCount; - public ProjectWrapper(Optional project, boolean multiline, boolean projectInfo, int stats, int last, int builds, NestedViewsSearch.Query nvrSearch) { + public ProjectWrapper(Optional project, boolean multiline, boolean projectInfo, int stats, int last, int builds, Query nvrSearch) { this.project = project; this.multiline = multiline; this.projectInfo = projectInfo; @@ -108,7 +110,7 @@ public List createDetails() { if (nvrSearch != null && nvrSearch.isSearchByNvr()) { for (String candidate : nvrSearch.getWithoutArgumentsSplit()) { String displayName = b.getDisplayName(); - boolean matches = NestedViewsSearch.NamableWithClass.matchSingle(displayName, candidate, nvrSearch.getHow()); + boolean matches = NamableWithClass.matchSingle(displayName, candidate, nvrSearch.getHow()); if (!nvrSearch.isInvert()) { if (matches) { buildsList.add(buildToString(b)); @@ -134,7 +136,8 @@ public List createDetails() { } if (stats >= 0) { result.add(new LinkableCandidate(summ.entrySet().stream().map(a -> - a.getKey() == null ? "RUNNING" : a.getKey() + ": " + a.getValue() + "x") + a.getKey() == null ? "RUNNING: " + a.getValue() + "x" + : a.getKey() + ": " + a.getValue() + "x") .collect(Collectors.joining(", ")))); } if (builds >= 0) { diff --git a/src/main/java/hudson/plugins/nested_view/search/HelpItem.java b/src/main/java/hudson/plugins/nested_view/search/HelpItem.java new file mode 100644 index 0000000..a6c901d --- /dev/null +++ b/src/main/java/hudson/plugins/nested_view/search/HelpItem.java @@ -0,0 +1,19 @@ +package hudson.plugins.nested_view.search; + +public class HelpItem { + private final String key; + private final String description; + + public HelpItem(String key, String description) { + this.key = key; + this.description = description; + } + + public String getKey() { + return key; + } + + public String getDescription() { + return description; + } +} diff --git a/src/main/java/hudson/plugins/nested_view/search/NamableWithClass.java b/src/main/java/hudson/plugins/nested_view/search/NamableWithClass.java new file mode 100644 index 0000000..3238bd8 --- /dev/null +++ b/src/main/java/hudson/plugins/nested_view/search/NamableWithClass.java @@ -0,0 +1,133 @@ +package hudson.plugins.nested_view.search; + +import hudson.model.AbstractProject; +import hudson.model.View; +import hudson.plugins.nested_view.NestedView; +import hudson.plugins.nested_view.search.Query; +import jenkins.model.Jenkins; + +import java.util.Optional; + +public class NamableWithClass { + private final String name; + private final String fullPath; + private Object item; + + public NamableWithClass(Object item, String name, String fullPath) { + this.item = item; + this.name = name; + this.fullPath = fullPath; + } + + public String getName() { + return name; + } + + public String getFullPath() { + return fullPath; + } + + public String getUsefulName() { + if (item instanceof AbstractProject) { + return name; + } else { + if (item instanceof NestedView) { + return fullPath + "/"; + } else { + return fullPath; + } + } + } + + public String getUrl() { + String rootUrl = Jenkins.get().getRootUrl(); + if (rootUrl.endsWith("/")) { + rootUrl = rootUrl.substring(0, rootUrl.length() - 1); + } + if (item instanceof AbstractProject) { + return rootUrl + "/job/" + name; + } else { + return rootUrl + getFullPath().replace("/", "/view/"); + } + } + + public boolean matches(Query query) { + if (query.isInvert()) { + return !matchesImpl(query); + } else { + return matchesImpl(query); + } + } + + private boolean matchesImpl(Query query) { + String nameOrPath = getFullPath(); + if (query.getPart().equals("p")) { + nameOrPath = getName(); + } + boolean clazzPass = false; + if (query.getWhere().contains("j") && (item instanceof AbstractProject)) { + clazzPass = true; + } + if (query.getWhere().contains("w") && (item instanceof View || item instanceof NestedView)) { + clazzPass = true; + } + if (query.getWhere().contains("n") && (item instanceof NestedView)) { + clazzPass = true; + } + if (query.getWhere().contains("v") && (item instanceof View) && !(item instanceof NestedView)) { + clazzPass = true; + } + if (!clazzPass) { + return false; + } + if (query.getBool().equals("a")) { + String[] parts = query.getWithoutArgumentsSplit(); + for (String part : parts) { + if (!matchSingle(nameOrPath, part, query)) { + return false; + } + } + return true; + } else if (query.getBool().equals("o")) { + String[] parts = query.getWithoutArgumentsSplit(); + for (String part : parts) { + if (matchSingle(nameOrPath, part, query)) { + return true; + } + } + return false; + } else { + return matchSingle(nameOrPath, query.getWithoutArguments(), query); + } + } + + private boolean matchSingle(String nameOrPath, String queryOrPart, Query query) { + return matchSingle(nameOrPath, queryOrPart, query.getHow()); + } + + public static boolean matchSingle(String nameOrPath, String queryOrPart, String how) { + if (how.equals("s")) { + return nameOrPath.startsWith(queryOrPart); + } else if (how.equals("e")) { + return nameOrPath.endsWith(queryOrPart); + } else if (how.equals("r")) { + return nameOrPath.matches(queryOrPart); + } else if (how.equals("R")) { + return nameOrPath.matches(".*" + queryOrPart + ".*"); + } else if (how.equals("q")) { + return nameOrPath.equalsIgnoreCase(queryOrPart); + } else if (how.equals("Q")) { + return nameOrPath.equals(queryOrPart); + } else { + return nameOrPath.contains(queryOrPart); + } + } + + public Optional getProject() { + if (item instanceof AbstractProject) { + return Optional.of((AbstractProject) item); + } else { + return Optional.empty(); + } + } +} diff --git a/src/main/java/hudson/plugins/nested_view/search/NestedViewsSearchResult.java b/src/main/java/hudson/plugins/nested_view/search/NestedViewsSearchResult.java new file mode 100644 index 0000000..72d4fb2 --- /dev/null +++ b/src/main/java/hudson/plugins/nested_view/search/NestedViewsSearchResult.java @@ -0,0 +1,67 @@ +package hudson.plugins.nested_view.search; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import hudson.model.AbstractProject; +import hudson.plugins.nested_view.ProjectWrapper; +import hudson.search.SearchIndex; +import hudson.search.SearchItem; + +import java.util.Optional; + +public class NestedViewsSearchResult implements SearchItem, Comparable, ExtendedSearch { + private final String searchName; + private final String searchUrl; + private final ProjectWrapper project; + + public boolean isStillValid() { + if (project != null) { + return project.isStillValid(); + } + return true; + } + + @Override + public String getSearchName() { + return searchName; + } + + @Override + public String getSearchUrl() { + return searchUrl; + } + + @Override + public SearchIndex getSearchIndex() { + return null; + } + + public NestedViewsSearchResult(String searchName, String searchUrl, Optional project, Query query) { + this.searchName = searchName; + this.searchUrl = searchUrl; + if (query != null) { + this.project = new ProjectWrapper(project, query.isMultiline(), query.isProjectInfo(), query.getStats(), query.getLast(), query.getBuilds(), query); + } else { + this.project = new ProjectWrapper(Optional.empty(), false, false, -1, -1, -1, null); + } + } + + @Override + public String toString() { + return searchName; + } + + public String toPlainOldHref() { + return "" + searchName + ""; + } + + @Override + @SuppressFBWarnings(value = {"EQ_COMPARETO_USE_OBJECT_EQUALS"}, justification = "intentional. We check the types when filling the allCached, and the classes have not much in common") + public int compareTo(Object o) { + return this.toString().length() - o.toString().length(); + } + + @Override + public ProjectWrapper getProject() { + return project; + } +} diff --git a/src/main/java/hudson/plugins/nested_view/search/Query.java b/src/main/java/hudson/plugins/nested_view/search/Query.java new file mode 100644 index 0000000..77dcc83 --- /dev/null +++ b/src/main/java/hudson/plugins/nested_view/search/Query.java @@ -0,0 +1,240 @@ +package hudson.plugins.nested_view.search; + +import hudson.plugins.nested_view.NestedViewsSearchFactory; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Query { + + private static final int MIN_LENGTH = 2; + + private final String original; + private final String withoutArguments; + + private boolean multiline; + private boolean searchByNvr; + private boolean finalFilter; + private boolean projectInfo; + private int stats = -1; + private int builds = -1; + private int last = -1; + + public String getOriginal() { + return original; + } + + public String getWhere() { + return where; + } + + public String getBool() { + return bool; + } + + public String getPart() { + return part; + } + + private String where = "vnj"; //v,n,j + private String how = "c"; //c,s,e,r,R,q,Q + private String bool = ""; //a,o,"" + private String part = "f"; //p,f + private boolean invert = false; + + public Query(boolean search, String ooriginal) { + if (search) { + NestedViewsSearchFactory.resetTmpSkip(); + } + this.original = ooriginal.trim(); + String query = null; + try { + Pattern getQuery = Pattern.compile("-.*:"); + Matcher m = getQuery.matcher(original); + m.find(); + query = m.group(); + } catch (Exception ex) { + //no query found is ok + } + if (query != null) { + withoutArguments = original.replace(query, "").trim(); + if (withoutArguments.contains(".*")) { + how = "r"; + } + if (query.contains("X") && search) { + String l = query.replaceAll(".*X", ""); + int n = 1; + if (l.length() > 0) { + try { + l = l.substring(0, 1); + n = Integer.parseInt(l); + } catch (Exception ex) { + //ok + } + } + NestedViewsSearchFactory.setTmpSkip(n); + } + if ((query.contains("D") || query.contains("d")) && search) { + if (query.contains("D")) { + finalFilter = true; + } + searchByNvr = true; + bool = "o"; + if (builds <= 0) { //maybe it was already set + builds = 10; + } + } + if (query.contains("m") && search) { + multiline = true; + } + if (query.contains("P") && search) { + projectInfo = true; + } + if (query.contains("S") && search) { + stats = getNumber(query, "S", 10); + } + if (query.contains("B") && search) { + builds = getNumber(query, "B", 10); + } + if (query.contains("L") && search) { + last = getNumber(query, "L", 0); + if (last == 0) { //it can be set also by user + last = 1234567; + } + } else { + last = -1; + } + if (query.contains("j") || query.contains("v") || query.contains("n") || query.contains("w")) { + where = ""; + } + if (query.contains("j")) { + where += "j"; + } + if (query.contains("v")) { + where += "v"; + } + if (query.contains("n")) { + where += "n"; + } + if (query.contains("w")) { + where += "vn"; + } + if (query.contains("c")) { + how = "c"; + } + if (query.contains("e")) { + how = "e"; + } + if (query.contains("s")) { + how = "s"; + } + if (query.contains("r")) { + how = "r"; + } + if (query.contains("R")) { + how = "R"; + } + if (query.contains("q")) { + how = "q"; + } + if (query.contains("Q")) { + how = "Q"; + } + if (query.contains("a")) { + bool = "a"; + } + if (query.contains("o")) { + bool = "o"; + } + if (query.contains("f")) { + part = "f"; + } + if (query.contains("p")) { + part = "p"; + } + if (query.contains("!")) { + invert = true; + } + } else { + withoutArguments = original; + if (withoutArguments.contains(".*")) { + how = "r"; + } + } + } + + public String getWithoutArguments() { + return withoutArguments; + } + + public String[] getWithoutArgumentsSplit() { + return withoutArguments.split("\\s+"); + } + + + private int getNumber(String query, String switcher, int n) { + String l = query.replaceAll(".*" + switcher, ""); + l = l.replaceAll("[^0-9].*", ""); + try { + n = Integer.parseInt(l); + } catch (Exception ex) { + //ok + } + return n; + } + + public boolean isMultiline() { + return multiline; + } + + public boolean isProjectInfo() { + return projectInfo; + } + + public int getStats() { + return stats; + } + + public int getBuilds() { + return builds; + } + + public int getLast() { + return last; + } + + public String getHow() { + return how; + } + + public boolean isSearchByNvr() { + return searchByNvr; + } + + public boolean isFinalFilter() { + return finalFilter; + } + + public boolean isInvert() { + return invert; + } + + public boolean isNonTrivial(boolean suggesting) { + final String loriginal; + if (original == null) { + loriginal = ""; + } else { + loriginal = original.trim(); + } + final String lwithout; + if (withoutArguments == null) { + lwithout = ""; + } else { + lwithout = withoutArguments.trim(); + } + return !loriginal.equals(".*") + && loriginal.length() >= MIN_LENGTH + && !lwithout.equals(".*") + && lwithout.length() >= MIN_LENGTH; + } +}