/*******************************************************************************
* Copyright (c) 2006-2012
* Software Technology Group, Dresden University of Technology
* DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026
*
* 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:
* Software Technology Group - TU Dresden, Germany;
* DevBoost GmbH - Berlin, Germany
* - initial API and implementation
******************************************************************************/
package org.reuseware.coconut.description.classify;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.resource.impl.ExtensibleURIConverterImpl;
import org.reuseware.coconut.description.DerivationData;
import org.reuseware.coconut.description.DerivationState;
import org.reuseware.coconut.description.DescriptionFactory;
import org.reuseware.coconut.description.Facet;
import org.reuseware.coconut.description.FacetDefinition;
import org.reuseware.coconut.description.FacetType;
import org.reuseware.coconut.description.FacetValue;
import org.reuseware.coconut.description.FragmentDescription;
import org.reuseware.coconut.description.Property;
import org.reuseware.coconut.description.classify.derive.DerivationManager;
import org.reuseware.coconut.fragment.Fragment;
import org.reuseware.coconut.repository.ID;
import org.reuseware.coconut.repository.PackageableElement;
import org.reuseware.coconut.repository.RepositoryFactory;
import org.reuseware.coconut.repository.resource.ReuseResources;
import org.reuseware.coconut.repository.util.IDUtil;
import org.reuseware.coconut.ui.eclipse.resource.WorkspaceFragmentStoreManager;
/**
* This class provides a number of functionalities to work with concepts for
* describing a <code>Fragment</code> in the Reuseware meaning. These include
* <code>FragmentDescription</code> and <code>Facet</code>. <br>
* <br>
* Usually it stores all changes to these concepts into a file in the Reuseware
* store. This file needs to be registered in a Reuseware fragment store, it has
* the ending *.fdesc and the same name as the <code>Fragment</code>'s file it
* describes.
*
* @author Matthias Schmidt
*
*/
public class DescriptionManager {
/**
* The file ending of a <code>FragmentDescription</code> in the store.
*/
private static String DESCRIPTION_FILE_ENDING = "fdesc";
/**
* The name / label which is shown for the dummy fact in the GUI.
*/
public static String DUMMY_FACET_TYPE = "[Add Facet]";
/**
* The default properties.
*/
public static String[] PROPERTY_TYPES = { "Name", "Author", "Version", "Hint" };
/**
* Builds a <code>Facet</code> out of the given type and values and adds it
* to the given <code>FragmentDescription</code>.
*
* @param context
* The description the <code>Facet</code> should be added to.
* @param type
* The <code>FacetType</code> of the description's facet.
* @param values
* The <code>FacetValue</code>s which where selected for the
* given <code>FacetType</code>.
* @return <code>True</code> if the build and adding process was successful,
* else <code>false</code>;
*/
public static boolean addFacet(FragmentDescription context, FacetType type,
List<FacetValue> values) {
if (context == null || type == null || values == null)
return false;
Facet newFacet = buildFacet(type, values);
// append to FragementDescription and save it
context.getFacets().add(newFacet);
return save(context);
}
public static boolean addFragmentenToDesc(Fragment fragment) {
FragmentDescription desc = getDescriptionFor(fragment);
if (desc == null)
return false;
desc.setSubject(fragment);
return true;
}
/**
* Checks whether the given file is described by the given
* <code>FragmentDescription</code>. <br>
* <br>
* <b>Hint:</b><br>
* The file needs to be a <code>Fragment</code> in the Reuseware context and
* needs to be registered in the store.
*
* @param fragmentFile
* The file to check.
* @param potentialContext
* The <code>FragmentDescription</code> object which could be the
* description for the give file.
* @return <code>True</code> if both belong together, else
* <code>false</code>.
*/
public static boolean belongTogether(IFile fragmentFile, FragmentDescription potentialContext) {
if (potentialContext == null)
return false;
// get Fragment's URI
URI fragURI = WorkspaceFragmentStoreManager.getURI(fragmentFile);
// open corresponding FragmentDescription file
FragmentDescription fDesc = DescriptionManager.getDescription(DescriptionManager
.getDescriptionURI(fragURI));
if (fDesc == null)
return false;
return potentialContext.equals(fDesc);
}
/**
* Takes a FragmentDescription and builds the it's inner structure.<br>
* Most importantly the Fragment is added to it.
*
* @see FragmentDescription
*
* @param desc
* The description in focus.
* @return The updated FragmentDescription.
*/
public static FragmentDescription build(FragmentDescription desc) {
System.out.println("try build");
return desc;
}
/**
* Simply build an <code>Facet</code> instance out of the given details.
*
* @param type
* The <code>Facet</code>'s type.
* @param values
* The <code>FacetValue</code>s which where selected for the
* given <code>FacetType</code>.
* @return The new created <code>Facet</code> object.
*/
public static Facet buildFacet(FacetType type, List<FacetValue> values) {
Facet newFacet = DescriptionFactory.eINSTANCE.createFacet();
// set FacetType and typeID
newFacet.setType(type);
newFacet.getTypeID().addAll(FacetManager.getTypeID(type).getSegments());
// set FacetValues and valuesNames
newFacet.getValues().addAll(values);
for (FacetValue facetValue : values)
newFacet.getValueNames().add(facetValue.getName());
return newFacet;
}
/**
* Creates a <code>FragmentDescription</code> object out of details stored
* in the given file. <br>
* <br>
* <b>Hint:</b><br>
* The file needs to be in a Reuseware store.
*
* @param fragmentFile
* The file which intents to be the on which stores the
* <code>FragmentDescription</code>.
* @return A new created <code>FragmentDescription</code> object, or
* <code>null</code> if anything goes wrong.
*/
public static FragmentDescription createDescription(IFile fragmentFile) {
return build(fragmentFile, true);
}
/**
* Retrieves a <code>List</code> of <code>FragmentDescription</code>s which
* represents all registered descriptions in the Reuseware store.
*
* @return All available <code>FragmentDescription</code>s.
*/
public static List<FragmentDescription> getRegisteredDescriptions() {
List<FragmentDescription> descriptions = new LinkedList<FragmentDescription>();
FragmentDescription desc;
for (PackageableElement packedElement : ReuseResources.INSTANCE.getRootPackage()
.getAllElements()) {
if (packedElement instanceof FragmentDescription) {
desc = (FragmentDescription) packedElement;
refresh(desc);
descriptions.add(desc);
}
}
return descriptions;
}
/**
* Allows the client to edit a given <code>Facet</code>.
*
* @param context
* The <code>FragmentDescription</code> which owns the
* <code>Facet</code>.
* @param editFacet
* The <code>Facet</code> which should be changed.
* @param newValues
* The <code>FacetValue</code>s which where selected to change
* the <code>Facet</code>.
*/
public static void editFacet(FragmentDescription context, Facet editFacet,
EList<FacetValue> newValues) {
if (context == null || editFacet == null || newValues == null)
return;
removeFacet(context, editFacet);
addFacet(context, editFacet.getType(), newValues);
}
/**
* The dummy facet is a place holder for the GUI and is simply used to allow
* adding new <code>Facet</code>s to the <code>FragmentDescription</code>. <br>
* <br>
* This method checks whether the given <code>Object</code> is the dummy
* facet.
*
* @param obj
* The <code>Object</code> to check.
* @return <code>True</code> if it is a dummy facet, else <code>false</code>
* .
*/
public static boolean isDummyFacet(Object obj) {
if (obj == null || !(obj instanceof Facet))
return false;
Facet facet = (Facet) obj;
return facet.getTypeID().get(0).equals(DUMMY_FACET_TYPE);
}
/**
* Derived <code>Facets</code>s are facets with values which were calculated
* out of the <code>Fragment</code> linked to the
* <code>FragmentDescription</code> and <code>DerivationData</code> linked
* to the <code>FacetType</code>.
*
* @see DerivationData
*
* @param obj
* The <code>Object</code> to test.
* @return <code>True</code> if it is a derived facet, else
* <code>false</code> .
*/
public static boolean isDerivedFacet(Object obj) {
if (obj == null || !(obj instanceof Facet))
return false;
Facet facet = (Facet) obj;
return FacetManager.isDerivableType(facet.getType());
}
/**
* Builds the inner structure of a newly created
* <code>FragmentDescription</code> object. The information needed is read
* out of the *.fdesc file in the Reuseware store.
*
* @param fDesc
* The fragment description which needs a new inner structure.
*/
public static void loadDescription(FragmentDescription fDesc) {
// try to add the subject!
if (fDesc.getSubject() == null)
fDesc.setSubject(getFragmentFor(fDesc));
// solve FacetType and FavetValue ids
solveFacets(fDesc);
// add some more facets (derived by the fragment)
addDeriveFacets(fDesc);
// add the dummy facet
addDummyFacet(fDesc);
}
/**
* Opens the <code>FragmentDescription</code> for the given
* <code>Fragment</code> file.
*
* @param fragmentFile
* The file which stores the <code>Fragment</code>.
* @return The new <code>FragmentDescription</code> object or
* <code>null</code> if anything went wrong.
*/
public static FragmentDescription open(IFile fragmentFile) {
return build(fragmentFile, false);
}
/**
* Sets the <code>FragmentDescription</code>'s subject attribute and derives
* new facets.<br>
* Both if needed!
*
* @param desc
* The description to change.
*/
public static void refresh(FragmentDescription desc) {
if (desc == null)
return;
// refresh the subject
if (desc.getSubject() == null) {
Fragment fragment = getFragmentFor(desc);
if (fragment == null)
return;
desc.setSubject(fragment);
}
// derive facets
addDeriveFacets(desc);
}
/**
* Deletes a <code>Facet</code> from the given
* <code>FragmentDescription</code>.
*
* @param context
* The <code>FragmentDescription</code> which owns the
* <code>Facet</code>.
* @param selectedFacet
* The one to delete.
*/
public static void removeFacet(FragmentDescription context, Facet selectedFacet) {
if (selectedFacet == null || isDummyFacet(selectedFacet))
return;
context.getFacets().remove(selectedFacet);
save(context);
}
/**
* Stores the given <code>FragmentDescription</code> into a file.
*
* @param fDesc
* The <code>FragmentDescription</code> recently changed.
* @return <code>True</code> if the process was successful, else
* <code>false</code>.
*/
public static boolean save(FragmentDescription fDesc) {
if (fDesc.eResource().getURI() == null)
return false;
System.err.println("**Save**");
return save(fDesc, fDesc.eResource().getURI());
}
/**
* Stores the given <code>FragmentDescription</code> into a file described
* by the <code>URI</code>.
*
* @param fDesc
* The <code>FragmentDescription</code> recently changed.
* @param uri
* The <code>URI</code> which identifies the *.fdesc file.
* @return <code>True</code> if the process was successful, else
* <code>false</code>.
*/
public static boolean save(FragmentDescription fDesc, URI uri) {
// don't store ID in the file
ID id = fDesc.getID();
fDesc.setID(null);
// don't store dummy facet
Facet dummy = null;
for (Facet facet : fDesc.getFacets()) {
if (isDummyFacet(facet)) {
dummy = facet;
break;
}
}
if (dummy != null)
fDesc.getFacets().remove(dummy);
// don't store derived facets
List<Facet> derivedFacets = new LinkedList<Facet>();
for (Facet facet : fDesc.getFacets()) {
if (isDerivedFacet(facet)) {
derivedFacets.add(facet);
fDesc.getFacets().remove(facet);
}
}
// begin serialization
ResourceSet rs = ReuseResources.INSTANCE.getResourceSet();
Resource res = rs.createResource(uri);
try {
res.unload();
res.getContents().add(fDesc);
res.save(null);
} catch (IOException e) {
e.printStackTrace();
return false;
}
// bring back in the data, which should not be stored in the file
fDesc.setID(id);
if (dummy != null)
fDesc.getFacets().add(dummy);
fDesc.getFacets().addAll(derivedFacets);
return true;
}
public static void setDirty(FragmentDescription fDesc){
fDesc.setDerivationState(DerivationState.REFRESH_ALL);
}
private static void addDeriveFacets(FragmentDescription focus) {
if (focus.getDerivationState().equals(DerivationState.UP_TO_DATE))
return;
focus = DerivationManager.getInstance().derive(focus);
focus.setDerivationState(DerivationState.UP_TO_DATE);
}
private static void addProperties(FragmentDescription focus) {
for (String type : PROPERTY_TYPES) {
Property property = DescriptionFactory.eINSTANCE.createProperty();
property.setName(type);
if (type.equals("Name")) {
ID id = focus.getSubject().getID();
String name = id.segment(-1);
name = name.substring(0, name.lastIndexOf('.'));
property.setValue(name);
} else
property.setValue("");
focus.getProperties().add(property);
}
}
private static FragmentDescription build(IFile fragmentFile, boolean createNew) {
// get the Fragment as subject
URI fragURI = WorkspaceFragmentStoreManager.getURI(fragmentFile);
Fragment subject = DescriptionManager.getFragment(fragURI);
if (subject == null)
return null;
// try to open existing FragmentDescription file
FragmentDescription fDesc = DescriptionManager.getDescription(DescriptionManager
.getDescriptionURI(fragURI));
if (fDesc != null) {
// opening was successful, first set the subject
fDesc.setSubject(subject);
loadDescription(fDesc);
} else if (createNew) {
// no FragmentDescription found, create a new one from scratch
fDesc = DescriptionFactory.eINSTANCE.createFragmentDescription();
// set ID
String idString = ReuseResources.INSTANCE.getLogicalURI(getDescriptionURI(fragURI))
.toString();
idString = idString.substring(idString.indexOf("/") + 1, idString.length());
System.out.println("idString: " + idString);
fDesc.setIDAsString(idString);
// set the subject
fDesc.setSubject(subject);
// set the initial list of properties
addProperties(fDesc);
// set some facets (inferred by the fragment)
// addDeriveFacets(fDesc);
// add the dummy facet
addDummyFacet(fDesc);
// try to save FragementDescription
URI descURI = DescriptionManager.getDescriptionURI(fragURI);
if (!DescriptionManager.save(fDesc, descURI))
fDesc = null;
}
return fDesc;
}
private static void addDummyFacet(FragmentDescription desc) {
for (Facet facet : desc.getFacets()) {
if (isDummyFacet(facet))
return;
}
Facet dummyFacet = DescriptionFactory.eINSTANCE.createFacet();
dummyFacet.getTypeID().add(DUMMY_FACET_TYPE);
dummyFacet.getValueNames().add("");
FacetType dummyType = DescriptionFactory.eINSTANCE.createListFacet();
dummyType.setName(DUMMY_FACET_TYPE);
dummyType.setDescription("A Placeholder");
FacetValue dummyValue = DescriptionFactory.eINSTANCE.createFacetValue();
dummyValue.setName("");
dummyValue.setDescription("");
dummyFacet.setType(dummyType);
dummyFacet.getValues().add(dummyValue);
desc.getFacets().add(dummyFacet);
}
private static URI getDescriptionURI(URI fragmentURI) {
URIConverter internalURIConverter = new ExtensibleURIConverterImpl();
URI descURI = internalURIConverter.normalize(fragmentURI);
descURI = descURI.appendFileExtension(DESCRIPTION_FILE_ENDING);
// descURI = descURI.appendFileExtension(
// DESCRIPTION_FILE_ENDING);
return descURI;
}
private static URI getFragmentURI(URI descURI) {
URIConverter internalURIConverter = new ExtensibleURIConverterImpl();
URI fragmentURI = internalURIConverter.normalize(descURI);
if (!descURI.fileExtension().equals(DESCRIPTION_FILE_ENDING)) {
// description URI's format not correct
System.err.println("Error!");
return null;
}
try {
fragmentURI = descURI.trimFileExtension();
} catch (IllegalArgumentException e) {
e.printStackTrace();
return null;
}
return fragmentURI;
}
private static PackageableElement getElement(URI fragURI) {
if (fragURI == null)
return null;
URI logic = ReuseResources.INSTANCE.getLogicalURI(fragURI);
if (logic == null)
return null;
PackageableElement element = ReuseResources.INSTANCE.getElement(
IDUtil.uriToID(logic));
return element;
}
private static Fragment getFragment(URI fragURI) {
PackageableElement element = DescriptionManager.getElement(fragURI);
if (element instanceof Fragment)
return (Fragment) element;
return null;
}
private static FragmentDescription getDescription(URI descURI) {
PackageableElement element = DescriptionManager.getElement(descURI);
if (element instanceof FragmentDescription) {
FragmentDescription fDesc = (FragmentDescription) element;
return fDesc;
}
return null;
}
private static FragmentDescription getDescriptionFor(Fragment fragment) {
if (fragment == null)
return null;
URI fragURI = null;
if (fragment.eResource() != null)
fragURI = fragment.eResource().getURI();
else {
fragURI = IDUtil.idToURI(fragment.getID());
}
if (fragURI == null)
return null;
URI descURI = getDescriptionURI(fragURI);
if (descURI == null)
return null;
return getDescription(descURI);
}
private static Fragment getFragmentFor(FragmentDescription desc) {
if (desc == null)
return null;
URI descURI;
if (desc.eResource() != null)
descURI = desc.eResource().getURI();
else {
if (desc.getID() == null) {
System.err.println("Getting the fragment for the description was not possible.");
return null;
}
descURI = IDUtil.idToURI(desc.getID());
}
if (descURI == null)
return null;
URI fragURI = getFragmentURI(descURI);
if (fragURI == null)
return null;
return getFragment(fragURI);
}
/**
* Uses a FacetType id string to retrieve the right FacetType instance. <br>
* This instance needs to be registered with the ReuseResources object and
* the id string needs to be in the following format: <br>
* <br>
* {s0,s1,...,sn-1,sn | s0...sn-1=FacetDefinition.getID(),
* sn=FacetType.getName()}
*
* @param id
* The <code>List</code> of <code>String</code>s describing the
* FacetType we look for.
* @return
*/
private static FacetType getTypeByID(ID id) {
if (id == null || id.getSegments().size() == 0)
return null;
FacetType back = null;
String facetTypeName = id.getSegments().remove(id.getSegments().size() - 1);
// search for FacetDefinition in registered Elements
PackageableElement elem = ReuseResources.INSTANCE.getElement(id);
if (elem instanceof FacetDefinition) {
// browse FacetType names for given type name
FacetDefinition fDef = (FacetDefinition) elem;
for (FacetType fType : fDef.getFacetTypes()) {
if (fType.getName().equals(facetTypeName)) {
back = fType;
break;
}
}
}
id.getSegments().add(facetTypeName);
return back;
}
private static List<FacetValue> getValuesByID(EList<String> valueList, FacetType facetType) {
List<FacetValue> back = new ArrayList<FacetValue>();
if (facetType == null || valueList == null || valueList.size() == 0)
return back;
// browse the FacetType and look for the given FacetValue name
for (String value : valueList) {
for (FacetValue fValue : facetType.getValues()) {
if (fValue.getName().equals(value)) {
back.add(fValue);
break;
}
}
}
return back;
}
private static boolean solveFacets(FragmentDescription desc) {
// return if there are no facets stored
if (desc == null || desc.getFacets() == null || desc.getFacets().size() == 0)
return false;
// for each facet: add facetValue and facetType
List<Facet> facets = desc.getFacets();
for (Facet facet : facets) {
if (facet.getType() == null) {
ID typeID = RepositoryFactory.eINSTANCE.createID();
typeID.getSegments().addAll(facet.getTypeID());
facet.setType(getTypeByID(typeID));
}
facet.getValues().addAll(getValuesByID(facet.getValueNames(), facet.getType()));
}
return true;
}
// public URI getURIFor(List<String> id) {
// if (id == null)
// return null;
//
// URI back = ReuseResources.REUSEWARE_BASE_URI;
// try {
// String[] template = {};
// back = back.appendSegments(id.toArray(template));
// } catch (IllegalArgumentException e) {
// return null;
// }
//
// return back;
// }
}