/*
* Copyright 2013 Hewlett-Packard Development Company, L.P
*
* Licensed 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 com.hp.alm.ali.idea.model;
import com.hp.alm.ali.idea.content.detail.AttachmentTableLoader;
import com.hp.alm.ali.idea.entity.EntityQuery;
import com.hp.alm.ali.idea.entity.EntityRef;
import com.hp.alm.ali.idea.action.ActionUtil;
import com.hp.alm.ali.idea.cfg.EntityFields;
import com.hp.alm.ali.idea.content.AliContent;
import com.hp.alm.ali.idea.content.defect.DefectsContent;
import com.hp.alm.ali.idea.content.requirements.RequirementsContent;
import com.hp.alm.ali.idea.content.settings.SettingsContent;
import com.hp.alm.ali.idea.content.detail.DetailContent;
import com.hp.alm.ali.idea.content.detail.LinksTableLoader;
import com.hp.alm.ali.idea.content.detail.TableContent;
import com.hp.alm.ali.idea.entity.edit.EntityEditStrategy;
import com.hp.alm.ali.idea.entity.edit.EntityEditStrategyImpl;
import com.hp.alm.ali.idea.entity.edit.MayaLock;
import com.hp.alm.ali.idea.entity.edit.LockingStrategy;
import com.hp.alm.ali.idea.filter.FilterChooser;
import com.hp.alm.ali.idea.model.parser.AuditList;
import com.hp.alm.ali.idea.model.type.BuildDurationType;
import com.hp.alm.ali.idea.model.type.BuildStatusType;
import com.hp.alm.ali.idea.model.type.DefectLinkIdType;
import com.hp.alm.ali.idea.model.type.DefectLinkNameType;
import com.hp.alm.ali.idea.model.type.DefectLinkStatusType;
import com.hp.alm.ali.idea.model.type.DefectLinkTypeType;
import com.hp.alm.ali.idea.model.type.FileSizeType;
import com.hp.alm.ali.idea.model.type.PercentType;
import com.hp.alm.ali.idea.model.type.PlainTextType;
import com.hp.alm.ali.idea.model.type.RequirementTypeType;
import com.hp.alm.ali.idea.model.type.TargetReleaseCycleType;
import com.hp.alm.ali.idea.model.type.TargetReleaseType;
import com.hp.alm.ali.idea.rest.RestService;
import com.hp.alm.ali.idea.services.EntityService;
import com.hp.alm.ali.idea.ui.chooser.EntityChooser;
import com.hp.alm.ali.idea.ui.chooser.FlatChooser;
import com.hp.alm.ali.idea.ui.chooser.HierarchicalChooser;
import com.hp.alm.ali.idea.ui.combo.LazyComboBoxModel;
import com.hp.alm.ali.idea.ui.combo.ProjectUsersComboBoxModel;
import com.intellij.openapi.actionSystem.ActionToolbar;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.IconLoader;
import javax.swing.JComponent;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class MayaStrategy implements ServerStrategy {
private static Map<String, String> developmentAliasMap;
static {
developmentAliasMap = new HashMap<String, String>();
developmentAliasMap.put("defect", "defect");
developmentAliasMap.put("requirement", "requirement");
developmentAliasMap.put("build-instance", "build-instance");
}
private static Map<String, String> hierarchicalPath;
static {
hierarchicalPath = new HashMap<String, String>();
hierarchicalPath.put("release-folder", "path");
hierarchicalPath.put("test-set-folder", "hierarchical-path");
hierarchicalPath.put("favorite-folder", "path");
hierarchicalPath.put("requirement", "hierarchical-path");
}
private static Map<String, EntityQuery> mayaFilter;
static {
mayaFilter = new HashMap<String, EntityQuery>();
addMayaFilter(requirementFilter());
addMayaFilter(defectFilter());
addMayaFilter(buildFilter());
addMayaFilter(buildTypeFilter());
addMayaFilter(changesetFilter());
addMayaFilter(defectLinkFilter());
}
private static Map<String, EntityFields> mayaFields;
static {
mayaFields = new HashMap<String, EntityFields>();
mayaFields.put("requirement", requirementFields());
mayaFields.put("defect", defectFields());
mayaFields.put("build-instance", buildFields());
}
protected Project project;
protected RestService restService;
protected EntityService entityService;
protected AliStrategyUtil aliStrategyUtil;
private EntityEditStrategy entityEditStrategy;
public MayaStrategy(Project project, RestService restService) {
this.project = project;
this.restService = restService;
entityEditStrategy = project.getComponent(EntityEditStrategyImpl.class);
entityService = project.getComponent(EntityService.class);
aliStrategyUtil = project.getComponent(AliStrategyUtil.class);
}
@Override
public EntityQuery preProcess(EntityQuery query) {
EntityQuery clone = query.clone();
// suppress client-side virtual fields
LinkedHashMap<String,Integer> columns = clone.getColumns();
boolean hasVirtual = false;
for(Iterator<String> it = columns.keySet().iterator(); it.hasNext(); ) {
if(it.next().startsWith("virtual:")) {
it.remove();
hasVirtual = true;
}
}
if(hasVirtual) {
clone.setColumns(columns);
}
if(!clone.getColumns().isEmpty()) {
// when explicitly listing columns explicitly, add those implicitly required
if("build-artifact".equals(query.getEntityType())) {
// mandatory otherwise server fails to respond (needed for virtual field calculation)
clone.addColumn("build", 1);
clone.addColumn("path", 1);
} else if("attachment".equals(query.getEntityType())) {
// following are needed for the action context construction
clone.addColumn("name", 1);
clone.addColumn("parent-type", 1);
clone.addColumn("parent-id", 1);
clone.addColumn("file-size", 1); // this one should be queried
} else if("defect-link".equals(query.getEntityType())) {
// following are needed for the action context construction
clone.addColumn("first-endpoint-id", 1);
clone.addColumn("second-endpoint-id", 1);
clone.addColumn("second-endpoint-type", 1);
clone.addColumn("second-endpoint-name", 1);
clone.addColumn("second-endpoint-status", 1);
} else if("changeset".equals(query.getEntityType())) {
// needed for ShowAffectedPathsAction
clone.addColumn("rev", 1);
} else if("changeset-file".equals(query.getEntityType())) {
// workaround for ALM server issue: query fails unless following fields are present
clone.addColumn("path", 1);
clone.addColumn("revision", 1);
clone.addColumn("operation", 1);
clone.addColumn("file-type", 1);
}
}
return clone;
}
@Override
public LazyComboBoxModel getUserModel() {
return new ProjectUsersComboBoxModel(project);
}
@Override
public LockingStrategy getLockingStrategy() {
return new MayaLock(project);
}
@Override
public EntityEditStrategy getEntityEditStrategy() {
return entityEditStrategy;
}
@Override
public List<DetailContent> getDetailContent(Entity entity) {
List<DetailContent> ret = new ArrayList<DetailContent>();
if("requirement".equals(entity.getType())) {
ret.add(attachmentTable(entity));
}
if("defect".equals(entity.getType())) {
ret.add(attachmentTable(entity));
ret.add(linkedTable(entity));
}
if("build-instance".equals(entity.getType())) {
EntityQuery detailQuery = new EntityQuery("build-artifact");
detailQuery.setValue("build", String.valueOf(entity.getId()));
ret.add(aliStrategyUtil.detailTable(entity, detailQuery, "Artifacts", IconLoader.getIcon("/nodes/artifact.png"), null));
}
return ret;
}
private TableContent attachmentTable(Entity entity) {
EntityQuery attachmentQuery = new EntityQuery("attachment");
final EntityRef parent = new EntityRef(entity);
attachmentQuery.setParent(parent);
attachmentQuery.setValue("parent-id", String.valueOf(entity.getId()));
attachmentQuery.setValue("parent-type", entity.getType());
ActionToolbar actionToolbar = ActionUtil.createActionToolbar("hpali.attachment", "detail", true);
return new TableContent(project, entity, "Attachments", IconLoader.getIcon("/attachment_16.png"), actionToolbar, new AttachmentTableLoader(project, entity, attachmentQuery, attachmentQuery.getPropertyMap().keySet()));
}
protected TableContent linkedTable(Entity entity) {
EntityQuery linkQuery = new EntityQuery("defect-link");
final EntityRef parent = new EntityRef(entity);
linkQuery.setParent(parent);
ActionToolbar actionToolbar = ActionUtil.createActionToolbar("hpali.defect-link", "detail", true);
return new TableContent(project, entity, "Links", IconLoader.getIcon("/actions/erDiagram.png"), actionToolbar, new LinksTableLoader(project, entity, linkQuery, linkedTableHiddenFields()));
}
protected Set<String> linkedTableHiddenFields() {
HashSet<String> set = new HashSet<String>();
set.add("second-endpoint-id");
set.add("second-endpoint-name");
set.add("second-endpoint-type");
set.add("second-endpoint-status");
set.add("first-endpoint-id");
return set;
}
@Override
public String getDevelopmentAlias(String entity) {
return developmentAliasMap.get(entity);
}
@Override
public EntityQuery getDefaultTableFilter(String entityType) {
EntityQuery query = new EntityQuery(entityType);
EntityQuery defaults = mayaFilter.get(entityType);
if(defaults != null) {
query.copyFrom(defaults);
} else {
query.addColumn("id", 50);
query.addColumn("name", 100);
query.addColumn("description", 150);
}
return query;
}
@Override
public List<String> getDefaultFields(String entityType) {
EntityFields defaults = mayaFields.get(entityType);
if(defaults != null) {
return defaults.getColumns();
} else {
return Arrays.asList("id", "name", "description");
}
}
@Override
public void fixMetadata(Metadata metadata) {
fixMetadataInherited(metadata);
// following are supported in Horizon (don't inherit them)
if("defect-link".equals(metadata.getEntityType())) {
metadata.getField("link-type").setCanFilter(false);
metadata.getField("comment").setCanFilter(false);
}
}
protected void fixMetadataInherited(Metadata metadata) {
if("build-instance".equals(metadata.getEntityType())) {
// build status is enum although it is not defined as such
metadata.getField("status").setClazz(BuildStatusType.class);
// present human readable value instead of non-descriptive number
metadata.getField("duration").setClazz(BuildDurationType.class);
// percent value
metadata.getField("test-success").setClazz(PercentType.class);
metadata.getField("release").setReferencedType("release");
metadata.getField("type").setReferencedType("build-type");
}
if("build-artifact".equals(metadata.getEntityType())) {
// reference to enclosing build instance
metadata.getField("build").setReferencedType("build-instance");
}
if("requirement".equals(metadata.getEntityType())) {
metadata.getField("type-id").setClazz(RequirementTypeType.class);
metadata.getField("target-rel").setReferencedType("release");
metadata.getField("target-rel").setClazz(TargetReleaseType.class);
metadata.getField("target-rcyc").setReferencedType("release-cycle");
metadata.getField("target-rcyc").setClazz(TargetReleaseCycleType.class);
}
if("defect".equals(metadata.getEntityType())) {
metadata.getField("target-rel").setReferencedType("release");
metadata.getField("target-rcyc").setReferencedType("release-cycle");
metadata.getField("detected-in-rel").setReferencedType("release");
metadata.getField("detected-in-rcyc").setReferencedType("release-cycle");
}
if("defect-link".equals(metadata.getEntityType())) {
metadata.getField("second-endpoint-type").setCanFilter(false);
// avoid resolving the entity ID field, there is dedicated column for it
metadata.getField("first-endpoint-id").setReferencedType(null);
// avoid volatile columns that change semantics based on query context
metadata.removeField("second-endpoint-name");
metadata.removeField("second-endpoint-status");
// status of linked entity
Field statusField = new Field("virtual:linked-status", "Entity Status");
statusField.setClazz(DefectLinkStatusType.class);
metadata.addField(statusField);
// name of of linked entity
Field nameField = new Field("virtual:linked-name", "Entity Name");
nameField.setClazz(DefectLinkNameType.class);
metadata.addField(nameField);
// ID of of linked entity
Field idField = new Field("virtual:linked-id", "Entity ID");
idField.setClazz(DefectLinkIdType.class);
metadata.addField(idField);
// type of of linked entity
Field typeField = new Field("virtual:linked-type", "Entity Type");
typeField.setClazz(DefectLinkTypeType.class);
metadata.addField(typeField);
}
if("attachment".equals(metadata.getEntityType())) {
metadata.getField("description").setClazz(PlainTextType.class);
metadata.getField("file-size").setClazz(FileSizeType.class);
}
}
@Override
public List<String> getCompoundEntityTypes(String entityType) {
return Collections.emptyList();
}
@Override
public List<Relation> getRelationList(String entityType) {
if("defect".equals(entityType)) {
return relationList("requirement", "test-set");
}
if("requirement".equals(entityType)) {
return relationList("release");
}
return new ArrayList<Relation>();
}
@Override
public List<AliContent> getSupportedContent() {
return new ArrayList<AliContent>(Arrays.asList(
DefectsContent.getInstance(),
RequirementsContent.getInstance(),
SettingsContent.getInstance()));
}
@Override
public String getFieldAlias(String entityType, String property) {
return property;
}
@Override
public String getHierarchicalPathProperty(String entityType) {
return hierarchicalPath.get(entityType);
}
@Override
public boolean hasSecondLevelDefectLink() {
return true;
}
@Override
public boolean canEditAttachmentFileName() {
return false;
}
@Override
public List<String> getDefectLinkColumns() {
return Arrays.asList(
"first-endpoint-id",
"second-endpoint-id",
"second-endpoint-type",
"second-endpoint-status",
"second-endpoint-name",
"comment");
}
@Override
public String getCheckinPrefix(EntityRef entityRef) {
return entityRef.toString() + ":";
}
@Override
public AuditList getEntityAudit(Entity entity) {
InputStream is = restService.getForStream("{0}s/{1}/audits", entity.getType(), entity.getId());
return AuditList.create(is);
}
@Override
public void beforeConnectionHandler() {
}
@Override
public JComponent getConnectionComponent() {
return null;
}
@Override
public FilterChooser getFilterChooser(String entityType, boolean multiple, boolean idSelection, boolean acceptEmpty, String value) {
EntityChooser dialog;
if(Metadata.getChildEntity(entityType) != null || Metadata.getParentEntity(entityType) != null) {
dialog = new HierarchicalChooser(project, entityType, false, multiple, idSelection, acceptEmpty, null);
} else {
dialog = new FlatChooser(project, entityType, multiple, acceptEmpty, null);
}
dialog.setValue(value);
return dialog;
}
protected List<Relation> relationList(String ... entityTypes) {
ArrayList<Relation> relations = new ArrayList<Relation>(entityTypes.length);
for(String entityType: entityTypes) {
relations.add(new Relation(entityType));
}
return relations;
}
private static EntityQuery requirementFilter() {
EntityQuery filter = new EntityQuery("requirement");
filter.addColumn("id", 25);
filter.addColumn("name", 150);
filter.addColumn("target-rel", 75);
filter.addColumn("owner", 50);
filter.addColumn("req-priority", 50);
return filter;
}
private static EntityFields requirementFields() {
EntityFields fields = new EntityFields();
fields.addColumn("id");
fields.addColumn("target-rel");
fields.addColumn("owner");
fields.addColumn("req-priority");
fields.addColumn("name");
fields.addColumn("req-comment");
fields.addColumn("dev-comments");
return fields;
}
private static EntityQuery buildFilter() {
EntityQuery filter = new EntityQuery("build-instance");
filter.addColumn("id", 50);
filter.addColumn("type", 50);
filter.addColumn("release", 50);
filter.addColumn("number", 50);
filter.addColumn("revision", 50);
filter.addColumn("status", 50);
filter.addColumn("start-date", 75);
filter.addColumn("qa-status", 50);
filter.addColumn("category", 50);
return filter;
}
private static EntityFields buildFields() {
EntityFields fields = new EntityFields();
fields.addColumn("id");
fields.addColumn("type");
fields.addColumn("release");
fields.addColumn("number");
fields.addColumn("revision");
fields.addColumn("status");
fields.addColumn("start-date");
fields.addColumn("duration");
fields.addColumn("qa-status");
fields.addColumn("test-success");
fields.addColumn("test-coverage");
fields.addColumn("category");
fields.addColumn("build-system-url");
return fields;
}
private static EntityQuery defectFilter() {
EntityQuery filter = new EntityQuery("defect");
filter.addColumn("id", 25);
filter.addColumn("name", 150);
filter.addColumn("status", 50);
filter.addColumn("target-rel", 75);
filter.addColumn("severity", 50);
filter.addColumn("owner", 50);
filter.addColumn("priority", 50);
return filter;
}
private static EntityFields defectFields() {
EntityFields fields = new EntityFields();
fields.addColumn("id");
fields.addColumn("name");
fields.addColumn("severity");
fields.addColumn("status");
fields.addColumn("target-rel");
fields.addColumn("owner");
fields.addColumn("priority");
fields.addColumn("description");
fields.addColumn("dev-comments");
return fields;
}
private static EntityQuery buildTypeFilter() {
EntityQuery filter = new EntityQuery("build-type");
filter.addColumn("id", 50);
filter.addColumn("name", 50);
filter.addColumn("release", 50);
filter.addColumn("description", 100);
filter.addColumn("category", 50);
filter.addColumn("enabled", 50);
filter.addColumn("default", 50);
return filter;
}
private static EntityQuery changesetFilter() {
EntityQuery filter = new EntityQuery("changeset");
filter.addColumn("id", 50);
filter.addColumn("revision", 50);
filter.addColumn("date", 50);
filter.addColumn("owner", 50);
filter.addColumn("description", 100);
return filter;
}
private static EntityQuery defectLinkFilter() {
EntityQuery filter = new EntityQuery("defect-link");
filter.addColumn("comment", 200);
filter.addColumn("link-type", 75);
filter.addColumn("virtual:linked-id", 55);
filter.addColumn("virtual:linked-name", 125);
filter.addColumn("virtual:linked-status", 55);
filter.addColumn("virtual:linked-type", 55);
return filter;
}
private static void addMayaFilter(EntityQuery entityQuery) {
mayaFilter.put(entityQuery.getEntityType(), entityQuery);
}
}