| 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; |
| } |
| |
| } |