package de.ovgu.cide.features.source; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import de.ovgu.cide.af.Alternative; import de.ovgu.cide.features.Feature; import de.ovgu.cide.features.FeatureModelNotFoundException; import de.ovgu.cide.features.IFeature; import de.ovgu.cide.features.IFeatureModel; import de.ovgu.cide.features.IFeatureModelWithID; import de.ovgu.cide.features.IFeatureWithID; /** * default storage provider, uses .color files for each annotated file and * .dircolors for annotated directories. * * can only store features of the list or guidsl feature model that implement * the {@link IFeatureWithID} interface * * annotatedResources have to be {@link IFile} or {@link IContainer} * * @author ckaestne * */ @SuppressWarnings("deprecation") public class DefaultStorageProvider implements IStorageProvider { class DefaultStorageProviderProxy extends StorageProviderProxy { public DefaultStorageProviderProxy() { super(); name = "Default"; id = "default"; } protected void loadTarget() { target = DefaultStorageProvider.this; } } protected static IFile getColorFile(IFile javaFile) { IPath colorFilePath = javaFile.getFullPath().addFileExtension("color"); return ResourcesPlugin.getWorkspace().getRoot().getFile(colorFilePath); } protected static IFile getColorFile(IContainer directory) { return directory.getFile(new Path(DIRCOLOR_FILENAME)); } private final static long serialVersionUID = 2l; private static final long LEGACY_SERIALIZED_VERSION = 1l; public static final String DIRCOLOR_FILENAME = ".dircolors"; public Map<String, Set<IFeature>> readAnnotations(IProject project, Object annotatedResource, IFeatureModel featureModel) { assert annotatedResource instanceof IFile || annotatedResource instanceof IContainer; assert project != null; assert featureModel instanceof IFeatureModelWithID; IFile colorFile = getColorFile(annotatedResource); try { if (!colorFile.exists()) return emptyMap(); InputStream is = colorFile.getContents(true); ObjectInputStream out = new ObjectInputStream(is); try { long version = out.readLong(); if (version == LEGACY_SERIALIZED_VERSION) return loadLegacySerialization(out, (IFeatureModelWithID) featureModel); else if (version != serialVersionUID) return emptyMap(); else return loadFeatureMap(out, (IFeatureModelWithID) featureModel); } finally { out.close(); } } catch (Exception e) { e.printStackTrace(); return emptyMap(); } } private IFile getColorFile(Object annotatedResource) { IFile colorFile; if (annotatedResource instanceof IFile) colorFile = getColorFile((IFile) annotatedResource); else if (annotatedResource instanceof IContainer) colorFile = getColorFile((IContainer) annotatedResource); else throw new RuntimeException( "annotated resource is not a file or directory"); return colorFile; } private HashMap<String, Set<IFeature>> emptyMap() { return new HashMap<String, Set<IFeature>>(); } public boolean storeAnnotations(IProject project, Object annotatedResource, Map<String, Set<IFeature>> annotations, Map<String, Boolean> isOptional, Map<String, List<String>> parentIDs, IProgressMonitor monitor) throws CoreException { assert annotatedResource instanceof IFile || annotatedResource instanceof IContainer; assert project != null; IFile colorFile = getColorFile(annotatedResource); try { boolean skipStorage = annotations.isEmpty() && !colorFile.exists(); if (!skipStorage) { ByteArrayOutputStream b = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(b); out.writeLong(serialVersionUID); out.writeObject(getIdMap(annotations)); out.close(); ByteArrayInputStream source = new ByteArrayInputStream(b .toByteArray()); if (!colorFile.exists()) colorFile.create(source, true, monitor); else colorFile.setContents(source, true, true, monitor); // System.out.println("saving color file " + colorFile); } else { if (colorFile.exists()) colorFile.delete(true, monitor); } return true; } catch (IOException e) { e.printStackTrace(); return false; } } /** * the features themselfs are not serialized, only their IDs. this method * does the serialization * * @param out * @param featureModel * @return * @throws IOException * @throws ClassNotFoundException * @throws FeatureModelNotFoundException */ @SuppressWarnings("unchecked") private HashMap<String, Set<IFeature>> loadFeatureMap( ObjectInputStream out, IFeatureModelWithID featureModel) throws IOException, ClassNotFoundException { HashMap<String, Set<Long>> storedMap = (HashMap<String, Set<Long>>) out .readObject(); HashMap<String, Set<IFeature>> result = emptyMap(); for (Map.Entry<String, Set<Long>> entry : storedMap.entrySet()) { Set<Long> colorIds = entry.getValue(); if (!colorIds.isEmpty()) { Set<IFeature> features = new HashSet<IFeature>(); for (long id : colorIds) { IFeature feature = featureModel.getFeature(id); if (feature != null) features.add(feature); } if (!features.isEmpty()) result.put(entry.getKey(), features); } } return result; } /** * old serialization, where a feature class was directly serialized. needed * to be able to load old files * * @param out * @param project * @param featureModel * @return * @throws IOException * @throws ClassNotFoundException * @throws FeatureModelNotFoundException */ @SuppressWarnings("unchecked") private HashMap<String, Set<IFeature>> loadLegacySerialization( ObjectInputStream out, IFeatureModelWithID featureModel) throws IOException, ClassNotFoundException { HashMap<String, Set<Feature>> storedMap = (HashMap<String, Set<Feature>>) out .readObject(); HashMap<String, Set<IFeature>> result = emptyMap(); for (Map.Entry<String, Set<Feature>> entry : storedMap.entrySet()) { Set<Feature> colorIds = entry.getValue(); if (!colorIds.isEmpty()) { Set<IFeature> features = new HashSet<IFeature>(); for (Feature id : colorIds) { IFeature feature = featureModel.getFeature(id.getId()); if (feature != null) features.add(feature); } if (!features.isEmpty()) result.put(entry.getKey(), features); } } return result; } /** * transforms the id2colors map into a form that is serializable with * standard API objects (Long instead of IFeature) * * @param annotations * * @return */ private Map<String, Set<Long>> getIdMap( Map<String, Set<IFeature>> annotations) { HashMap<String, Set<Long>> result = new HashMap<String, Set<Long>>(); for (Map.Entry<String, Set<IFeature>> entry : annotations.entrySet()) { Set<IFeature> features = entry.getValue(); if (!features.isEmpty()) { Set<Long> ids = new HashSet<Long>(); for (IFeature feature : features) { assert feature instanceof IFeatureWithID; ids.add(((IFeatureWithID) feature).getId()); } if (!ids.isEmpty()) result.put(entry.getKey(), ids); } } return result; } public boolean isCompatible(IFeatureModel featureModel) { return featureModel instanceof IFeatureModelWithID; } public boolean activateAlternative(IProject project, Object annotatedResource, Alternative alternative, Map<String, String> id2oldText) { return false; } public boolean storeNewAlternative(IProject project, Object annotatedResource, Alternative alternative, Map<String, String> id2oldText) { return false; } public Map<String, List<Alternative>> getAllAlternatives(IProject project, Object annotatedResource, IFeatureModelWithID featureModel) { return null; } public boolean canHandleAlternatives() { return false; } }