| /******************************************************************************* |
| * Copyright (c) 2004, 2008 Tasktop Technologies and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Tasktop Technologies - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.mylyn.internal.bugzilla.core; |
| |
| import java.io.Serializable; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| |
| import org.eclipse.mylyn.internal.bugzilla.core.IBugzillaConstants.BUGZILLA_REPORT_STATUS; |
| import org.eclipse.mylyn.tasks.core.data.TaskAttribute; |
| import org.eclipse.mylyn.tasks.core.data.TaskData; |
| import org.eclipse.mylyn.tasks.core.data.TaskOperation; |
| |
| /** |
| * Class describing the configuration of products and components for a given Bugzilla installation. |
| * |
| * @author Rob Elves |
| */ |
| public class RepositoryConfiguration implements Serializable { |
| |
| private static final long serialVersionUID = -3600257926613730005L; |
| |
| private String repositoryUrl = "<unknown>"; //$NON-NLS-1$ |
| |
| private final Map<String, ProductEntry> products = new HashMap<String, ProductEntry>(); |
| |
| private final List<String> platforms = new ArrayList<String>(); |
| |
| private final List<String> operatingSystems = new ArrayList<String>(); |
| |
| private final List<String> priorities = new ArrayList<String>(); |
| |
| private final List<String> severities = new ArrayList<String>(); |
| |
| private final List<String> bugStatus = new ArrayList<String>(); |
| |
| private final List<String> openStatusValues = new ArrayList<String>(); |
| |
| private final List<String> resolutionValues = new ArrayList<String>(); |
| |
| private final List<String> keywords = new ArrayList<String>(); |
| |
| // master lists |
| |
| private final List<String> versions = new ArrayList<String>(); |
| |
| private final List<String> components = new ArrayList<String>(); |
| |
| private final List<String> milestones = new ArrayList<String>(); |
| |
| private final List<BugzillaCustomField> customFields = new ArrayList<BugzillaCustomField>(); |
| |
| private final List<BugzillaFlag> flags = new ArrayList<BugzillaFlag>(); |
| |
| private BugzillaVersion version = BugzillaVersion.MIN_VERSION; |
| |
| public RepositoryConfiguration() { |
| super(); |
| } |
| |
| public void addStatus(String status) { |
| bugStatus.add(status); |
| } |
| |
| public List<String> getStatusValues() { |
| return bugStatus; |
| } |
| |
| public void addResolution(String res) { |
| resolutionValues.add(res); |
| } |
| |
| public List<String> getResolutions() { |
| return resolutionValues; |
| } |
| |
| /** |
| * Adds a product to the configuration. |
| */ |
| public void addProduct(String name) { |
| if (!products.containsKey(name)) { |
| ProductEntry product = new ProductEntry(name); |
| products.put(name, product); |
| } |
| } |
| |
| /** |
| * Returns an array of names of current products. |
| */ |
| public List<String> getProducts() { |
| ArrayList<String> productList = new ArrayList<String>(products.keySet()); |
| Collections.sort(productList); |
| return productList; |
| } |
| |
| /** |
| * Returns an array of names of component that exist for a given product or <code>null</code> if the product does |
| * not exist. |
| */ |
| public List<String> getComponents(String product) { |
| ProductEntry entry = products.get(product); |
| if (entry != null) { |
| return entry.getComponents(); |
| } else { |
| return Collections.emptyList(); |
| } |
| } |
| |
| /** |
| * Returns an array of names of versions that exist for a given product or <code>null</code> if the product does not |
| * exist. |
| */ |
| public List<String> getVersions(String product) { |
| ProductEntry entry = products.get(product); |
| if (entry != null) { |
| return entry.getVersions(); |
| } else { |
| return Collections.emptyList(); |
| } |
| } |
| |
| /** |
| * Returns an array of names of valid severity values. |
| */ |
| public List<String> getSeverities() { |
| return severities; |
| } |
| |
| /** |
| * Returns an array of names of valid OS values. |
| */ |
| public List<String> getOSs() { |
| return operatingSystems; |
| } |
| |
| public void addOS(String os) { |
| operatingSystems.add(os); |
| } |
| |
| /** |
| * Returns an array of names of valid platform values. |
| */ |
| public List<String> getPlatforms() { |
| return platforms; |
| } |
| |
| /** |
| * Returns an array of names of valid platform values. |
| */ |
| public List<String> getPriorities() { |
| return priorities; |
| } |
| |
| /** |
| * Adds a component to the given product. |
| */ |
| public void addComponent(String product, String component) { |
| if (!components.contains(component)) { |
| components.add(component); |
| } |
| ProductEntry entry = products.get(product); |
| if (entry == null) { |
| entry = new ProductEntry(product); |
| products.put(product, entry); |
| } |
| entry.addComponent(component); |
| } |
| |
| public void addVersion(String product, String version) { |
| if (!versions.contains(version)) { |
| versions.add(version); |
| } |
| ProductEntry entry = products.get(product); |
| if (entry == null) { |
| entry = new ProductEntry(product); |
| products.put(product, entry); |
| } |
| entry.addVersion(version); |
| } |
| |
| public void addKeyword(String keyword) { |
| keywords.add(keyword); |
| } |
| |
| public List<String> getKeywords() { |
| return keywords; |
| } |
| |
| public void addPlatform(String platform) { |
| platforms.add(platform); |
| } |
| |
| public void addPriority(String priority) { |
| priorities.add(priority); |
| } |
| |
| public void addSeverity(String severity) { |
| severities.add(severity); |
| |
| } |
| |
| public void setInstallVersion(String version) { |
| this.version = new BugzillaVersion(version); |
| } |
| |
| public BugzillaVersion getInstallVersion() { |
| return version; |
| } |
| |
| public void addTargetMilestone(String product, String target) { |
| if (!milestones.contains(target)) { |
| milestones.add(target); |
| } |
| ProductEntry entry = products.get(product); |
| if (entry == null) { |
| entry = new ProductEntry(product); |
| products.put(product, entry); |
| } |
| |
| entry.addTargetMilestone(target); |
| |
| } |
| |
| public List<String> getTargetMilestones(String product) { |
| ProductEntry entry = products.get(product); |
| if (entry != null) { |
| return entry.getTargetMilestones(); |
| } else { |
| return Collections.emptyList(); |
| } |
| } |
| |
| /** |
| * Container for product information: name, components. |
| */ |
| private static class ProductEntry implements Serializable { |
| |
| private static final long serialVersionUID = 4120139521246741120L; |
| |
| String productName; |
| |
| List<String> components = new ArrayList<String>(); |
| |
| List<String> versions = new ArrayList<String>(); |
| |
| List<String> milestones = new ArrayList<String>(); |
| |
| ProductEntry(String name) { |
| this.productName = name; |
| } |
| |
| List<String> getComponents() { |
| return components; |
| } |
| |
| void addComponent(String componentName) { |
| if (!components.contains(componentName)) { |
| components.add(componentName); |
| } |
| } |
| |
| List<String> getVersions() { |
| return versions; |
| } |
| |
| void addVersion(String name) { |
| if (!versions.contains(name)) { |
| versions.add(name); |
| } |
| } |
| |
| List<String> getTargetMilestones() { |
| return milestones; |
| } |
| |
| void addTargetMilestone(String target) { |
| milestones.add(target); |
| } |
| } |
| |
| public List<String> getOpenStatusValues() { |
| return openStatusValues; |
| } |
| |
| public void addOpenStatusValue(String value) { |
| openStatusValues.add(value); |
| } |
| |
| public List<String> getComponents() { |
| return components; |
| } |
| |
| public List<String> getTargetMilestones() { |
| return milestones; |
| } |
| |
| public List<String> getVersions() { |
| return versions; |
| } |
| |
| public String getRepositoryUrl() { |
| return repositoryUrl; |
| } |
| |
| public void setRepositoryUrl(String repositoryUrl) { |
| this.repositoryUrl = repositoryUrl; |
| } |
| |
| /* |
| * Intermediate step until configuration is made generic. |
| */ |
| public List<String> getOptionValues(BugzillaAttribute element, String product) { |
| switch (element) { |
| case PRODUCT: |
| return getProducts(); |
| case TARGET_MILESTONE: |
| return getTargetMilestones(product); |
| case BUG_STATUS: |
| return getStatusValues(); |
| case VERSION: |
| return getVersions(product); |
| case COMPONENT: |
| return getComponents(product); |
| case REP_PLATFORM: |
| return getPlatforms(); |
| case OP_SYS: |
| return getOSs(); |
| case PRIORITY: |
| return getPriorities(); |
| case BUG_SEVERITY: |
| return getSeverities(); |
| case KEYWORDS: |
| return getKeywords(); |
| case RESOLUTION: |
| return getResolutions(); |
| default: |
| return Collections.emptyList(); |
| } |
| } |
| |
| /** |
| * Adds a field to the configuration. |
| */ |
| public void addCustomField(BugzillaCustomField newField) { |
| customFields.add(newField); |
| } |
| |
| public List<BugzillaCustomField> getCustomFields() { |
| return customFields; |
| } |
| |
| public void configureTaskData(TaskData taskData) { |
| if (taskData != null) { |
| addMissingFlags(taskData); |
| updateAttributeOptions(taskData); |
| addValidOperations(taskData); |
| } |
| } |
| |
| private void addMissingFlags(TaskData taskData) { |
| List<String> existingFlags = new ArrayList<String>(); |
| List<BugzillaFlag> flags = getFlags(); |
| for (TaskAttribute attribute : new HashSet<TaskAttribute>(taskData.getRoot().getAttributes().values())) { |
| if (attribute.getId().startsWith("task.common.kind.flag")) { //$NON-NLS-1$ |
| TaskAttribute state = attribute.getAttribute("state"); //$NON-NLS-1$ |
| if (state != null) { |
| String nameValue = state.getMetaData().getLabel(); |
| if (!existingFlags.contains(nameValue)) { |
| existingFlags.add(nameValue); |
| } |
| String desc = attribute.getMetaData().getLabel(); |
| if (desc == null || desc.equals("")) { //$NON-NLS-1$ |
| for (BugzillaFlag bugzillaFlag : flags) { |
| if (bugzillaFlag.getType().equals("attachment")) { //$NON-NLS-1$ |
| continue; |
| } |
| if (bugzillaFlag.getName().equals(nameValue)) { |
| attribute.getMetaData().setLabel(bugzillaFlag.getDescription()); |
| } |
| } |
| } |
| } |
| } |
| } |
| TaskAttribute productAttribute = taskData.getRoot().getMappedAttribute(BugzillaAttribute.PRODUCT.getKey()); |
| TaskAttribute componentAttribute = taskData.getRoot().getMappedAttribute(BugzillaAttribute.COMPONENT.getKey()); |
| for (BugzillaFlag bugzillaFlag : flags) { |
| if (bugzillaFlag.getType().equals("attachment")) { //$NON-NLS-1$ |
| continue; |
| } |
| if (!bugzillaFlag.isUsedIn(productAttribute.getValue(), componentAttribute.getValue())) { |
| continue; |
| } |
| if (existingFlags.contains(bugzillaFlag.getName()) && !bugzillaFlag.isMultiplicable()) { |
| continue; |
| } |
| BugzillaFlagMapper mapper = new BugzillaFlagMapper(); |
| mapper.setRequestee(""); //$NON-NLS-1$ |
| mapper.setSetter(""); //$NON-NLS-1$ |
| mapper.setState(" "); //$NON-NLS-1$ |
| mapper.setFlagId(bugzillaFlag.getName()); |
| mapper.setNumber(0); |
| mapper.setDescription(bugzillaFlag.getDescription()); |
| TaskAttribute attribute = taskData.getRoot().createAttribute( |
| "task.common.kind.flag_type" + bugzillaFlag.getFlagId()); //$NON-NLS-1$ |
| mapper.applyTo(attribute); |
| } |
| setFlagsRequestee(taskData); |
| } |
| |
| private void setFlagsRequestee(TaskData taskData) { |
| for (TaskAttribute attribute : new HashSet<TaskAttribute>(taskData.getRoot().getAttributes().values())) { |
| if (attribute.getId().startsWith("task.common.kind.flag")) { //$NON-NLS-1$ |
| TaskAttribute state = attribute.getAttribute("state"); //$NON-NLS-1$ |
| if (state != null) { |
| String nameValue = state.getMetaData().getLabel(); |
| for (BugzillaFlag bugzillaFlag : flags) { |
| if (nameValue.equals(bugzillaFlag.getName())) { |
| TaskAttribute requestee = attribute.getAttribute("requestee"); //$NON-NLS-1$ |
| if (requestee == null) { |
| requestee = attribute.createMappedAttribute("requestee"); //$NON-NLS-1$ |
| requestee.getMetaData().defaults().setType(TaskAttribute.TYPE_SHORT_TEXT); |
| requestee.setValue(""); //$NON-NLS-1$ |
| } |
| requestee.getMetaData().setReadOnly(!bugzillaFlag.isSpecifically_requestable()); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| public void updateAttributeOptions(TaskData existingReport) { |
| TaskAttribute attributeProduct = existingReport.getRoot() |
| .getMappedAttribute(BugzillaAttribute.PRODUCT.getKey()); |
| if (attributeProduct == null) { |
| return; |
| } |
| String product = attributeProduct.getValue(); |
| for (TaskAttribute attribute : new HashSet<TaskAttribute>(existingReport.getRoot().getAttributes().values())) { |
| |
| List<String> optionValues = getAttributeOptions(product, attribute); |
| |
| if (attribute.getId().equals(BugzillaAttribute.TARGET_MILESTONE.getKey()) && optionValues.isEmpty()) { |
| existingReport.getRoot().removeAttribute(BugzillaAttribute.TARGET_MILESTONE.getKey()); |
| continue; |
| } |
| |
| if (attribute.getId().startsWith("task.common.kind.flag")) { //$NON-NLS-1$ |
| attribute = attribute.getAttribute("state"); //$NON-NLS-1$ |
| } |
| |
| attribute.clearOptions(); |
| for (String option : optionValues) { |
| attribute.putOption(option, option); |
| } |
| } |
| |
| } |
| |
| public List<String> getAttributeOptions(String product, TaskAttribute attribute) { |
| List<String> options = new ArrayList<String>(); |
| |
| if (attribute.getId().startsWith(BugzillaCustomField.CUSTOM_FIELD_PREFIX)) { |
| for (BugzillaCustomField bugzillaCustomField : customFields) { |
| if (bugzillaCustomField.getName().equals(attribute.getId())) { |
| options = bugzillaCustomField.getOptions(); |
| break; |
| } |
| } |
| |
| } else if (attribute.getId().startsWith("task.common.kind.flag")) { //$NON-NLS-1$ |
| |
| TaskAttribute state = attribute.getAttribute("state"); //$NON-NLS-1$ |
| if (state != null) { |
| String nameValue = state.getMetaData().getLabel(); |
| options.add(""); //$NON-NLS-1$ |
| for (BugzillaFlag bugzillaFlag : flags) { |
| if (nameValue.equals(bugzillaFlag.getName())) { |
| if (nameValue.equals(bugzillaFlag.getName())) { |
| if (bugzillaFlag.isRequestable()) { |
| options.add("?"); //$NON-NLS-1$ |
| } |
| break; |
| } |
| } |
| } |
| options.add("+"); //$NON-NLS-1$ |
| options.add("-"); //$NON-NLS-1$ |
| } |
| } |
| |
| else { |
| String type = attribute.getMetaData().getType(); |
| |
| if (type != null && type.equals(IBugzillaConstants.EDITOR_TYPE_FLAG)) { |
| options.add(""); //$NON-NLS-1$ |
| options.add("?"); //$NON-NLS-1$ |
| options.add("+"); //$NON-NLS-1$ |
| options.add("-"); //$NON-NLS-1$ |
| } else { |
| |
| BugzillaAttribute element; |
| try { |
| element = BugzillaAttribute.valueOf(attribute.getId().trim().toUpperCase(Locale.ENGLISH)); |
| } catch (RuntimeException e) { |
| if (e instanceof IllegalArgumentException) { |
| // ignore unrecognized tags |
| return options; |
| } |
| throw e; |
| } |
| |
| options = getOptionValues(element, product); |
| |
| if (element != BugzillaAttribute.RESOLUTION && element != BugzillaAttribute.OP_SYS |
| && element != BugzillaAttribute.BUG_SEVERITY && element != BugzillaAttribute.PRIORITY |
| && element != BugzillaAttribute.BUG_STATUS) { |
| Collections.sort(options); |
| } |
| } |
| } |
| return options; |
| } |
| |
| public void addValidOperations(TaskData bugReport) { |
| TaskAttribute attributeStatus = bugReport.getRoot().getMappedAttribute(TaskAttribute.STATUS); |
| BUGZILLA_REPORT_STATUS status = BUGZILLA_REPORT_STATUS.NEW; |
| if (attributeStatus != null) { |
| try { |
| status = BUGZILLA_REPORT_STATUS.valueOf(attributeStatus.getValue()); |
| } catch (RuntimeException e) { |
| // StatusHandler.log(new Status(IStatus.INFO, BugzillaCorePlugin.PLUGIN_ID, "Unrecognized status: " |
| // + attributeStatus.getValue(), e)); |
| status = BUGZILLA_REPORT_STATUS.NEW; |
| } |
| } |
| switch (status) { |
| case UNCONFIRMED: |
| case REOPENED: |
| case NEW: |
| addOperation(bugReport, BugzillaOperation.none); |
| addOperation(bugReport, BugzillaOperation.accept); |
| addOperation(bugReport, BugzillaOperation.resolve); |
| addOperation(bugReport, BugzillaOperation.duplicate); |
| break; |
| case ASSIGNED: |
| addOperation(bugReport, BugzillaOperation.none); |
| addOperation(bugReport, BugzillaOperation.resolve); |
| addOperation(bugReport, BugzillaOperation.duplicate); |
| break; |
| case RESOLVED: |
| addOperation(bugReport, BugzillaOperation.none); |
| addOperation(bugReport, BugzillaOperation.reopen); |
| addOperation(bugReport, BugzillaOperation.verify); |
| addOperation(bugReport, BugzillaOperation.close); |
| break; |
| case CLOSED: |
| addOperation(bugReport, BugzillaOperation.none); |
| addOperation(bugReport, BugzillaOperation.reopen); |
| break; |
| case VERIFIED: |
| addOperation(bugReport, BugzillaOperation.none); |
| addOperation(bugReport, BugzillaOperation.reopen); |
| addOperation(bugReport, BugzillaOperation.close); |
| } |
| BugzillaVersion bugzillaVersion = getInstallVersion(); |
| if (bugzillaVersion == null) { |
| bugzillaVersion = BugzillaVersion.MIN_VERSION; |
| } |
| |
| if (bugzillaVersion.compareTo(BugzillaVersion.BUGZILLA_3_0) < 0) { |
| // Product change is only supported for Versions >= 3.0 without verify html page |
| TaskAttribute productAttribute = bugReport.getRoot().getMappedAttribute(BugzillaAttribute.PRODUCT.getKey()); |
| productAttribute.getMetaData().setReadOnly(true); |
| } |
| |
| if (status == BUGZILLA_REPORT_STATUS.NEW || status == BUGZILLA_REPORT_STATUS.ASSIGNED |
| || status == BUGZILLA_REPORT_STATUS.REOPENED || status == BUGZILLA_REPORT_STATUS.UNCONFIRMED) { |
| if (bugzillaVersion.compareMajorMinorOnly(BugzillaVersion.BUGZILLA_3_0) <= 0) { |
| // old bugzilla workflow is used |
| addOperation(bugReport, BugzillaOperation.reassign); |
| addOperation(bugReport, BugzillaOperation.reassignbycomponent); |
| } else { |
| BugzillaAttribute key = BugzillaAttribute.SET_DEFAULT_ASSIGNEE; |
| TaskAttribute operationAttribute = bugReport.getRoot().getAttribute(key.getKey()); |
| if (operationAttribute == null) { |
| operationAttribute = bugReport.getRoot().createAttribute(key.getKey()); |
| operationAttribute.getMetaData() |
| .defaults() |
| .setReadOnly(key.isReadOnly()) |
| .setKind(key.getKind()) |
| .setLabel(key.toString()) |
| .setType(key.getType()); |
| operationAttribute.setValue("0"); //$NON-NLS-1$ |
| } |
| operationAttribute = bugReport.getRoot().getMappedAttribute(TaskAttribute.USER_ASSIGNED); |
| if (operationAttribute != null) { |
| operationAttribute.getMetaData().setReadOnly(false); |
| } |
| } |
| } |
| } |
| |
| public void addOperation(TaskData bugReport, BugzillaOperation opcode) { |
| TaskAttribute attribute; |
| TaskAttribute operationAttribute = bugReport.getRoot().getAttribute(TaskAttribute.OPERATION); |
| if (operationAttribute == null) { |
| operationAttribute = bugReport.getRoot().createAttribute(TaskAttribute.OPERATION); |
| } |
| |
| switch (opcode) { |
| case none: |
| attribute = bugReport.getRoot().createAttribute(TaskAttribute.PREFIX_OPERATION + opcode.toString()); |
| String label = "Leave"; //$NON-NLS-1$ |
| TaskAttribute attributeStatus = bugReport.getRoot().getMappedAttribute(TaskAttribute.STATUS); |
| TaskAttribute attributeResolution = bugReport.getRoot().getMappedAttribute(TaskAttribute.RESOLUTION); |
| if (attributeStatus != null && attributeResolution != null) { |
| label = String.format(opcode.getLabel(), attributeStatus.getValue(), attributeResolution.getValue()); |
| } |
| |
| TaskOperation.applyTo(attribute, opcode.toString(), label); |
| // set as default |
| TaskOperation.applyTo(operationAttribute, opcode.toString(), label); |
| break; |
| case resolve: |
| attribute = bugReport.getRoot().createAttribute(TaskAttribute.PREFIX_OPERATION + opcode.toString()); |
| TaskOperation.applyTo(attribute, opcode.toString(), opcode.getLabel()); |
| TaskAttribute attrResolvedInput = attribute.getTaskData().getRoot().createAttribute(opcode.getInputId()); |
| attrResolvedInput.getMetaData().setType(opcode.getInputType()); |
| attribute.getMetaData().putValue(TaskAttribute.META_ASSOCIATED_ATTRIBUTE_ID, opcode.getInputId()); |
| for (String resolution : getResolutions()) { |
| // DUPLICATE and MOVED have special meanings so do not show as resolution |
| if (resolution.compareTo("DUPLICATE") != 0 && resolution.compareTo("MOVED") != 0) { //$NON-NLS-1$ //$NON-NLS-2$ |
| attrResolvedInput.putOption(resolution, resolution); |
| } |
| } |
| if (getResolutions().size() > 0) { |
| attrResolvedInput.setValue(getResolutions().get(0)); |
| } |
| break; |
| default: |
| attribute = bugReport.getRoot().createAttribute(TaskAttribute.PREFIX_OPERATION + opcode.toString()); |
| TaskOperation.applyTo(attribute, opcode.toString(), opcode.getLabel()); |
| if (opcode.getInputId() != null) { |
| TaskAttribute attrInput = bugReport.getRoot().createAttribute(opcode.getInputId()); |
| attrInput.getMetaData().defaults().setReadOnly(false).setType(opcode.getInputType()); |
| attribute.getMetaData().putValue(TaskAttribute.META_ASSOCIATED_ATTRIBUTE_ID, opcode.getInputId()); |
| } |
| break; |
| } |
| } |
| |
| /** |
| * Adds a flag to the configuration. |
| */ |
| public void addFlag(BugzillaFlag newFlag) { |
| flags.add(newFlag); |
| } |
| |
| public List<BugzillaFlag> getFlags() { |
| return flags; |
| } |
| |
| public BugzillaFlag getFlagWithId(Integer id) { |
| for (BugzillaFlag bugzillaFlag : flags) { |
| if (bugzillaFlag.getFlagId() == id) { |
| return bugzillaFlag; |
| } |
| } |
| return null; |
| } |
| } |