blob: 6e20d260896a4ea3278437268895eb382c33678f [file] [log] [blame]
//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 IBM Corporation 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:
// IBM Corporation - initial implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.library.configuration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.epf.library.LibraryResources;
import org.eclipse.epf.library.edit.util.TngUtil;
import org.eclipse.epf.library.services.DependencyManager;
import org.eclipse.epf.library.services.ElementDependency;
import org.eclipse.epf.library.services.ElementReference;
import org.eclipse.epf.library.services.PackageReference;
import org.eclipse.epf.library.util.LibraryUtil;
import org.eclipse.ui.views.properties.IPropertyDescriptor;
import org.eclipse.ui.views.properties.IPropertySource;
import com.ibm.uma.Activity;
import com.ibm.uma.BreakdownElement;
import com.ibm.uma.MethodConfiguration;
import com.ibm.uma.MethodElement;
import com.ibm.uma.MethodLibrary;
import com.ibm.uma.MethodPackage;
import com.ibm.uma.MethodPlugin;
import com.ibm.uma.ProcessComponent;
/**
* @author Jinhua Xi
* @since 1.0
*/
public class ConfigurationClosure {
private MethodConfiguration currentConfig = null;
// node change information. The object are the model objects
// check the linked objects in needed
protected List selected = new ArrayList();
// a map of invalid notes to ElementDependencyError object
protected Map invalidNodesMap = new HashMap();
protected List changedNodes = new ArrayList();
private ConfigurationFactory configFactory = null;
protected DependencyManager depMgr = null;
private MethodLibrary library = null;
public ConfigurationClosure(ConfigurationFactory configFactory,
MethodConfiguration currentConfig) {
this.configFactory = configFactory;
this.currentConfig = currentConfig;
this.library = configFactory.getLibrary();
depMgr = configFactory.getDependencyManager();
// cleanup the old status and rebuild the list
selected.clear();
changedNodes.clear();
invalidNodesMap.clear();
// configuration changed, re-build the selection list
try {
buildList(library);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public ConfigurationFactory getConfigurationFactory() {
return this.configFactory;
}
public String getName() {
return currentConfig.getName();
}
public MethodConfiguration getConfiguration() {
return currentConfig;
}
public MethodLibrary getLibrary() {
return this.library;
}
/**
* build the selection list based on the current configuration
*
* @param input
*/
private void buildList(EObject input) {
// validate the configuration before save
LibraryUtil.validateMethodConfiguration(currentConfig);
selected.addAll(currentConfig.getMethodPluginSelection());
selected.addAll(currentConfig.getMethodPackageSelection());
// changedNodes.addAll(selected);
// need to re-build the selections to auto add the process packages
setSelections(selected.toArray());
}
public void setSelections(Object[] elements) {
if (elements == null) {
return;
}
// cleanup the old status and rebuild the list
selected.clear();
changedNodes.clear();
invalidNodesMap.clear();
beginUpdate();
for (int i = 0; i < elements.length; i++) {
Object e = elements[i];
if (!selected.contains(e)) {
if ((e instanceof EObject)) {
// selected.add(e);
add((EObject) e, false);
// if selected element is ProcessComponent
// select all the processPackages that contains the
// activities
if (e instanceof ProcessComponent) {
selectProcessPackages(((ProcessComponent) e)
.getProcess());
}
// else if ( (e instanceof MethodPackage) &&
// ConfigurationHelper.isGlobalPackage((MethodPackage)e) )
// {
// // if the selected package is a global package,
// // make sure it's invisible global packages are also
// selected
// // for example, when the core content package is
// selected,
// // we need to select the content categories packages,
// which are invisible to ui
// selectChildGlobalPackages((MethodPackage)e);
//
// }
} else {
// this is a UI folder, add to the selection
selected.add(e);
}
}
}
// changedNodes.addAll(selected);
endUpdate();
}
private void selectProcessPackages(Activity a) {
if (a == null) {
return;
}
for (Iterator it = a.getBreakdownElements().iterator(); it.hasNext();) {
BreakdownElement e = (BreakdownElement) it.next();
Object pkg = e.eContainer();
if (!selected.contains(pkg)) {
selected.add(pkg);
changedNodes.add(pkg);
}
if (e instanceof Activity) {
selectProcessPackages((Activity) e);
}
}
}
private void add(EObject element, boolean addChildren) {
if (!LibraryUtil.selectable(element)) {
return;
}
if (!selected.contains(element)) {
selected.add(element);
// save the changed notes so that we can update the status later
addChanged(element);
if (element instanceof MethodPlugin) {
selectSystemPackages((MethodPlugin) element);
}
}
// add parent as well
EObject parent = element.eContainer();
if ((parent != null) && !selected.contains(parent)) {
add(parent, false);
}
// add children as needed
if (addChildren) {
EList elements = element.eContents();
if (elements != null) {
for (Iterator it = elements.iterator(); it.hasNext();) {
EObject child = (EObject) it.next();
add(child, true);
}
}
}
}
private void addChanged(Object element) {
if (!changedNodes.contains(element)) {
changedNodes.add(element);
}
}
private void selectSystemPackages(MethodPlugin plugin) {
List pkgs = TngUtil.getAllSystemPackages(plugin);
for (Iterator it = pkgs.iterator(); it.hasNext();) {
EObject pkg = (EObject) it.next();
add(pkg, false);
}
}
public boolean isSelected(Object input) {
if ((input instanceof MethodLibrary) || input == currentConfig
|| selected.contains(input)) {
return true;
}
return false;
}
public ElementDependencyError getError(Object element) {
return getError(element, false);
}
private ElementDependencyError getError(Object element, boolean create) {
ElementDependencyError error = (ElementDependencyError) invalidNodesMap
.get(element);
if (error == null && create) {
error = new ElementDependencyError(element);
invalidNodesMap.put(element, error);
}
return error;
}
public boolean hasError() {
for (Iterator it = invalidNodesMap.values().iterator(); it.hasNext();) {
ElementDependencyError error = (ElementDependencyError) it.next();
if (error.isError()) {
return true;
}
}
return false;
}
public boolean hasProblem() {
return invalidNodesMap.size() > 0;
}
/**
* get a list of all ErrInfo objects
*
* @return List a list of ErrorInfo objects
*/
public List getAllErrors() {
List errors = new ArrayList();
for (Iterator it = invalidNodesMap.values().iterator(); it.hasNext();) {
ElementDependencyError error = (ElementDependencyError) it.next();
errors.addAll(error.getAll());
}
return errors;
}
/**
* return an array of ElementDependencyError objects
*
* @return Object[] array of ElementDependencyError
*/
public Object[] getDependencyErrors() {
return invalidNodesMap.values().toArray();
}
public List getInvalidElements() {
return new ArrayList(invalidNodesMap.keySet());
}
private void removeError(Object element) {
if (invalidNodesMap.containsKey(element)) {
invalidNodesMap.remove(element);
// error status changed, add to the changed list
if (!changedNodes.contains(element)) {
changedNodes.add(element);
}
}
}
/**
* get a list of elements whose check state changed and/or image changed due
* to error
*
* @return
*/
public List getChangedElements() {
List items = new ArrayList(changedNodes);
for (Iterator it = invalidNodesMap.keySet().iterator(); it.hasNext();) {
Object item = it.next();
if (!items.contains(item)) {
items.add(item);
}
}
return items;
}
public Object[] getSelection() {
return selected.toArray();
}
// public Object[] getSelection(EObject element)
// {
// List sels = new ArrayList();
// getSelection(element, sels);
// return sels.toArray();
// }
// private void getSelection(EObject element, List sels)
// {
// if ( !LibraryUtil.selectable(element) )
// {
// return;
// }
//
// if ( selected.contains(element) )
// {
// sels.add(element);
// }
//
// EList elements = element.eContents();
// if ( elements != null )
// {
// for ( Iterator it = elements.iterator(); it.hasNext(); )
// {
// EObject child = (EObject)it.next();
// getSelection(child, sels);
// }
// }
// }
public void beginUpdate() {
changedNodes.clear();
}
public void endUpdate() {
// process the changed elements,
// not we may add additional elements into the changesNodes due to error
// status change
// but only the original changed elements need to be processed
for (Iterator it = new ArrayList(changedNodes).iterator(); it.hasNext();) {
Object changedElement = it.next();
ElementDependency dep = depMgr
.getDependency((MethodElement) changedElement);
if (dep == null) {
continue;
}
if (isSelected(changedElement)) {
validateSelected(dep);
} else {
validateUnSelected(dep);
}
} // end the changed notes for loop
System.out.println("There are (" + invalidNodesMap.size() + ") errors"); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* validate the ElementDependency when the element is selected. When the
* element is selected, we need to do the following: 1. check error for
* references 2. remove error for dependents associated with element 3.
* update parents: if the selection is valid, remove all errors from parents
* associated with this element if the selection is invalid, set error to
* all parents
*
* @param dep
* ElementDependency
*/
private void validateSelected(ElementDependency dep) {
Object changedElement = dep.getElement();
// since this element is selected. any error caused it in the previous
// un-select should be removed
removeError(changedElement);
if (changedElement instanceof MethodLibrary) {
return;
}
// since i am selected, any dependency error caused my me should be
// removed
List dependents = dep.getDependents();
if (dependents != null && dependents.size() > 0) {
Object element;
for (Iterator itr = dependents.iterator(); itr.hasNext();) {
element = itr.next();
ElementDependencyError error = getError(element, false);
if (error != null) {
error.removeError(changedElement);
if (error.size() == 0) {
removeError(element);
// also need to clear the parent error introduced by
// this element
updateParentsForErrors((EObject) element);
}
}
}
}
// if an element is checked, check the element it depends on,
// which should checked as well
List refs = dep.getReferences();
if (refs != null && refs.size() > 0) {
PackageReference ref;
Object element;
for (Iterator itr = refs.iterator(); itr.hasNext();) {
ref = (PackageReference) itr.next();
// for ( Iterator ite = ref.getReferenceList().iterator();
// ite.hasNext(); )
{
element = ref.getRefElement();
if (element instanceof MethodLibrary) {
continue;
}
// RATLC00382739 - Don't warn on optional inputs not being
// present
// so added the canIgnore() method
if (!isSelected(element) && !canIgnore(ref)) {
String message;
int errorType = 0;
if (ref.hasBaseReference()) {
errorType = ErrorInfo.ERROR;
message = LibraryResources
.getString("Library.configClosureWarning.msg2"); //$NON-NLS-1$
} else {
errorType = ErrorInfo.WARNING;
message = LibraryResources
.getString("Library.configClosureWarning.msg3"); //$NON-NLS-1$
}
ElementDependencyError error = getError(changedElement,
true);
error
.addError(new ErrorInfo(errorType, message,
changedElement, element,
ErrorInfo.REFERENCE_TO));
} else {
ElementDependencyError error = getError(changedElement,
false);
if (error != null) {
error.removeError(element);
}
}
}
}
}
// finally, update the parents
updateParentsForErrors((EObject) changedElement);
}
/**
* validate the ElementDependency when the element is unselected. When the
* element is unselected, we need to do the following: 1. check error for
* dependencts 2. remove error for references associated with element, in
* case of any added when the element was check 3. update parents: if the
* selection is valid, remove all errors from parents associated with this
* element if the selection is invalid, set error to all parents
*
* @param dep
* ElementDependency
*/
private void validateUnSelected(ElementDependency dep) {
Object changedElement = dep.getElement();
// since this element is un-selected. any error caused it in the
// previous select should be removed
removeError(changedElement);
if (changedElement instanceof MethodLibrary) {
return;
}
// since i am un-selected, any error due i my referecing others should
// be removed
List refs = dep.getReferences();
if (refs != null && refs.size() > 0) {
ElementReference ref;
Object element;
for (Iterator itr = refs.iterator(); itr.hasNext();) {
ref = (ElementReference) itr.next();
// for ( Iterator ite = ref.getReferenceList().iterator();
// ite.hasNext(); )
{
element = ref.getRefElement();
ElementDependencyError error = getError(element, false);
if (error != null) {
error.removeError(changedElement);
if (error.size() == 0) {
removeError(element);
// also need to clear the parent error introduced by
// this element
updateParentsForErrors((EObject) element);
}
}
}
}
}
// if an element is unchecked, check the element depending on it,
// if there are check elements depending on it, it can't be unchecked
List dependents = dep.getDependents();
if (dependents != null && dependents.size() > 0) {
Object element;
for (Iterator itr = dependents.iterator(); itr.hasNext();) {
element = itr.next();
if (element instanceof MethodLibrary) {
continue;
}
if (isSelected(element)) {
// String message;
// int errorType = 0;
// need to determine what type of dependency it is
ElementDependency childDep = depMgr
.getDependency((MethodElement) element);
validateSelected(childDep);
/*
* PackageReference ref =
* (PackageReference)childDep.getReference(changedElement);
* if ( ref == null ) { // error, inconsistency continue; }
*
* if ( ref.hasBaseReference() ) { errorType =
* ErrorInfo.ERROR; message = "Element can't be unselect
* since it is the base element of other selected
* element(s)"; } else { errorType = ErrorInfo.WARNING;
* message = "Un-selected element has other element(s)
* reference to it"; }
*
* ElementDependencyError error = getError(changedElement,
* true); error.addError(new ErrorInfo(errorType, message,
* changedElement, element, ErrorInfo.REFERENCED_BY));
*/
} else {
removeError(changedElement);
}
}
}
// finally, update the parents
updateParentsForErrors((EObject) changedElement);
}
private void updateParentError(EObject parent, EObject element,
int errorType) {
if (parent == null || (parent instanceof MethodLibrary)) {
return;
}
if ((parent instanceof MethodPackage)
&& ConfigurationHelper.isGlobalPackage((MethodPackage) parent)) {
updateParentError(parent.eContainer(), element, errorType);
return;
}
// remove the error associated with this element from all parents
ElementDependencyError error = getError(parent, false);
if (error != null && error.size() > 0) {
error.removeError(element);
}
if (errorType != ErrorInfo.NONE) {
// propegate the error to all parents
error = getError(parent, true);
String message = LibraryResources
.getString("Library.configClosureWarning.msg1"); //$NON-NLS-1$
error.addError(new ErrorInfo(errorType, message, parent, element,
ErrorInfo.NONE));
} else if ((error != null) && (error.size() == 0)) {
removeError(parent);
}
updateParentError(parent.eContainer(), element, errorType);
}
private void updateParentsForErrors(EObject element) {
int errorType = ErrorInfo.NONE;
ElementDependencyError error = getError(element);
if (error != null && error.size() > 0) {
if (error.isError() || error.isChildError()) {
errorType = ErrorInfo.CHILD_ERROR;
} else if (error.isWarning() || error.isChildWarning()) {
errorType = ErrorInfo.CHILD_WARNING;
}
}
updateParentError(element.eContainer(), element, errorType);
}
/**
* accept the cutrrent selection and ignore any warning message. make the
* configuration.
*
*/
public void makeClosure() {
// if no error update the method configuration
while (hasError()) {
fixProblems(true);
}
// saveMethodConfiguration();
}
/**
* fix all error(s) and warnign(s)
*
*/
public void fixProblems() {
// if no error update the method configuration
while (hasProblem()) {
fixProblems(false);
}
}
private void fixProblems(boolean errorOnly) {
// Note: make closure will select elements as needed.
// so we need to make a copy of the current selcted ones
// in order to trace the status
List currentSelected = new ArrayList(selected);
// beginUpdate();
// list of errorInfo objects
List errors = getAllErrors();
if (errors.size() > 0) {
invalidNodesMap.clear();
ErrorInfo error;
EObject ownerElement, causeElement;
boolean ownerSelected, causeSelected;
for (Iterator it = errors.iterator(); it.hasNext();) {
error = (ErrorInfo) it.next();
ownerElement = (EObject) error.getOwnerElement();
causeElement = (EObject) error.getCauseElement();
addChanged(ownerElement);
addChanged(causeElement);
if (error.isChildError() || error.isChildWarning()) {
continue;
}
if (error.isWarning() && errorOnly) {
continue;
}
ownerSelected = currentSelected.contains(ownerElement);
causeSelected = currentSelected.contains(causeElement);
// if the owner element is not selected
// the error is caused by un-selecting this element,
// select it will fix the error
// if the owner element is selected, the error is caused by
// un-selected refwereces
// select those cause elements will fix the error
if (!ownerSelected) {
selectErrorElement(ownerElement);
} else if (!causeSelected) {
selectErrorElement(causeElement);
}
}
}
endUpdate();
}
private void selectErrorElement(EObject element) {
// since the selection is on package level,
// if the element is not a package, when selected, all it's non-package
// siblings must be selected as well
if (LibraryUtil.selectable(element)) {
add(element, true);
}
}
public void saveMethodConfiguration() {
List models = currentConfig.getMethodPluginSelection();
List packages = currentConfig.getMethodPackageSelection();
models.clear();
packages.clear();
EObject element;
for (Iterator it = selected.iterator(); it.hasNext();) {
element = (EObject) it.next();
if (element instanceof MethodPlugin) {
if (!models.contains(element)) {
models.add(element);
}
} else if ((element instanceof MethodPackage)
&& !ConfigurationHelper
.isGlobalPackage((MethodPackage) element)) {
if (!packages.contains(element)) {
packages.add(element);
}
}
}
}
/**
* package the library based on the selection. Note: this will change the
* original library. So should make this libray as a copy before call this.
* I.e. create a new ConfigurationFactory with a copy of the original
* library, rebuild the dependency, create a Configuration (this object)
* with the current configuration, and call this method.
*
* @return
*/
public MethodLibrary packageLibrary(boolean removeBrokenReferences) {
processSelection(library, removeBrokenReferences);
// remove the configurations except the current one
List configs = library.getPredefinedConfigurations();
configs.clear();
configs.add(currentConfig);
return library;
}
/**
* process the selected package by removeing all unselected elements and any
* missing references
*
* @param element
*/
private void processSelection(EObject element,
boolean removeBrokenReferences) {
if (removeBrokenReferences) {
// iterator the references and remove broken references
EList references = element.eCrossReferences();
if (references != null) {
for (Iterator it = new ArrayList(references).iterator(); it
.hasNext();) {
EObject ref = (EObject) it.next();
EObject pkgRef = LibraryUtil.getSelectable(ref);
if (pkgRef != null && !isSelected(pkgRef)) {
removeReference(element, ref);
}
}
}
}
EList elements = element.eContents();
if (elements != null) {
for (Iterator it = new ArrayList(elements).iterator(); it.hasNext();) {
EObject child = (EObject) it.next();
// if the child element is selectable but is not in the
// configuration, remove it
if (LibraryUtil.selectable(child) && !isSelected(child)) {
EcoreUtil.remove(child);
} else {
processSelection(child, removeBrokenReferences);
}
}
}
}
private void removeReference(EObject ownerElement, EObject refElement) {
AdapterFactoryContentProvider provider = configFactory
.getContentProvider();
IPropertySource ps = provider.getPropertySource(ownerElement);
IPropertyDescriptor[] pds = ps.getPropertyDescriptors();
if (pds != null && pds.length > 0) {
for (int i = 0; i < pds.length; i++) {
IPropertyDescriptor descriptor = (IPropertyDescriptor) pds[i];
Object id = descriptor.getId();
Object value = ps.getPropertyValue(id);
// check if need to convert to editable value
IPropertySource source = provider.getPropertySource(value);
if (source != null) {
value = source.getEditableValue();
}
if (value instanceof EList) {
EList refList = (EList) value;
if (refList.contains(refElement)) {
System.out
.println("Reference [" + LibraryUtil.getName(refElement) //$NON-NLS-1$
+ "] removed from [" //$NON-NLS-1$
+ LibraryUtil.getName(ownerElement)
+ "]'s reference list"); //$NON-NLS-1$
refList.remove(refElement);
ps.setPropertyValue(id, refList);
}
} else if (value instanceof MethodElement) {
System.out
.println("Reference [" + LibraryUtil.getName(refElement) //$NON-NLS-1$
+ "] removed from [" //$NON-NLS-1$
+ LibraryUtil.getName(ownerElement) + "]"); //$NON-NLS-1$
ps.setPropertyValue(id, null);
}
}
}
}
public void dispose() {
this.configFactory = null;
this.currentConfig = null;
this.library = null;
depMgr = null;
// cleanup the old status and rebuild the list
selected.clear();
changedNodes.clear();
invalidNodesMap.clear();
}
private boolean canIgnore(PackageReference pkgRef) {
return pkgRef.canIgnore();
}
}