package org.eclipse.jdt.apt.core.internal.generatedfile; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.apt.core.internal.AptPlugin; import org.eclipse.jdt.core.IBuffer; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.WorkingCopyOwner; /******************************************************************************* * Copyright (c) 2006, 2007 BEA Systems, Inc. * 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: * wharley@bea.com - refactored, and reinstated reconcile-time type gen *******************************************************************************/ /** * Helper utilities to create, modify, save, and discard compilation units and their * working copies. Basically, calls to ICompilationUnit. * These are encapsulated here not so much because the code is complex, but rather to * make it very clear what the algorithms are (as opposed to distributing these calls * throughout other code). All calls to the Java Model involved in generating types * should go through methods here. */ public class CompilationUnitHelper { /** * Update the contents of a working copy and commit it to disk. * @throws JavaModelException */ public void commitNewContents(ICompilationUnit wc, String contents, IProgressMonitor monitor) throws JavaModelException { IBuffer b = wc.getBuffer(); b.setContents(contents); wc.commitWorkingCopy(true, monitor); } /** * Get an in-memory working copy. This does not create the type or package on disk. * <p> * The methods called by this routine are all read-only with respect to the resource * tree, so they do not require taking any scheduling locks. Therefore we think * it's safe to call this method within a synchronized block. * @param typeName the fully qualified type name, e.g., "foo.Bar" * @param root the package fragment root within which the type will be created * @return a working copy that is ready to be modified. The working copy may not * yet be backed by a file on disk. */ public ICompilationUnit getWorkingCopy(String typeName, IPackageFragmentRoot root) { String[] names = parseTypeName(typeName); String pkgName = names[0]; String fname = names[1]; IPackageFragment pkgFragment; ICompilationUnit workingCopy = null; try { pkgFragment = root.getPackageFragment(pkgName ); workingCopy = pkgFragment.getCompilationUnit(fname); workingCopy.becomeWorkingCopy(null); } catch (JavaModelException e) { AptPlugin.log(e, "Unable to become working copy: " + typeName); //$NON-NLS-1$ return null; } if (AptPlugin.DEBUG_GFM) AptPlugin.trace( "Created working copy: root = " + //$NON-NLS-1$ root + ",\n\tfragment = " + pkgFragment + ",\n\twc = " + workingCopy); //$NON-NLS-1$ //$NON-NLS-2$ return workingCopy; } /** * Discard a working copy, ie, remove it from memory. Each call to * {@link #getWorkingCopy(String typeName, IPackageFragmentRoot root)} * must be balanced with exactly one call to this method. */ public void discardWorkingCopy(ICompilationUnit wc) { if (null == wc) return; if (AptPlugin.DEBUG_GFM) AptPlugin.trace( "discarding working copy: " + wc.getElementName()); //$NON-NLS-1$ try { wc.discardWorkingCopy(); } catch (JavaModelException e) { AptPlugin.log(e, "Unable to discard working copy: " + wc.getElementName()); //$NON-NLS-1$ } } /** * Update the contents of an existing working copy. * * @param contents * the new text. * @param reconcile * true if the changes should be reconciled. * @return true if the contents were modified as a result. */ public boolean updateWorkingCopyContents(String contents, ICompilationUnit wc, WorkingCopyOwner wcOwner, boolean reconcile) { boolean modified = true; IBuffer b = null; try { b = wc.getBuffer(); } catch (JavaModelException e) { AptPlugin.log(e, "Unable to get buffer for working copy: " + wc.getElementName()); //$NON-NLS-1$ return false; } // We need to do this diff to tell our caller whether this is a modification. // It's not obvious to me that the caller actually needs to know, so // this might just be a needless performance sink. - WHarley 11/06 modified = !contents.equals(b.getContents()); b.setContents(contents); if (AptPlugin.DEBUG_GFM_MAPS) AptPlugin.trace( "updated contents of working copy: " //$NON-NLS-1$ + wc.getElementName() + " modified = " + modified); //$NON-NLS-1$ if (reconcile && modified) { try { wc.reconcile(ICompilationUnit.NO_AST, true, wcOwner, null); } catch (JavaModelException e) { AptPlugin.log(e, "Unable to reconcile generated type: " + wc.getElementName()); //$NON-NLS-1$ } } return modified; } /** * Create a package fragment on disk. * @param pkgName the name of the package. * @param root the package fragment root under which to place the package. * @param progressMonitor * @return a package fragment, or null if there was an error. */ public IPackageFragment createPackageFragment(String pkgName, IPackageFragmentRoot root, IProgressMonitor progressMonitor) { IPackageFragment pkgFrag = null; try { pkgFrag = root.createPackageFragment(pkgName, true, progressMonitor); } catch (JavaModelException e) { AptPlugin.log(e, "Unable to create package fragment for package " + pkgName); //$NON-NLS-1$ } return pkgFrag; } /** * Given a fully qualified type name, generate the package name and the local filename * including the extension. For instance, type name <code>foo.bar.Baz</code> is * turned into package <code>foo.bar</code> and filename <code>Baz.java</code>. * * @param qualifiedName * a fully qualified type name * @return a String array containing {package name, filename} */ private String[] parseTypeName(String qualifiedName) { String[] names = new String[2]; String pkgName; String fname; int idx = qualifiedName.lastIndexOf( '.' ); if ( idx > 0 ) { pkgName = qualifiedName.substring( 0, idx ); fname = qualifiedName.substring(idx + 1, qualifiedName.length()) + ".java"; //$NON-NLS-1$ } else { pkgName = ""; //$NON-NLS-1$ fname = qualifiedName + ".java"; //$NON-NLS-1$ } names[0] = pkgName; names[1] = fname; return names; } }