/*
* Copyright (c) 2006, 2007 Borland Software Corporation
*
* 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:
* Artem Tikhomirov (Borland) - initial API and implementation
*/
package org.eclipse.gmf.internal.bridge.wizards.strategy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
/**
* FIXME Tests!!!
* @author artem
*/
public class Hierarchy {
private final List<EReference> myRefs;
private final EPackage myDomainModel;
private final Map<EClass, Set<EClass>> myResult;
private Set<EClass> myAccessibleLeaves;
private Set<EClass> myAccessibleClasses = new HashSet<EClass>();
/**
* EClasses that may suit as link
*/
private Set<EClass> myAccessibleLinkClasses = new HashSet<EClass>();
/**
* Containment references to get to classes in myAccessibleLinkClasses set
*/
private Set<EReference> myLinkClassContainmentRefs = new HashSet<EReference>();
private final EClass myDiagramContainer;
/**
*
* @param eRefs
* @param diagramContainer - can be <code>null</code>
* @param domainModel
*/
public Hierarchy(List<EReference> eRefs, EClass diagramContainer, EPackage domainModel) {
myRefs = eRefs;
myDiagramContainer = diagramContainer;
myDomainModel = domainModel;
myResult = new HashMap<EClass, Set<EClass>>();
for (EReference element : eRefs) {
myResult.put(element.getEReferenceType(), new HashSet<EClass>());
}
}
public Hierarchy(EClass diagramContainer) {
this(diagramContainer.getEAllContainments(), diagramContainer, diagramContainer.getEPackage());
}
public Hierarchy(EClass diagramContainer, EPackage domainModel) {
this(diagramContainer.getEAllContainments(), diagramContainer, domainModel);
}
/**
* @return domain element associated with diagram itself, or <code>null</code> if not specified.
*/
public EClass getDiagramContainer() {
return myDiagramContainer;
}
public EReference nodeBackRef(EClass nodeElement) {
for (EReference r : myRefs) {
if (r.getEReferenceType().isSuperTypeOf(nodeElement)) {
return r;
}
}
return null;
}
public EReference linkBackRef(EClass linkElement) {
ArrayList<EReference> compatible = new ArrayList<EReference>();
for (EReference r : myLinkClassContainmentRefs) {
if (r.getEReferenceType().isSuperTypeOf(linkElement)) {
compatible.add(r);
}
}
if (compatible.isEmpty()) {
return null;
}
// try exact match
for (int i = compatible.size() - 1; i >= 0; i--) {
EReference r = compatible.get(i);
if (r.getEReferenceType().equals(linkElement)) {
return r;
}
}
// just pick any
return compatible.get(0);
}
public boolean isLeaf(EClass element) {
return myAccessibleLeaves.contains(element);
}
public EReference getLinkFeature(EClass element) {
List<EReference> l = collectAllNonContainment(element);
if (l.isEmpty()) {
return null;
}
for (EReference ref : l) {
if (element.isSuperTypeOf(ref.getEReferenceType())) {
continue;
}
// check that target is accessible (part of the scope)
// i.e. link target could be either superclass or subclass of one of root classes.
for (EClass c : myResult.keySet()) {
if (c.isSuperTypeOf(ref.getEReferenceType()) || ref.getEReferenceType().isSuperTypeOf(c)) {
return ref;
}
}
}
return null;
}
public void collect() {
collect(true);
}
@SuppressWarnings("unchecked")
void collect(boolean recurse) {
Set<EClass> nonLeaves = new HashSet<EClass>();
final HashSet<EClass> leavesSet = new HashSet<EClass>();
for (Iterator it = myDomainModel.getEClassifiers().iterator(); it.hasNext();) {
Object next = it.next();
if (next instanceof EClass) {
EClass eClass = (EClass) next;
for (Map.Entry<EClass, Set<EClass>> entry : myResult.entrySet()) {
final EClass element = entry.getKey();
if (element.isSuperTypeOf(eClass)) {
entry.getValue().add(eClass);
if (recurse) {
Hierarchy h2 = new Hierarchy(eClass.getEAllContainments(), null, myDomainModel);
h2.collect(false);
myLinkClassContainmentRefs.addAll(eClass.getEAllContainments());
myAccessibleLinkClasses .addAll(h2.getAccessibleClasses());
leavesSet.addAll(h2.myAccessibleLeaves);
}
if (!eClass.isAbstract() && !eClass.isInterface()) {
myAccessibleClasses.add(eClass);
}
nonLeaves.addAll(eClass.getESuperTypes());
}
}
}
}
leavesSet.addAll(myAccessibleClasses);
leavesSet.removeAll(nonLeaves);
myAccessibleLeaves = Collections.unmodifiableSet(leavesSet);
myAccessibleClasses = Collections.unmodifiableSet(myAccessibleClasses);
myAccessibleLinkClasses = Collections.unmodifiableSet(myAccessibleLinkClasses);
}
public Set<EClass> getAllClasses() {
HashSet<EClass> rv = new HashSet<EClass>();
for (Set<EClass> next : myResult.values()) {
rv.addAll(next);
}
return rv;
}
public Set<EClass> getAccessibleClasses() {
return myAccessibleClasses;
}
public Set<EClass> getAccessibleLinkClasses() {
return myAccessibleLinkClasses;
}
public Set<EReference> getAccessibleReferences() {
return getAccessibleReferences(myAccessibleClasses.iterator());
}
public Set<EReference> getAccessibleReferences(Iterator<EClass> iter) {
HashSet<EReference> rv = new HashSet<EReference>();
for (; iter.hasNext();) {
EClass element = iter.next();
rv.addAll(collectAllNonContainment(element));
}
return rv;
}
@SuppressWarnings({"unchecked" })
List<EReference> collectAllNonContainment(EClass element) {
List<EReference> l = new LinkedList<EReference>(element.getEAllReferences());
l.removeAll(element.getEAllContainments());
return l;
}
}