alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

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

 

new blog posts

 

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.