/*
* The MIT License
*
* Copyright (c) 2013, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.branch;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.Util;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import jenkins.scm.api.SCMHead;
import org.apache.commons.lang.StringUtils;
import org.apache.tools.ant.types.selectors.SelectorUtils;
import org.kohsuke.stapler.DataBoundConstructor;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Allows named branches to get different properties from the rest.
*
* @author Stephen Connolly
*/
public class NamedExceptionsBranchPropertyStrategy extends BranchPropertyStrategy {
/**
* The properties that all non-exception {@link SCMHead}s will get.
*/
@NonNull
private final List<BranchProperty> defaultProperties;
/**
* The configured exceptions.
*/
@NonNull
private final List<Named> namedExceptions;
/**
* Stapler's constructor.
*
* @param defaultProperties the properties.
* @param namedExceptions the named exceptions.
*/
@DataBoundConstructor
public NamedExceptionsBranchPropertyStrategy(@CheckForNull BranchProperty[] defaultProperties,
@CheckForNull Named[] namedExceptions) {
this.defaultProperties =
defaultProperties == null ? Collections.<BranchProperty>emptyList() : Arrays.asList(defaultProperties);
this.namedExceptions =
namedExceptions == null ? Collections.<Named>emptyList() : Arrays.asList(namedExceptions);
}
/**
* Gets the default properties.
*
* @return the default properties.
*/
@NonNull
@SuppressWarnings("unused")// by stapler
public List<BranchProperty> getDefaultProperties() {
return defaultProperties;
}
/**
* Gets the named exceptions to the defaults.
*
* @return the named exceptions to the defaults.
*/
@NonNull
@SuppressWarnings("unused")// by stapler
public List<Named> getNamedExceptions() {
return namedExceptions;
}
/**
* {@inheritDoc}
*/
@NonNull
@Override
public List<BranchProperty> getPropertiesFor(SCMHead head) {
for (Named named : namedExceptions) {
if (named.isMatch(head)) {
return new ArrayList<BranchProperty>(named.getProps());
}
}
return new ArrayList<BranchProperty>(defaultProperties);
}
/**
* Our {@link BranchPropertyStrategyDescriptor}.
*/
@Extension
@SuppressWarnings("unused") // by jenkins
public static class DescriptorImpl extends BranchPropertyStrategyDescriptor {
/**
* {@inheritDoc}
*/
@Override
public String getDisplayName() {
return Messages.NamedExceptionsBranchPropertyStrategy_DisplayName();
}
}
/**
* Holds the specific named exception details.
*/
public static class Named extends AbstractDescribableImpl<Named> {
/**
* The properties that all {@link SCMHead}s will get.
*/
@NonNull
private final List<BranchProperty> props;
/**
* The name to match
*/
@NonNull
private final String name;
/**
* Constructor
*
* @param name the names to match.
* @param props the properties that the matching branches will get.
*/
@SuppressWarnings("unused") // via stapler
@DataBoundConstructor
public Named(@CheckForNull String name, @CheckForNull BranchProperty[] props) {
this.name = Util.fixNull(name);
this.props = props == null ? Collections.<BranchProperty>emptyList() : Arrays.asList(props);
}
/**
* Returns the exception properties.
*
* @return the exception properties.
*/
@NonNull
public List<BranchProperty> getProps() {
return props;
}
/**
* Returns the name(s) to match.
*
* @return the name(s) to match.
*/
@NonNull
public String getName() {
return name;
}
/**
* Returns {@code true} if the head is a match.
*
* @param head the head.
* @return {@code true} if the head is a match.
*/
public boolean isMatch(@NonNull SCMHead head) {
return isMatch(head.getName(), this.name);
}
/**
* Returns {@code true} if and only if the branch name matches one of the name(s).
*
* @param branchName the branch name.
* @param names the name(s) that are valid to match against.
* @return {@code true} if and only if the branch name matches one of the name(s).
*/
public static boolean isMatch(String branchName, String names) {
for (String name : StringUtils.split(names, ",")) {
name = name.trim();
boolean invertMatch;
if (name.startsWith("!")) {
name = name.substring(1);
invertMatch = true;
} else if (name.startsWith("\\!") || name.startsWith("\\\\!")) {
// provide an escape hatch
name = name.substring(1);
invertMatch = false;
} else {
invertMatch = false;
}
boolean match;
if (name.indexOf('*') == -1 && name.indexOf('?') == -1) {
match = name.equalsIgnoreCase(branchName);
} else {
name = name.replace('\\', File.separatorChar).replace('/', File.separatorChar);
branchName = branchName.replace('\\', File.separatorChar).replace('/', File.separatorChar);
match = SelectorUtils.matchPath(name, branchName, false);
}
if (invertMatch ? !match : match) {
return true;
}
}
return false;
}
/**
* Our {@link hudson.model.Descriptor}
*/
@Extension
@SuppressWarnings("unused") // instantiated by Jenkins.
public static class DescriptorImpl extends Descriptor<Named> {
/**
* {@inheritDoc}
*/
@Override
public String getDisplayName() {
return "Named exception";
}
}
}
}