/*
* The MIT License
*
* Copyright 2011 Sony Ericsson Mobile Communications. All rights reserved.
* Copyright 2012 Sony Mobile Communications AB. All rights reserved.
*
* 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 com.sonyericsson.hudson.plugins.metadata.model.values;
import com.sonyericsson.hudson.plugins.metadata.Messages;
import com.sonyericsson.hudson.plugins.metadata.model.JsonUtils;
import com.sonyericsson.hudson.plugins.metadata.model.MetadataContainer;
import com.sonyericsson.hudson.plugins.metadata.model.MetadataParent;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import hudson.EnvVars;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.model.Descriptor;
import hudson.model.Hudson;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import static com.sonyericsson.hudson.plugins.metadata.Constants.REQUEST_ATTR_METADATA_CONTAINER;
import static com.sonyericsson.hudson.plugins.metadata.Constants.SERIALIZATION_ALIAS_TREE;
import static com.sonyericsson.hudson.plugins.metadata.model.JsonUtils.CHILDREN;
import static com.sonyericsson.hudson.plugins.metadata.model.JsonUtils.DESCRIPTION;
import static com.sonyericsson.hudson.plugins.metadata.model.JsonUtils.NAME;
import static com.sonyericsson.hudson.plugins.metadata.model.JsonUtils.GENERATED;
import static com.sonyericsson.hudson.plugins.metadata.model.JsonUtils.EXPOSED;
import static com.sonyericsson.hudson.plugins.metadata.model.JsonUtils.checkRequiredJsonAttribute;
/**
* Meta data containing other meta data values. Used to create tree structures of data.
*
* @author Robert Sandell <robert.sandell@sonyericsson.com>
*/
@edu.umd.cs.findbugs.annotations.SuppressWarnings(
value = "UG_SYNC_SET_UNSYNC_GET",
justification = "It is synchronized")
@XStreamAlias(SERIALIZATION_ALIAS_TREE)
public class TreeNodeMetadataValue extends AbstractMetadataValue implements MetadataParent<MetadataValue>, Cloneable {
/**
* Constant for hashcode.
*/
private static final int HASH_CONST = 32;
private List<MetadataValue> children;
/**
* Standard Constructor.
*
* @param name the name.
* @param description the description
* @param children its children.
* @param exposedToEnvironment if this value should be exposed to the build as an
* environment variable.
*/
@DataBoundConstructor
public TreeNodeMetadataValue(String name, String description, List<MetadataValue> children,
boolean exposedToEnvironment) {
super(name, description, exposedToEnvironment);
setChildren(children);
}
/**
* Standard Constructor.
*
* @param name the name
* @param description the description.
* @param children its children.
*/
public TreeNodeMetadataValue(String name, String description, List<MetadataValue> children) {
super(name, description, false);
setChildren(children);
}
/**
* Standard Constructor.
*
* @param name the name
* @param description the description.
*/
public TreeNodeMetadataValue(String name, String description) {
super(name, description);
this.children = new LinkedList<MetadataValue>();
}
/**
* Standard Constructor.
*
* @param name the name.
* @param children its children.
*/
public TreeNodeMetadataValue(String name, List<MetadataValue> children) {
super(name);
setChildren(children);
}
/**
* Standard Constructor.
*
* @param name the name.
*/
public TreeNodeMetadataValue(String name) {
super(name);
this.children = new LinkedList<MetadataValue>();
}
/**
* Sets {@link #children} and sets their parent to this.
*
* @param children the children.
*/
private synchronized void setChildren(List<MetadataValue> children) {
this.children = children;
if (this.children != null) {
for (MetadataValue value : this.children) {
value.setParent(this);
}
} else {
this.children = new LinkedList<MetadataValue>();
}
}
@Override
public synchronized List<MetadataValue> getValue() {
if (children == null) {
children = new LinkedList<MetadataValue>();
}
return children;
}
/**
* Returns the child with the given name, or null if there is none. comparison is case insensitive.
*
* @param name the name to search for.
* @return the value.
*/
@Override
public synchronized MetadataValue getChild(String name) {
return ParentUtil.getChildValue(children, name);
}
@Override
public synchronized int indexOf(String name) {
return ParentUtil.getChildIndex(children, name);
}
@Override
public synchronized MetadataValue setChild(int index, MetadataValue value) {
return children.set(index, value);
}
@Override
public synchronized Collection<MetadataValue> addChild(MetadataValue value) {
return ParentUtil.addChildValue(this, children, value);
}
@Override
public synchronized Collection<MetadataValue> addChildren(Collection<MetadataValue> values) {
return ParentUtil.addChildValues(this, children, values);
}
@Override
public synchronized Collection<MetadataValue> getChildren() {
return getValue();
}
@Override
public Collection<String> getChildNames() {
return ParentUtil.getChildNames(this);
}
@Override
public synchronized void replacementOf(MetadataValue old) {
if (old instanceof MetadataParent) {
MetadataParent<MetadataValue> oldParent = (MetadataParent<MetadataValue>)old;
for (MetadataValue child : oldParent.getChildren()) {
if (getChild(child.getName()) == null) {
addChild(child);
}
}
}
}
@Override
public Descriptor<AbstractMetadataValue> getDescriptor() {
return Hudson.getInstance().getDescriptorByType(TreeNodeMetaDataValueDescriptor.class);
}
@Override
public synchronized JSONObject toJson() {
JSONObject obj = toAbstractJson();
JSONArray array = new JSONArray();
for (MetadataValue child : children) {
array.add(child.toJson());
}
obj.put(CHILDREN, array);
return obj;
}
@Override
public boolean requiresReplacement() {
return false;
}
@Override
public void addEnvironmentVariables(EnvVars variables, boolean exposeAll) {
for (MetadataValue v : getChildren()) {
if (isExposedToEnvironment()) {
v.addEnvironmentVariables(variables, true);
} else {
v.addEnvironmentVariables(variables, exposeAll);
}
}
}
@Override
public TreeNodeMetadataValue clone() throws CloneNotSupportedException {
TreeNodeMetadataValue tree = (TreeNodeMetadataValue)super.clone();
List<MetadataValue> clonedChildren = new LinkedList<MetadataValue>();
for (MetadataValue child : children) {
MetadataValue clonedChild = child.clone();
clonedChild.setParent(tree);
clonedChildren.add(clonedChild);
}
tree.setChildren(clonedChildren);
return tree;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final TreeNodeMetadataValue other = (TreeNodeMetadataValue)obj;
Collection<MetadataValue> otherChildren = other.getChildren();
if (children.size() != otherChildren.size()) {
return false;
}
for (MetadataValue child : getChildren()) {
MetadataValue otherChild = other.getChild(child.getName());
if (otherChild == null) {
return false;
} else if (!child.equals(otherChild)) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
return HASH_CONST;
}
@Override
public int compareTo(Object t) {
//implementation @pending
return -1;
}
/**
* Descriptor for {@link TreeNodeMetadataValue}s.
*/
@Extension
public static class TreeNodeMetaDataValueDescriptor extends AbstractMetaDataValueDescriptor {
@Override
public String getDisplayName() {
//TODO Find a better display name.
return Messages.TreeNodeMetadataValue_DisplayName();
}
@Override
public String getJsonType() {
return SERIALIZATION_ALIAS_TREE;
}
@Override
public MetadataValue fromJson(JSONObject json, MetadataContainer<MetadataValue> container)
throws JsonUtils.ParseException {
checkRequiredJsonAttribute(json, NAME);
List<MetadataValue> children = new LinkedList<MetadataValue>();
if (json.has(CHILDREN)) {
JSONArray array = json.getJSONArray(CHILDREN);
for (int i = 0; i < array.size(); i++) {
JSONObject obj = array.getJSONObject(i);
children.add(JsonUtils.toValue(obj, container));
}
}
TreeNodeMetadataValue value = new TreeNodeMetadataValue(
json.getString(NAME), json.optString(DESCRIPTION), children);
if (json.has(EXPOSED)) {
value.setExposeToEnvironment(json.getBoolean(EXPOSED));
}
if (json.has(GENERATED)) {
value.setGenerated(json.getBoolean(GENERATED));
} else {
//TODO Decide if this is really what should be done.
value.setGenerated(true);
}
return value;
}
/**
* Returns all the registered meta data descriptors. For use in a hetero-list.
*
* @param request the current request.
* @return the descriptors.
*/
public List<AbstractMetaDataValueDescriptor> getValueDescriptors(StaplerRequest request) {
Object containerObj = request.getAttribute(REQUEST_ATTR_METADATA_CONTAINER);
Descriptor container = null;
if ((containerObj != null) && containerObj instanceof Descriptor) {
container = (Descriptor)containerObj;
}
List<AbstractMetaDataValueDescriptor> list = new LinkedList<AbstractMetaDataValueDescriptor>();
ExtensionList<AbstractMetaDataValueDescriptor> extensionList =
Hudson.getInstance().getExtensionList(AbstractMetaDataValueDescriptor.class);
for (AbstractMetaDataValueDescriptor d : extensionList) {
if (d.appliesTo(container)) {
list.add(d);
}
}
return list;
}
}
}