package org.eclipse.gmf.codegen.util; import static org.eclipse.xtext.util.Strings.notNull; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.net.URL; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.emf.codegen.merge.java.JControlModel; import org.eclipse.emf.codegen.merge.java.JMerger; import org.eclipse.emf.codegen.util.CodeGenUtil; import org.eclipse.emf.common.util.URI; import org.eclipse.gmf.internal.common.codegen.DefaultTextMerger; import org.eclipse.gmf.internal.common.codegen.TextMerger; import org.eclipse.xtext.generator.AbstractFileSystemAccess; import org.eclipse.xtext.util.StringInputStream; import com.google.inject.Inject; /** * {@link MergeFileSystemAccess} is an implementation of {@link AbstractFileSystemAccess} that * calls a {@link TextMerger} to merge the content of Java files during the generation of files. * * @author ghillairet */ @SuppressWarnings("restriction") public class MergeFileSystemAccess extends AbstractFileSystemAccess { @Inject private IWorkspaceRoot root; @Override public void generateFile(String fileName, String slot, CharSequence contents) { IFile file = getFile(fileName, slot); try { createFolder(file.getParent()); final String defaultCharset = file.getCharset(); final String newContentAsString = postProcess(fileName, slot, contents).toString(); if (file.exists()) { StringInputStream newContent = null; try { newContent = new StringInputStream(newContentAsString, defaultCharset); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } final boolean contentChanged = hasChanged(file, newContent); if (contentChanged) { if (isJava(file)) { InputStream mergedContent = null; try { mergedContent = getMergedContent(file, newContentAsString, defaultCharset); file.setContents(mergedContent, true, true, null); } finally { if (mergedContent != null) { try { mergedContent.close(); } catch (IOException e) { e.printStackTrace(); } } } } } } else { file.create(new StringInputStream(newContentAsString, defaultCharset), true, null); } file.setDerived(true, new NullProgressMonitor()); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } catch (CoreException e) { throw new RuntimeException(e); } } @Override public URI getURI(String fileName, String outputConfiguration) { IFile file = getFile(fileName, outputConfiguration); return URI.createPlatformResourceURI(file.getFullPath().toString(), true); } protected IFile getFile(String fileName, String slot) { String outletPath = getPathes().get(slot); return root.getFile(new Path(outletPath + "/" + fileName)); } protected void createFolder(IContainer parent) throws CoreException { if(!parent.exists()) { if(!(parent instanceof IFolder)) throw new RuntimeException("IContainer " + notNull(parent) + " does not exist"); createFolder(parent.getParent()); ((IFolder)parent).create(true, false, new NullProgressMonitor()); } } private boolean hasChanged(IFile file, InputStream newContent) { boolean contentChanged = false; BufferedInputStream oldContent = null; try { if (newContent != null) { oldContent = new BufferedInputStream(file.getContents()); int newByte = newContent.read(); int oldByte = oldContent.read(); while(newByte != -1 && oldByte != -1 && newByte == oldByte) { newByte = newContent.read(); oldByte = oldContent.read(); } contentChanged = newByte != oldByte; } } catch (CoreException e) { contentChanged = true; } catch (IOException e) { contentChanged = true; } finally { if (oldContent != null) { try { oldContent.close(); } catch (IOException e) { // ignore } } // reset to offset zero allows to reuse internal byte[] try { newContent.reset(); } catch (IOException e) { // ignore } } return contentChanged; } private boolean isJava(IFile file) { return file.getFileExtension().equals("java"); } private String getStringContent(IFile file, String defaultCharset) { String oldContentAsString = null; InputStream in; try { in = file.getContents(); } catch (CoreException e) { throw new RuntimeException(e); } try { oldContentAsString = convertStreamToString(in, 2048, defaultCharset); } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } return oldContentAsString; } private InputStream getMergedContent(IFile file, String newContentAsString, String defaultCharset) { final TextMerger textMerger = createMergeService(); final String oldContentAsString = getStringContent(file, defaultCharset); final String mergedString = textMerger.mergeJava(oldContentAsString, newContentAsString); StringInputStream mergedContent = null; try { mergedContent = new StringInputStream(mergedString, defaultCharset); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return mergedContent; } protected TextMerger createMergeService() { URL controlFile = getJMergeControlFile(); if (controlFile != null) { JControlModel controlModel = new JControlModel(); controlModel.initialize(CodeGenUtil.instantiateFacadeHelper(JMerger.DEFAULT_FACADE_HELPER_CLASS), controlFile.toString()); if (!controlModel.canMerge()) { throw new IllegalStateException("Can not initialize JControlModel"); } return new DefaultTextMerger(controlModel); } return null; } protected URL getJMergeControlFile() { return MergeFileSystemAccess.class.getResource("emf-merge.xml"); } private String convertStreamToString(final InputStream is, final int bufferSize, String defaultCharset) { final char[] buffer = new char[bufferSize]; final StringBuilder out = new StringBuilder(); try { final Reader in = new InputStreamReader(is, defaultCharset); try { for (;;) { int rsz = in.read(buffer, 0, buffer.length); if (rsz < 0) break; out.append(buffer, 0, rsz); } } finally { in.close(); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return out.toString(); } }