Skip to content
Snippets Groups Projects
Select Git revision
  • c9a4039d74a6b50878aa4a833feda31a5f0da76f
  • develop default protected
  • master protected
  • 3.6.0
  • 3.5.1
  • 3.5.0
  • 3.4.1
  • 3.4.0
8 results

PersistenceHelper.java

Blame
  • user avatar
    Sebastian Krings authored
    c9a4039d
    History
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    PersistenceHelper.java 11.85 KiB
    /** 
     * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
     * Heinrich Heine Universitaet Duesseldorf
     * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
     * */
    
    package org.eventb.texttools;
    
    import java.io.IOException;
    import java.util.Collections;
    
    import org.eclipse.core.resources.IResource;
    import org.eclipse.core.resources.ResourcesPlugin;
    import org.eclipse.core.runtime.CoreException;
    import org.eclipse.core.runtime.IProgressMonitor;
    import org.eclipse.core.runtime.IStatus;
    import org.eclipse.core.runtime.Status;
    import org.eclipse.emf.common.util.EList;
    import org.eclipse.emf.common.util.EMap;
    import org.eclipse.emf.common.util.URI;
    import org.eclipse.emf.ecore.EClass;
    import org.eclipse.emf.ecore.EObject;
    import org.eclipse.emf.ecore.resource.Resource;
    import org.eventb.emf.core.Attribute;
    import org.eventb.emf.core.AttributeType;
    import org.eventb.emf.core.CoreFactory;
    import org.eventb.emf.core.EventBElement;
    import org.eventb.emf.core.EventBNamedCommentedComponentElement;
    import org.eventb.texttools.merge.ModelMerge;
    import org.eventb.texttools.prettyprint.PrettyPrinter;
    
    import de.be4.eventb.core.parser.BException;
    import de.be4.eventb.core.parser.EventBParser;
    import de.be4.eventb.core.parser.node.AContextParseUnit;
    import de.be4.eventb.core.parser.node.AMachineParseUnit;
    import de.be4.eventb.core.parser.node.PParseUnit;
    import de.be4.eventb.core.parser.node.Start;
    
    public class PersistenceHelper {
    
    	public static final Boolean DEBUG = false;
    
    	public static IResource getIResource(final Resource resource) {
    		final URI uri = resource.getURI();
    		if (uri.isPlatformResource()) {
    			return ResourcesPlugin.getWorkspace().getRoot()
    					.findMember(uri.toPlatformString(true));
    		}
    
    		return null;
    	}
    
    	public static void saveText(final Resource resource,
    			final boolean overwrite, final IProgressMonitor monitor)
    			throws CoreException {
    		try {
    			resource.save(Collections.EMPTY_MAP);
    
    			/*
    			 * Try to set timestamp to the same as in the annotation. Setting on
    			 * both Resource and IResource to be save.
    			 */
    			// final long textTimestamp = getTextTimestamp(resource);
    			// REMOVED WORKAROUND BELOW: a better fix is to not ignore
    			// attributes in EventBReferencesCheck
    			// --- TODO: Workaround for save bug. The method getTextTimestamp
    			// --- returns in some cases -1 (no time annotation exists). I
    			// noticed
    			// --- that many different instances of the same resource exist.
    			// Some
    			// --- are annotated, the others not.
    			long textTimestamp = getTextTimestamp(resource);
    			// if(textTimestamp == -1)
    			// textTimestamp = System.currentTimeMillis();
    			resource.setTimeStamp(textTimestamp);
    			getIResource(resource).setLocalTimeStamp(textTimestamp);
    		} catch (final IOException e) {
    			throw new CoreException(new Status(IStatus.ERROR,
    					TextToolsPlugin.PLUGIN_ID, "Saving to RodinDB failed", e));
    		}
    	}
    
    	public static void addTextAnnotation(final Resource resource,
    			final String textRepresentation, final long timeStamp)
    			throws CoreException {
    		final EventBNamedCommentedComponentElement component = getComponent(resource);
    		if (component != null) {
    			addTextAnnotation(component, textRepresentation, timeStamp);
    		} else {
    			throw new CoreException(new Status(IStatus.ERROR,
    					TextToolsPlugin.PLUGIN_ID,
    					"Resource has no EventBComponent"));
    		}
    	}
    
    	public static void addTextAnnotation(final EventBElement element,
    			final String textRepresentation, final long timeStamp) {
    		final EMap<String, Attribute> attributes = element.getAttributes();
    
    		// update text representation
    		Attribute textAttribute = attributes
    				.get(TextToolsPlugin.TYPE_TEXTREPRESENTATION.getId());
    		if (textAttribute == null) {
    			textAttribute = CoreFactory.eINSTANCE.createAttribute();
    			textAttribute.setType(AttributeType.STRING);
    			attributes.put(TextToolsPlugin.TYPE_TEXTREPRESENTATION.getId(),
    					textAttribute);
    		}
    		textAttribute.setValue(textRepresentation);
    
    		// update timestamp
    		Attribute timeAttribute = attributes
    				.get(TextToolsPlugin.TYPE_LASTMODIFIED.getId());
    		if (timeAttribute == null) {
    			timeAttribute = CoreFactory.eINSTANCE.createAttribute();
    			timeAttribute.setType(AttributeType.LONG);
    			attributes.put(TextToolsPlugin.TYPE_LASTMODIFIED.getId(),
    					timeAttribute);
    		}
    		timeAttribute.setValue(timeStamp);
    	}
    
    	public static void addUsesAnnotation(final EventBElement element,
    			final String usesStatements) {
    		final EMap<String, Attribute> attributes = element.getAttributes();
    		Attribute usesAttribute = attributes
    				.get(TextToolsPlugin.TYPE_USESEXTENSION.getId());
    		if (usesAttribute == null) {
    			usesAttribute = CoreFactory.eINSTANCE.createAttribute();
    			usesAttribute.setType(AttributeType.STRING);
    			attributes.put(TextToolsPlugin.TYPE_USESEXTENSION.getId(),
    					usesAttribute);
    		}
    		usesAttribute.setValue(usesStatements);
    
    	}
    
    	private static void mergeComponents(
    			final EventBNamedCommentedComponentElement oldVersion,
    			final EventBNamedCommentedComponentElement newVersion,
    			final IProgressMonitor monitor) {
    		try {
    			long time0 = System.currentTimeMillis();
    			final ModelMerge merge = new ModelMerge(oldVersion, newVersion);
    			long time1 = System.currentTimeMillis();
    			merge.applyChanges(monitor);
    			long time2 = System.currentTimeMillis();
    			if (DEBUG) {
    				System.out.println("new ModelMerge: " + (time1 - time0));
    				System.out.println("merge.applyChanges: " + (time2 - time1));
    			}
    		} catch (final InterruptedException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    
    	public static void mergeRootElement(final Resource resource,
    			final EventBNamedCommentedComponentElement newVersion,
    			final IProgressMonitor monitor) {
    		final EventBNamedCommentedComponentElement component = getComponent(resource);
    		if (component != null) {
    			// FIXME Hier stimmt die Reihenfolge noch
    			long start = System.currentTimeMillis();
    			mergeComponents(component, newVersion, monitor);
    			long end = System.currentTimeMillis();
    			if (DEBUG) {
    				System.out
    						.println("Time to merge components: " + (end - start));
    			}
    			// Hier stimmt die Reihenfolge in component nicht mehr
    		} else {
    			resource.getContents().add(newVersion);
    		}
    	}
    
    	public static String loadText(final Resource resource,
    			final String linebreak) throws IOException {
    		// make sure resource is loaded
    		if (!resource.isLoaded()) {
    			resource.load(Collections.EMPTY_MAP);
    		}
    
    		// check if we have the most recent text representation
    		if (isTextUptodate(resource)) {
    			// we should find a text representation in the EMF
    			final String text = getTextAnnotation(resource);
    
    			/*
    			 * workaround for Bug #3305107
    			 * 
    			 * When a machine- or contextfile is renamed the lastmodified date
    			 * does not change. Since isTextUptodate() compares timestamps only,
    			 * it returns true for renamed files.
    			 */
    			boolean namesMatch = true;
    			final EventBNamedCommentedComponentElement rootElement = getComponent(resource);
    			if (rootElement != null) {
    				final EventBParser parser = new EventBParser();
    				try {
    					Start start = parser.parse(text, false);
    					System.out.println(start);
    					PParseUnit pParseUnit = start.getPParseUnit();
    					String parsedName = null;
    					if (pParseUnit instanceof AMachineParseUnit) {
    						parsedName = ((AMachineParseUnit) start.getPParseUnit())
    								.getName().getText();
    					}
    					if (pParseUnit instanceof AContextParseUnit) {
    						parsedName = ((AContextParseUnit) start.getPParseUnit())
    								.getName().getText();
    					}
    
    					if (parsedName != null) {
    						if (!parsedName.equals(rootElement.getName())) {
    							namesMatch = false;
    							System.err
    									.println("Conflicting names of ParseUnit! Expected name: '"
    											+ rootElement.getName()
    											+ "' actual name: '"
    											+ parsedName
    											+ "'! Prettyprinting unit...");
    						}
    					}
    				} catch (BException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    			/* End of woraround */
    
    			if (text != null && namesMatch) {
    				return text;
    			}
    		}
    
    		/*
    		 * Reload resource to get latest changes. This is necessary if the model
    		 * was already loaded and external changes have occured meanwhile. Then
    		 * unloading+loading makes sure the EMF persistence loads those changes.
    		 */
    		resource.unload();
    		resource.load(Collections.EMPTY_MAP);
    
    		// pretty-print the machine/context
    		final EventBNamedCommentedComponentElement rootElement = getComponent(resource);
    		if (rootElement != null) {
    			return getPrettyPrint(rootElement, linebreak);
    		} else {
    			throw new IOException(
    					"Cannot find load Event-B component: No machine/context found");
    		}
    	}
    
    	public static String getTextAnnotation(final Resource resource) {
    		final EMap<String, Attribute> attributes = getAttributesMap(resource);
    		if (attributes != null) {
    			final Attribute attr = attributes
    					.get(TextToolsPlugin.TYPE_TEXTREPRESENTATION.getId());
    
    			if (attr != null) {
    				return (String) attr.getValue();
    			}
    		}
    
    		return null;
    	}
    
    	public static EClass getComponentType(final Resource resource) {
    		final EventBNamedCommentedComponentElement component = getComponent(resource);
    
    		if (component != null) {
    			return component.eClass();
    		}
    
    		return null;
    	}
    
    	private static EventBNamedCommentedComponentElement getComponent(
    			final Resource resource) {
    		final EList<EObject> contents = resource.getContents();
    		if (contents.size() > 0
    				&& contents.get(0) instanceof EventBNamedCommentedComponentElement) {
    			return (EventBNamedCommentedComponentElement) contents.get(0);
    		}
    
    		return null;
    	}
    
    	/**
    	 * Extracts the timestamp of the latest saved text representation from the
    	 * EMF and returns it.
    	 * 
    	 * @param resource
    	 * @return timestamp or <code>-1</code> if none is found
    	 */
    	private static long getTextTimestamp(final Resource resource) {
    		final EMap<String, Attribute> attributes = getAttributesMap(resource);
    		if (attributes != null) {
    			final Attribute attr = attributes
    					.get(TextToolsPlugin.TYPE_LASTMODIFIED.getId());
    			return attr != null ? (Long) attr.getValue() : -1;
    		}
    
    		return -1;
    	}
    
    	private static EMap<String, Attribute> getAttributesMap(
    			final Resource resource) {
    		final EList<EObject> contents = resource.getContents();
    		if (contents.size() > 0) {
    			final EObject object = contents.get(0);
    			if (object instanceof EventBNamedCommentedComponentElement) {
    				final EventBNamedCommentedComponentElement component = (EventBNamedCommentedComponentElement) object;
    				return component.getAttributes();
    			}
    		}
    
    		return null;
    	}
    
    	/**
    	 * Checks if the text representation saved in the EMF is up-to-date. The
    	 * timestamps in the EMF and of the underlying file are compared for this
    	 * decision.
    	 * 
    	 * @param resource
    	 * @return <code>true</code> if there was no external change and the text
    	 *         representation is still up-to-date
    	 */
    	private static boolean isTextUptodate(final Resource resource) {
    		final long textTimestamp = getTextTimestamp(resource);
    
    		if (true) {
    			// return false;
    		}
    
    		try {
    			final IResource file = getIResource(resource);
    			// refresh to get latest timestamp
    			file.refreshLocal(IResource.DEPTH_ONE, null);
    			final long resourceTimestamp = file.getLocalTimeStamp();
    
    			// easy case: text is newer than resource
    			if (textTimestamp >= resourceTimestamp) {
    				return true;
    			}
    
    			final long diff = resourceTimestamp - textTimestamp;
    
    			// tolerate 50ms offset (time to save file)
    			// TODO this is ugly!!!
    			if (diff < 50) {
    				return true;
    			}
    		} catch (final CoreException e) {
    			TextToolsPlugin
    					.getDefault()
    					.getLog()
    					.log(new Status(IStatus.ERROR, TextToolsPlugin.PLUGIN_ID,
    							"Error checking file timestamps", e));
    		}
    
    		return false;
    	}
    
    	private static String getPrettyPrint(final EventBElement rootElement,
    			final String linebreak) {
    		final StringBuilder buffer = new StringBuilder();
    		new PrettyPrinter(buffer, linebreak, null).prettyPrint(rootElement);
    
    		return buffer.toString();
    	}
    }