|
What this is
Other links
The source code/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.javacore.jmiimpl.javamodel; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.IOException; import java.io.OutputStream; import java.io.Reader; import java.util.*; import javax.jmi.reflect.*; import javax.swing.text.Position; import javax.swing.text.StyledDocument; import org.netbeans.jmi.javamodel.*; import org.netbeans.lib.java.parser.*; import org.netbeans.api.mdr.MDRepository; import org.netbeans.mdr.NBMDRepositoryImpl; import org.netbeans.mdr.handlers.AttrListWrapper; import org.netbeans.mdr.persistence.StorageException; import org.netbeans.mdr.storagemodel.StorableObject; import org.netbeans.modules.javacore.internalapi.JavaMetamodel; import org.netbeans.modules.javacore.*; import org.netbeans.modules.javacore.parser.*; import org.openide.ErrorManager; import org.openide.filesystems.FileLock; import org.openide.filesystems.FileObject; import org.openide.loaders.DataObject; import org.openide.loaders.MultiDataObject; import org.openide.text.EditorSupport; import org.openide.text.NbDocument; import org.openide.text.PositionBounds; import org.openide.text.PositionRef; import org.netbeans.modules.javacore.scanning.ClassUpdater; import org.netbeans.modules.javacore.scanning.JavaUpdater; /** * Implementation of Resource interface. * * @author Martin Matula */ public abstract class ResourceImpl extends SemiPersistentElement implements Resource { // this switch enable test JavaMetamodel without bridge and persistency behaviour public static boolean READ_ONLY = false; // java features flags public static final int HAS_GENERICS = 1; public static final int HAS_ENUMS = 2; public static final int HAS_ANNOTATIONTYPES = 4; public static final int HAS_ANNOTATION = 8; public static final int HAS_SYNTAX_ERROR = 16; // status indicating that the resource represents a classfile public static final int IS_FROM_CLASSFILE = 0x80000000; public static final String CLASSIFIERS_ATTR = "classifiers"; // NOI18N private static final ElementInfo DEFAULT_INFO = new ResourceInfo(null, ResourceInfo.RESOURCE_TYPE, null, null, null); private LightAttrList classifiers = null; private LightAttrList imports = null; private FileObject fileToDeleteOnRollback = null; private MultipartId packageIdentifier = null; private boolean elementsInited = false; private List invertedDiffs = null; // rollback related info private String rbText = null; private List rbList = null; private List errors = null; /** Creates a new instance of ResourceImpl */ public ResourceImpl(StorableObject s) { super(s); } private static String lastPackageName = null; private static JavaPackage lastPackage = null; private static JavaPackageClass lastPackageProxy = null; public void _setPackageName(String name) { if (JMManager.INCONSISTENCY_DEBUG) System.err.println("ResourceImpl: Setting package name of resource " + getName() + " to " + name); JavaPackage oldPackage = null; try { oldPackage = (JavaPackage) ((NBMDRepositoryImpl) repository()).getHandler(((StorableObject) _getDelegate()).getImmediateComposite()); } catch (StorageException e) { ErrorManager.getDefault().notify(e); } JavaPackageClassImpl pkgProxy = (JavaPackageClassImpl) ((JavaModelPackage) refImmediatePackage()).getJavaPackage(); if (oldPackage != null) { if (name.equals(oldPackage.getName())) return; // [TODO] simplify this after getResources on proxy package is fixed oldPackage.getResources().remove(this); } if (lastPackage == null || !name.equals(lastPackageName) || !pkgProxy.equals(lastPackageProxy)) { lastPackage = pkgProxy.resolveRealPackage(name); lastPackageName = name; lastPackageProxy = pkgProxy; } lastPackage.getResources().add(this); } public void setPackageName(String name) { String oldName = getPackageName(); if (!name.equals(oldName)) { MultipartIdClass proxy = ((JavaModelPackage) refImmediatePackage()).getMultipartId(); setPackageIdentifier(proxy.createMultipartId(name, null, null)); } } private void setPkgNameAndUpdateIdx(String name) { _setPackageName(name); for (Iterator it = getClassifiers().iterator(); it.hasNext();) { Object temp = it.next(); if (temp instanceof JavaClassImpl) { JavaClassImpl cls = (JavaClassImpl) temp; String interName = name.length() == 0 ? "" : name + '.'; cls.internalSetName(interName.concat(cls.getSimpleName())); } } } public Collection getReferences() { return Collections.EMPTY_LIST; } public boolean classifiersInited() { return classifiers != null; } public void reinitClassifiers() { assert classifiers!=null; classifiers.setInnerList(getPersistentClassifiers(), false); } public List getClassifiers() { if (classifiers == null) { checkUpToDate(); classifiers = createChildrenList(CLASSIFIERS_ATTR, (AttrListWrapper) super_getClassifiers(), null, CHANGED_CLASSIFIERS); // NOI18N } return classifiers; } public List getPersistentClassifiers() { AttrListWrapper list = (AttrListWrapper) super_getClassifiers(); list.setAttrName(CLASSIFIERS_ATTR); // NOI18N return list; } protected abstract List super_getClassifiers(); public List getNakedClassifiers() { try { return (List) ((StorableObject) _getDelegate()).getAttribute(CLASSIFIERS_ATTR); // NOI18N } catch (StorageException e) { throw (GeneralException) ErrorManager.getDefault().annotate(new GeneralException(e.getMessage()), e); } } public String getPackageName() { JavaPackage pkg = (JavaPackage) _realImmediateComposite(); return pkg == null ? null : pkg.getName(); } // protected abstract void super_setPackageName(String name); /** Should be overriden by elements that have persistent attributes. * They should implement this method to compare values in newly passed AST info with * values of the persistent attributes and if the values do not match, they should be * updated and proper events should be fired. */ protected void matchPersistent(ElementInfo info) { super.matchPersistent(info); if (!isPersisted()) { setPersisted(true); } processMembers(getClassifiers(), ((ResourceInfo) info).classes); } public boolean isPersisted() { return _getDelegate().getSlot1() != null; } public void setPersisted(boolean persisted) { _getDelegate().setSlot1(persisted ? "" : null); } protected ElementInfo getDefaultInfo() { return DEFAULT_INFO; } /** The method has to make sure that the AST infos of children are also updated. */ protected void matchElementInfo(ElementInfo newInfo) { Iterator classIt; ResourceInfo astInfo; int i=0; super.matchElementInfo(newInfo); astInfo=(ResourceInfo)newInfo; if (imports != null) { processMembers(getImports(),astInfo.imports); } resetASTElements(); } protected void resetChildren() { if (childrenInited) { resetASTElements(); initChildren(true); } } protected List getInitedChildren() { List list=new ArrayList(); if (childrenInited) { list.addAll(getImports()); list.addAll(getClassifiers()); } if (elementsInited) { addIfNotNull(list, packageIdentifier); } return list; } public List getChildren() { List list = new ArrayList(); addIfNotNull(list, getPackageIdentifier()); list.addAll(getImports()); list.addAll(getClassifiers()); return list; } /** * Returns the value of attribute imports. * @return Value of imports attribute. */ public List getImports() { if (!childrenInited) { initChildren(); } return imports; } public void setName(String name) { // [TODO] change the name of the underlying file // [TODO] check for duplicates assert !name.startsWith("/") : "Resource name cannot start with /"; // NOI18N super_setName(name); } public void setTimestamp(long timestamp) { super_setTimestamp(timestamp); setPersisted(false); } protected abstract void super_setTimestamp(long timestamp); protected abstract void super_setName(String name); protected void initChildren() { initChildren(false); } private void initChildren(boolean rebuild) { childrenInited = false; ClassInfo classes[] = ((ResourceInfo) getElementInfo()).classes; // create children if (getClassifiers().size() != classes.length) { JMManager.getLog().log("Inconsistent storage. (" + getClassifiers().size() + " != " + classes.length + ")"); JMManager.getLog().log("recovering..."); fixMembers(getPersistentClassifiers(), classes); reinitClassifiers(); } else { Iterator it = getClassifiers().iterator(); for (int i = 0; i < classes.length; i++) { SemiPersistentElement element = (SemiPersistentElement) it.next(); if (!element.infoIdenticalTo(classes[i])) { element.setElementInfo(classes[i]); } } } imports = createChildrenList(imports, "imports", ((ResourceInfo) getElementInfo()).imports, CHANGED_IMPORTS, rebuild); // NOI18N childrenInited = true; if (elementsInited) { initASTElements(); } fireImportsInited(); } private void initASTElements() { elementsInited = false; if (!childrenInited) { initChildren(); } ResourceInfo info = (ResourceInfo) getElementInfo(); packageIdentifier = (MultipartId) initOrCreate(packageIdentifier, info.getTypeAST(this)); elementsInited = true; } private void resetASTElements() { if (elementsInited) { if (packageIdentifier != null) { MultipartId temp = packageIdentifier; packageIdentifier = null; temp.refDelete(); } elementsInited = false; } } public int getStatus() { if (getName().endsWith(".class")) { return IS_FROM_CLASSFILE; } else { MDRParser parser = getParser(); int res = parser.getJavaFeatures(); if (parser.hasSyntaxError()) { res |= HAS_SYNTAX_ERROR; } return res; } } public List getErrors() { if (errors == null) { errors = new ErrorList(); } return errors; } private ResourceInfo getResInfoFromClassFile() { Iterator iter = getPersistentClassifiers().iterator(); FileObject resFile = JavaMetamodel.getManager().getFileObject(this); FileObject folder = resFile.getParent(); ArrayList clsInfo = new ArrayList(); while (iter.hasNext()) { Object elem = iter.next(); if (elem instanceof JavaClassImpl) { JavaClassImpl jc = (JavaClassImpl) elem; ClassInfo inf = ClassFileInfoUtil.createClassInfo (folder, jc.getSimpleName(), -1); clsInfo.add(inf); } // if } // while return new ResourceInfo(null, ResourceInfo.RESOURCE_TYPE, this, (ClassInfo[]) clsInfo.toArray(new ClassInfo[clsInfo.size()]), null); } void initResource(MDRParser parser) { checkUpToDate(false, parser, true); } protected void objectChanged(int mask) { if (disableChanges) return; if (!isChanged()) { ElementInfo info = getElementInfo(); if (info.getASTree() == null && info != getDefaultInfo()) { throw new UnsupportedOperationException("This object is immutable."); // NOI18N } ((ExclusiveMutex) _getMdrStorage().getRepositoryMutex()).registerChange(this); } super.objectChanged(mask); } public void updateFromDataObject(DataObject dob, boolean force) { boolean fail = true; _lock(true); try { checkUpToDate(dob, force, null, false, false); fail = false; } finally { _unlock(fail); } } PositionBounds getFeaturePosition(FeatureImpl feature) { if (!isValid()) return null; MDRParser parser = getParser(); if (parser == null || !parser.isFromDocument()) return null; if (parser.mofidToBounds == null) { // cache position bounds MDRepository repository = repository(); repository.beginTrans(false); try { parser.mofidToBounds = new HashMap(); if (((JMManager) JavaMetamodel.getManager()).getTransactionMutex().getClassPath() == null) { JavaMetamodel.getManager().setClassPath(this); } for (Iterator iter = getClassifiers().iterator(); iter.hasNext(); ) { cachePositions(parser, (MetadataElement)iter.next()); } } catch (InvalidObjectException e) { // ignore } finally { repository.endTrans(false); } } return parser.mofidToBounds == null ? null : (PositionBounds) parser.mofidToBounds.get(feature.refMofId()); } private void cachePositions(MDRParser parser, MetadataElement f) { ASTree tree = f.getASTree(); if (tree != null) parser.mofidToBounds.put(f.refMofId(), parser.createBounds(tree)); if (f instanceof JavaClassImpl) { for (Iterator iter = ((JavaClassImpl)f).getFeatures().iterator(); iter.hasNext(); ) { cachePositions(parser, (MetadataElement)iter.next()); } } } private void resetAST(MDRParser parser) { try { ClassIndex classIndex=ClassIndex.getIndex((JavaModelPackage)refOutermostPackage()); Set ids=new HashSet(60); TokenIterator tokens; int idIndexes[]; int token; int i=0; Iterator idIt; parser.getASTree(); tokens=new TokenIterator(parser); while((token=tokens.getNextTokenType())!=0) { if (token==ParserTokens.IDENTIFIER) { ids.add(tokens.getIdentifierText()); } } idIndexes=new int[ids.size()]; idIt=ids.iterator(); while(idIt.hasNext()) { idIndexes[i++]=idIt.next().hashCode(); } classIndex.setIdentifiers(this,idIndexes); updateMetadata(parser.enterMembers()); } catch (IOException ex) { ErrorManager.getDefault().notify(ex); } } boolean checkUpToDate(boolean initIfOutOfDate, MDRParser parser, boolean initialize) { DataObject dObj = JavaMetamodel.getManager().getDataObject(this); if (dObj==null) return true; return checkUpToDate(dObj, false, parser, initialize, initIfOutOfDate); } private boolean checkUpToDate(DataObject dobj, boolean force, MDRParser parser, boolean initialize, boolean initIfOutOfDate) { // we do not want to update resource when commit is in progress // (it will be updated at the end) if (!isChanged()) { force |= JavaMetamodel.getManager().removeModified(dobj); FileObject file=dobj.getPrimaryFile(); long timestamp=file.lastModified().getTime(); boolean isOutOfDate = force || getTimestamp() != timestamp; boolean shouldParse = initialize || (initIfOutOfDate && !isPersisted()) || (isOutOfDate && (initIfOutOfDate || isInitialized())); if (isOutOfDate || shouldParse) { if (parser != null || getName().endsWith(".java")) { directUpdate(dobj, isOutOfDate, parser, shouldParse, timestamp); } else { directClassFileUpdate(dobj, isOutOfDate, shouldParse); } return false; } } return true; } private void directClassFileUpdate(DataObject dobj, boolean isOutOfDate, boolean shouldParse) { if (isOutOfDate) { boolean changes = disableChanges; disableChanges = true; try { ClassUpdater.updateIndex(this,(MultiDataObject)dobj); } finally { disableChanges = changes; } } if (shouldParse) { ResourceInfo resInfo=getResInfoFromClassFile(); updateMetadata(resInfo); } } private void directUpdate(DataObject dobj, boolean isOutOfDate, MDRParser parser, boolean shouldParse, long timestamp) { if (parser == null) parser = new MDRParser(this,dobj); if (JMManager.INCONSISTENCY_DEBUG) { System.err.println("Direct update of: " + getName()); System.err.println("isOutOfDate: " + isOutOfDate + " shouldParse: " + shouldParse); Thread.dumpStack(); } TokenIterator tokenIterator = null; Reader reader = null; boolean changes = disableChanges; disableChanges = true; try { if (shouldParse) { parser.getASTree(); tokenIterator = new TokenIterator(parser); } else if (isOutOfDate) { reader = parser.getReader(); tokenIterator = new TokenIterator(reader, parser.getSourceLevel()); } if (isOutOfDate) { errors = null; int ids[]; JavaModelPackage pck=(JavaModelPackage)refOutermostPackage(); ClassIndex classIndex=ClassIndex.getIndex(pck); setTimestamp(timestamp); ids=new JavaUpdater(pck, null, null).computeIndex(this, tokenIterator); classIndex.setIdentifiers(this,ids); } } catch (IOException e) { ErrorManager.getDefault().notify(e); } finally { disableChanges = changes; try { if (reader != null) { reader.close(); } } catch (IOException e) { ErrorManager.getDefault().notify(e); } } if (shouldParse) { errors = null; ResourceInfo resInfo=parser.enterMembers(); if (resInfo==null) { // syntax error if (isInitialized()) { JMManager.fireResourceParsed(this); return; } Collection javaClasses=getPersistentClassifiers(); ClassInfo classes[]=new ClassInfo[javaClasses.size()]; int i=0; // the naked collection does not implement clear() method // we need to iterate through it and remove elements one by one for (Iterator it = javaClasses.iterator(); it.hasNext();) { JavaClassImpl jcls=(JavaClassImpl)it.next(); int infoType=jcls.isInterface()?ClassInfo.INTERFACE_TYPE:ClassInfo.CLASS_TYPE; ClassInfo clsInfo=new ClassInfo(null, infoType, jcls.getName(), jcls.getModifiers(), null, null, null, null, null); classes[i++]=clsInfo; } resInfo=new ResourceInfo(null, ResourceInfo.RESOURCE_TYPE, this, classes, null); } updateMetadata(resInfo); JMManager.fireResourceParsed(this); } } private void updateMetadata(ResourceInfo info) { if (infoIdenticalTo(info)) return; updatePersistent(info); setElementInfo(info); } void setFileToDeleteOnRollback(FileObject file){ this.fileToDeleteOnRollback = file; } public void rollbackChanges() { if (fileToDeleteOnRollback != null) { try { fileToDeleteOnRollback.delete(); } catch (IOException ioe) { ErrorManager.getDefault().notify(ioe); } } rollback(); if (invertedDiffs != null) { rollbackDiff(); invertedDiffs = null; DataObject dObj = JavaMetamodel.getManager().getDataObject(this); MDRParser parser = new MDRParser(this,dObj); ResourceInfo resInfo=parser.enterMembers(); uninitialize(); updateMetadata(resInfo); } } protected void uninitialize() { super.uninitialize(); } public void commitChanges() { errors = null; fileToDeleteOnRollback = null; if (!READ_ONLY) { String newSourceText = null; final List diff = new DiffList(); if (isNew()) { newSourceText = getSourceText(); } else { getDiff(diff); } DataObject dobj = JavaMetamodel.getManager().getDataObject(this); final EditorSupport editor = (EditorSupport) dobj.getCookie(EditorSupport.class); StyledDocument doc = null; if (editor != null) { doc = editor.getDocument(); if (doc == null && getParser().isFromDocument()) { try { doc = editor.openDocument(); } catch (IOException ex) { throw (RuntimeException) ErrorManager.getDefault().annotate(new RuntimeException(), ex); } } } if (doc != null) { if (newSourceText == null) { NbDocument.runAtomic(doc, new Runnable() { public void run() { applyDiff(diff, editor); // fail } }); } else { try { PositionRef start = editor.createPositionRef(0, Position.Bias.Forward); PositionRef end = editor.createPositionRef(doc.getLength(), Position.Bias.Backward); new PositionBounds(start, end).setText(newSourceText); } catch (Exception e) { throw (RuntimeException) ErrorManager.getDefault().annotate(new RuntimeException(), e); } } } else { if (newSourceText == null) { applyDiff(diff, dobj); // fail } else { FileObject primaryFile = dobj.getPrimaryFile(); OutputStream stream = null; FileLock lock = null; try { lock = primaryFile.lock(); stream = primaryFile.getOutputStream(lock); stream.write(newSourceText.getBytes()); stream.close(); setTimestamp(primaryFile.lastModified().getTime()); } catch (IOException ex) { throw (RuntimeException) ErrorManager.getDefault().annotate(new RuntimeException(ex.toString()), ex); } finally { if (lock != null) lock.releaseLock(); } } } } } /** Method called by ExclusiveMutex to confirm that the file is still parsable after commit. */ public void parseResource() { if (!READ_ONLY) { DataObject dObj = JavaMetamodel.getManager().getDataObject(this); MDRParser parser = new MDRParser(this, dObj); if (parser.getASTree() == null) { throw new ConstraintViolationException(null, this, "Cannot parse resource " + getName()); // NOI18N } commit(); try { resetAST(parser); } catch (Exception e) { ErrorManager.getDefault().notify(e); } } } public void commitConfirmed() { JavaMetamodel.getUndoManager().addItem(this, invertedDiffs); invertedDiffs = null; rbText = null; rbList = null; JMManager.fireResourceParsed(this); } public void applyDiff(List diff) { applyDiff(diff, false, true); } public void applyDiff(List diff, boolean internalCall, boolean parseAfterApply) { DataObject dobj = JavaMetamodel.getManager().getDataObject(this); final EditorSupport editor = (EditorSupport) dobj.getCookie(EditorSupport.class); if (editor!=null) { StyledDocument doc = editor.getDocument(); if (doc != null) { final List diffList = diff; NbDocument.runAtomic(doc, new Runnable() { public void run() { applyDiff(diffList, editor); } }); } else { // [PENDING] (MM) this is incorrect - if either editor or document are null, // diff should be applied to the file rather than creating a document for it applyDiff(diff, dobj); } } else { applyDiff(diff, dobj); } if (!internalCall) { JavaMetamodel.getUndoManager().addItem(this, invertedDiffs); if (parseAfterApply) initResource(null); } } private void applyDiff(List diff, EditorSupport doc) { List tempDiffs = null; List list = new LinkedList(); try { HashMap positions = new HashMap(diff.size()); int[] offsets = new int[diff.size() * 2]; int i = 0; for (Iterator it = diff.iterator(); it.hasNext();) { DiffElement de = (DiffElement) it.next(); offsets[i++] = de.getStartOffset(); offsets[i++] = de.getEndOffset(); } MDRParser parser = getParser(); if (parser != null && offsets.length > 0) offsets = parser.getDocumentOffsets(offsets); for (i = 0; i < diff.size(); i++) { PositionRef start = doc.createPositionRef(offsets[i * 2], Position.Bias.Backward); PositionRef end = doc.createPositionRef(offsets[i * 2 + 1], Position.Bias.Forward); positions.put(diff.get(i), new PositionBounds(start, end)); } tempDiffs = new ArrayList(); for (Iterator it = diff.iterator(); it.hasNext();) { DiffElement de = (DiffElement) it.next(); PositionBounds pos = (PositionBounds) positions.get(de); int startOffset = pos.getBegin().getOffset(); DiffElement diffElement = new DiffElement(startOffset, startOffset+de.getText().length(), pos.getText()); String text = pos.getText(); pos.setText(de.getText()); list.add (new PosInfo(text, pos)); tempDiffs.add(diffElement); } } catch (Exception e) { throw (ConstraintViolationException) ErrorManager.getDefault().annotate( new ConstraintViolationException(null, this, "Exception thrown when applying diff: " + e.getMessage()), e // NOI18N ); } invertedDiffs = tempDiffs; rbList = list; } private void applyDiff(List diff, DataObject dobj) { MDRParser parser = getParser(); String orig = parser.getSourceText(); int lastPrinted = 0; StringBuffer buf = new StringBuffer(); invertedDiffs = null; List tempDiffs = new ArrayList(); int shift = 0; int shiftedOffset = 0; for (Iterator it = diff.iterator(); it.hasNext();) { DiffElement de = (DiffElement) it.next(); int startOffset = de.getStartOffset(); int endOffset = de.getEndOffset(); buf.append(orig.substring(lastPrinted, startOffset)); buf.append(de.getText()); lastPrinted = endOffset; shiftedOffset = startOffset + shift; tempDiffs.add(new DiffElement(shiftedOffset, shiftedOffset + de.getText().length(), orig.substring(startOffset, endOffset))); shift += startOffset + de.getText().length() - endOffset; } buf.append(orig.substring(lastPrinted)); FileObject primaryFile = dobj.getPrimaryFile(); OutputStream stream = null; FileLock lock = null; try { lock = primaryFile.lock(); stream = primaryFile.getOutputStream(lock); stream.write(buf.toString().getBytes()); stream.close(); invertedDiffs = tempDiffs; rbText = orig; setTimestamp(primaryFile.lastModified().getTime()); } catch (IOException ex) { ex.printStackTrace(); throw (ConstraintViolationException) ErrorManager.getDefault().annotate( new ConstraintViolationException(null, this, "Exception thrown when applying diff: " + ex.getMessage()), ex // NOI18N ); } finally { if (lock != null) lock.releaseLock(); } } private void rollbackDiff() { DataObject dobj = JavaMetamodel.getManager().getDataObject(this); if (rbList != null) { final EditorSupport editor = (EditorSupport) dobj.getCookie(EditorSupport.class); StyledDocument doc = editor.getDocument(); final List list = rbList; rbList = null; NbDocument.runAtomic(doc, new Runnable() { public void run() { for (Iterator iter = list.iterator(); iter.hasNext();) { PosInfo info = (PosInfo) iter.next(); try { info.pos.setText(info.text); } catch (Exception ex) { ErrorManager.getDefault().notify(ex); } } } }); } else { FileObject primaryFile = dobj.getPrimaryFile(); OutputStream stream = null; FileLock lock = null; try { lock = primaryFile.lock(); stream = primaryFile.getOutputStream(lock); stream.write(rbText.getBytes()); stream.close(); setTimestamp(primaryFile.lastModified().getTime()); } catch (IOException ex) { ErrorManager.getDefault().notify(ex); } finally { if (lock != null) lock.releaseLock(); rbText = null; } } } public void getDiff(List diff) { ResourceInfo astInfo = (ResourceInfo) getElementInfo(); ASTProvider parser = getParser(); // package statement ASTree tree = getASTree(); String packageName = getPackageName(); if (isChanged(CHANGED_PACKAGE_NAME) && tree.getSubTrees()[0] == null) { if (packageName != null && packageName.length() > 0) { diff.add(new DiffElement(0, 0, "package " + getPackageName() + ";\n")); // NOI18N } } else if (isChanged(CHANGED_PACKAGE_NAME) && (packageName == null || packageName.length() == 0)) { // remove package statement if the resource is located in default // package int startPads = getStartOffset(parser, tree.getSubTrees()[0], false); // assume there is only package statement with no new line after. // (value will be rewritten if it is not true) int endOff = getEndOffset(parser, tree.getSubTrees()[0]); ASTree[] rscChildren = tree.getSubTrees(); Token newLine = null; // find the first new line after package statement, if there exists if (rscChildren[1] != null) { // there is at least one import newLine = getFirstEOL(parser, rscChildren[1].getFirstToken()); } else if (rscChildren[2] != null) { // there is no import, but at least one classifier newLine = getFirstEOL(parser, rscChildren[2].getFirstToken()); } if (newLine != null) { // we knows about first new line after the package statement, // We will update the end offset character. endOff = newLine.getEndOffset(); } diff.add(new DiffElement(startPads, endOff, "")); } else if (tree.getSubTrees()[0] != null) { getChildDiff(diff, parser, tree.getSubTrees()[0].getSubTrees()[0], (MetadataElement) getPackageIdentifier(), CHANGED_PACKAGE_NAME); } // import statements int endOffset; ASTree typeDecls = tree.getSubTrees()[2]; if (typeDecls == null) { endOffset = parser.getSourceText().length(); } else { if (isChanged(CHANGED_IMPORTS)) { // find the end of import section. if (tree.getSubTrees()[0] == null && tree.getSubTrees()[1] == null) { // there is not any package or import statement, the // compilation unit starts with 'class Anything...'. // start at the beginning of file. endOffset = 0; } else if (tree.getSubTrees()[1] == null) { // there is package statement but no import endOffset = getStartOffset(parser, typeDecls, true)-1; diff.add(new DiffElement(endOffset, endOffset, "\n\n")); // NOI18N } else { // there are imports already. endOffset = getStartOffset(parser, typeDecls, true); if (parser.getSourceText().charAt(endOffset-1) == '\n') { endOffset--; } } } else { endOffset = getStartOffset(parser, typeDecls, true); } } getCollectionDiff(diff, parser, CHANGED_IMPORTS, astInfo.imports, getImports(), endOffset, "\n", false); // NOI18N // there was neither package nor import statement in source, put // new line after new imports when imports where changed. if (isChanged(CHANGED_IMPORTS) && tree.getSubTrees()[0] == null && tree.getSubTrees()[1] == null) { diff.add(new DiffElement(0, 0, "\n")); // NOI18N } // classes endOffset = parser.getSourceText().length(); getCollectionDiff(diff, parser, CHANGED_CLASSIFIERS, astInfo.classes, getClassifiers(), endOffset, "\n\n"); // NOI18N } protected int getBeginIndex() { return 0; } protected int getEndIndex() { return getParser().getSourceText().length(); } public String getSourceText() { String origElem; if ((origElem = checkChange()) != null) { return origElem; } StringBuffer buf = new StringBuffer(); String packageName = getPackageName(); if (packageName != null && packageName.length() > 0) { buf.append("package "); // NOI18N buf.append(getPackageName()); buf.append(";\n\n"); // NOI18N } for (Iterator importsIt = getImports().iterator(); importsIt.hasNext(); ) { ImportImpl imp = (ImportImpl) importsIt.next(); buf.append(imp.getSourceText() + "\n"); // NOI18N } for (Iterator clazzIt = getClassifiers().iterator(); clazzIt.hasNext(); ) { JavaClassImpl javaClass = (JavaClassImpl) clazzIt.next(); buf.append('\n' + javaClass.getSourceText() + '\n'); } return buf.toString(); } /** * Overrides default implementation from SemiPersistentElement. * It guarantees interruption of the getIndentation() calls' chain for * newly created elements. * See example below. * * Example: * Consider situation, when you have created new Resource 'Foo.java'. * You have created JavaClass 'public class Foo' and put it to the * Resource. In addition to, you have created Field 'int count;'. Now * when generator starts, it goes through the elements and generates them. * When getSourceText() method in Field is called, it asks for the * indentation. Default implementation in SemiPersistentElement.getIndentation() * asks for indentation of its parent, which is JavaClass. It calls the * same default implementation, which asks for the parents (in this case Resource) * implementation. It is empty string, so it stops the getIndentation() * calls' chain. * * @return empty string, see comment above. */ protected String getIndentation() { return ""; } // .......................................................................... protected void _delete() { // --- delete from package ----------------------------------------- JavaPackage parent = (JavaPackage) refImmediateComposite(); if (parent != null) { // [TODO] simplify this after getResources on proxy package is fixed JavaPackage realPackage = ((JavaPackageClassImpl) ((JavaModelPackage) refImmediatePackage()).getJavaPackage()).resolveRealPackage(parent.getName()); realPackage.getResources().remove(this); } // --- delete components ------------------------------------------- // delete all classifiers for (Iterator it = getPersistentClassifiers().iterator(); it.hasNext();) { RefObject classifier = (RefObject) it.next(); it.remove(); classifier.refDelete(); } // --- delete from indexes ---------------------------------------- ClassIndex.getIndex((JavaModelPackage) refOutermostPackage()).removeResource(this); ((ExclusiveMutex) _getMdrStorage().getRepositoryMutex()).unregisterChange(this); super._delete(); } public void replaceChild(Element oldElement, Element newElement) { if (replaceObject(classifiersInited() ? getClassifiers() : getPersistentClassifiers(), oldElement, newElement)) return; if (childrenInited) { if (replaceObject(getImports(), oldElement, newElement)) return; } if (elementsInited && oldElement.equals(packageIdentifier)) { if (newElement != null) { // [PENDING] null value causes that a composite (package) is set setPackageIdentifier((MultipartId) newElement); } // [PENDING] return; } super.replaceChild(oldElement, newElement); } // .......................................................................... // a hack allowing the bridge to be informed about imports initialization // .......................................................................... // [TODO] synchronization private PropertyChangeSupport importsSupp; public boolean importsInited () { return childrenInited; } public void addImportsListener (PropertyChangeListener listener) { if (childrenInited) { listener.propertyChange (null); } else { synchronized (this) { if (importsSupp == null) { importsSupp = new PropertyChangeSupport (this); } } importsSupp.addPropertyChangeListener(listener); } } private void fireImportsInited () { if (importsSupp != null) importsSupp.firePropertyChange (null, null, null); importsSupp = null; } // end of the hack .......................................................... protected ASTree getPartTree(ElementPartKind part) { if (ElementPartKindEnum.HEADER.equals(part)) return getASTree().getSubTrees()[0]; throw new IllegalArgumentException("Invalid part for this element: " + part); // NOI18N } public MultipartId getPackageIdentifier() { if (!elementsInited) { initASTElements(); } return packageIdentifier; } public void setPackageIdentifier(MultipartId packageIdentifier) { objectChanged(CHANGED_PACKAGE_NAME); changeChild(getPackageIdentifier(), packageIdentifier); this.packageIdentifier = packageIdentifier; // doSetPackageIdentifier(packageIdentifier); setPkgNameAndUpdateIdx(packageIdentifier != null ? ((MetadataElement) packageIdentifier).getSourceText() : ""); } // private void doSetPackageIdentifier(MultipartId packageIdentifier) { // objectChanged(CHANGED_PACKAGE_NAME); // changeChild(getPackageIdentifier(), packageIdentifier); // this.packageIdentifier = packageIdentifier; // } void childChanged(MetadataElement element) { if (element == getPackageIdentifier()) { setPkgNameAndUpdateIdx(((MetadataElement) packageIdentifier).getSourceText()); } } public boolean containsIdentifier(String identifier) { return ClassIndex.containsIdentifier(this, identifier.hashCode()); } /** * Tries to find first EOL in paddings of provided token. If there are not * any padding connected to token or there is not any EOL token in its * paddings, returns null. Otherwise returns EOL token. * * @param parser parser which is used for obtaining token by token id * @param token token id * @return token representing first EOL token of parameters paddings * or null if EOL token is not present */ private static Token getFirstEOL(ASTProvider parser, int token) { Token[] pads = parser.getToken(token).getPadding(); for (int i = 0; i < pads.length; i++) { if (pads[i].getType() == ParserTokens.EOL) { return pads[i]; } } return null; } // .......................................................................... private static class PosInfo { String text; PositionBounds pos; PosInfo(String text, PositionBounds pos) { this.text = text; this.pos = pos; } } private class ErrorList extends AbstractList implements ErrConsumer { private List errors = null; private String name; public ErrorList() { name = getName(); } public Object get(int index) { initCheck(); return errors.get(index); } private void initCheck() { if (errors == null) { MDRParser provider = getParser(); if (provider != null) { ECRequestDesc desc = new ECRequestDescImpl(provider, this); Factory.getDefault().getErrorChecker(desc).parse(); } if (errors == null) { errors = Collections.EMPTY_LIST; } } } public int size() { initCheck(); return errors.size(); } public void pushError(Object severity, String errorFileName, int line, int column, String message) { if (errorFileName.equals(name)) { if (errors == null) { errors = new ArrayList(); } errors.add(new ErrorInfoImpl(severity, line, column, message)); } } } private static class ErrorInfoImpl implements ErrorInfo { private final String description; private final int line, column; private final ErrorType severity; private static List fieldNames = Arrays.asList((Object[]) new String[] {"description", "lineNumber", "column", "severity"}); // NOI18N private static List typeName = Arrays.asList((Object[]) new String[] {"JavaModel", "ErrorInfo"}); // NOI18N ErrorInfoImpl(Object severity, int line, int column, String message) { this.description = message; this.line = line; this.column = column; this.severity = severity == ErrConsumer.WARNING ? ErrorTypeEnum.WARNING : ErrorTypeEnum.ERROR; } public String getDescription() { return description; } public int getLineNumber() { return line; } public int getColumn() { return column; } public ErrorType getSeverity() { return severity; } public List refFieldNames() { return fieldNames; } public Object refGetValue(String s) { if ("description".equals(s)) { // NOI18N return getDescription(); } else if ("lineNumber".equals(s)) { // NOI18N return new Integer(getLineNumber()); } else if ("column".equals(s)) { // NOI18N return new Integer(getColumn()); } else if ("severity".equals(s)) { // NOI18N return getSeverity(); } else { throw new InvalidNameException(s); } } public List refTypeName() { return typeName; } } private static class DiffList extends AbstractList { private final ArrayList delegate = new ArrayList(); public int size() { return delegate.size(); } public Object get(int index) { return delegate.get(index); } public void add(int index, Object object) { DiffElement element = (DiffElement) object; if (index == delegate.size()) { add(object); } DiffElement diff = (DiffElement) delegate.get(index); if (element.getEndOffset() > diff.getStartOffset()) { throw new IllegalArgumentException("Start offset of the diff element is lower than end offset of the previous diff element. (previous: " + diff + ", this: " + element + ")"); // NOI18N } else if (element.getEndOffset() == diff.getStartOffset()) { delegate.remove(index); element = new DiffElement(element.getStartOffset(), diff.getEndOffset(), element.getText() + diff.getText()); } if (index > 0) { diff = (DiffElement) delegate.get(index - 1); if (diff.getEndOffset() > element.getStartOffset()) { throw new IllegalArgumentException("Start offset of the diff element is lower than end offset of the previous diff element. (previous: " + diff + ", this: " + element + ")"); // NOI18N } else if (diff.getEndOffset() == element.getStartOffset()) { delegate.set(index - 1, new DiffElement(diff.getStartOffset(), element.getEndOffset(), diff.getText() + element.getText())); return; } } delegate.add(index, element); } public Object remove(int index) { return delegate.remove(index); } public Object set(int index, Object object) { Object result = remove(index); add(index, object); return result; } public boolean add(Object object) { DiffElement element = (DiffElement) object; if (!delegate.isEmpty()) { int lastIndex = delegate.size() - 1; DiffElement diff = (DiffElement) delegate.get(lastIndex); if (diff.getEndOffset() > element.getStartOffset()) { throw new IllegalArgumentException("Start offset of the diff element is lower than end offset of the previous diff element. (previous: " + diff + ", this: " + element); // NOI18N } else if (diff.getEndOffset() == element.getStartOffset()) { delegate.set(lastIndex, new DiffElement(diff.getStartOffset(), element.getEndOffset(), diff.getText() + element.getText())); return true; } } return delegate.add(element); } } protected void hardRefParent(boolean enabled) { } protected void parentChanged() { } } |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.