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.openide.src.nodes;


import java.awt.Component;
import java.awt.datatransfer.Transferable;
import java.beans.*;
import java.io.IOException;
import java.lang.reflect.Modifier; 
import java.util.*;
import org.netbeans.api.java.classpath.ClassPath;

import org.openide.*;
import org.openide.src.*;
import org.openide.nodes.*;
import org.openide.cookies.SourceCookie;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import org.openide.util.*;
import org.openide.util.datatransfer.NewType;
import org.openide.util.datatransfer.PasteType;
import org.openide.util.datatransfer.ExTransferable;

/** This class defines utilities for editing source using hierarchy API,
* e.g. creation new types for class elements, runAtomicAsUser support, ...
*
* @author Petr Hamernik
*/
class SourceEditSupport {

    static final ResourceBundle bundle = NbBundle.getBundle(SourceEditSupport.class);

    static final String[] MENU_NAMES = {
        bundle.getString("MENU_CREATE_BLOCK"), bundle.getString("MENU_CREATE_VARIABLE"),
        bundle.getString("MENU_CREATE_CONSTRUCTOR"), bundle.getString("MENU_CREATE_METHOD"),
        bundle.getString("MENU_CREATE_CLASS"), bundle.getString("MENU_CREATE_INTERFACE")
    };

    /* Get the new types that can be created in this node.
    * For example, a node representing a Java package will permit classes to be added.
    * @return array of new type operations that are allowed
    */
    public static NewType[] createNewTypes(ClassElement element) {
        if (element.isClass()) {
            // class new types
            return new NewType[] {
                       new ElementNewType(element, (byte) 0),
                       new ElementNewType(element, (byte) 1),
                       new ElementNewType(element, (byte) 2),
                       new ElementNewType(element, (byte) 3),
                       new ElementNewType(element, (byte) 4),
                       new ElementNewType(element, (byte) 5)
                   };
        }
        else {
            // interface new types
            return new NewType[] {
                       new ElementNewType(element, (byte) 1),
                       new ElementNewType(element, (byte) 3),
                       new ElementNewType(element, (byte) 4),
                       new ElementNewType(element, (byte) 5)
                   };
        }
    }

    /** New types for class element */
    static class ElementNewType extends NewType {
        /** Class element where to create new element */
        ClassElement element;

        /** Filled in if the target class is an interface to fool customizers and
         * have the restrictions on them.
         */
        ClassElement proxy;
        
        /** The kind of element to create */
        byte kind;
        
        /** Creates new type
        * @param element Where to create new element.
        * @param kind The kind of the element to create
        */
        public ElementNewType(ClassElement element, byte kind) {
            this.element = element;
            proxy = new ClassElement();
            try {
                proxy.setName(Identifier.create("Default"));  // NOI18N
                proxy.setClassOrInterface(element.isClassOrInterface());
            } catch (SourceException ex) {
                    // it can NOT happen
            }
            this.kind = kind;
        }

        /** Get the name of the new type.
        * @return localized name.
        */
        public String getName() {
            return MENU_NAMES[kind];
        }

        /** Help context */
        public org.openide.util.HelpCtx getHelpCtx() {
            return new org.openide.util.HelpCtx (SourceEditSupport.class.getName () + ".newElement" + kind); // NOI18N
        }

        /** Creates new element */
        public void create () throws IOException {
            final Identifier outerName = element.getName();
            final boolean outerIsClass = element.isClass();

            Element newElement = null;

            try {
                switch (kind) {
                case 0:
                    {
                        // Adding initializer
                        InitializerElement e = new InitializerElement();
                        e.setStatic(true);
                        e.setBody("\n"); // NOI18N
                        newElement = e;
                        break;
                    }
                case 1:
                    {
                        // Adding field
                        FieldElement e = new FieldElement();
                        e.setType(Type.INT);
                        e.setName(Identifier.create("newField")); // NOI18N
                        e.setModifiers(Modifier.PRIVATE + (outerIsClass ? 0 : Modifier.STATIC));
                        proxy.addField(e);
                        e = proxy.getFields()[0];
                        FieldCustomizer cust = new FieldCustomizer(e);
                        if (openCustomizer(cust, "TIT_NewField") && cust.isOK()) // NOI18N
                            newElement = e;
                        break;
                    }
                case 2:
                    {
                        // Adding constructor
                        ConstructorElement e = new ConstructorElement();
                        e.setName(Identifier.create(((ClassElement)element).getName().getName()));
                        e.setModifiers(Modifier.PUBLIC);
                        e.setBody("\n"); // NOI18N
                        MethodCustomizer cust = new MethodCustomizer(e);
                        if (openCustomizer(cust, "TIT_NewConstructor") && cust.isOK()) // NOI18N
                            newElement = e;
                        break;
                    }
                case 3:
                    {
                        // Adding method
                        MethodElement e = new MethodElement();
                        e.setReturn(Type.VOID);
                        e.setName(Identifier.create("newMethod")); // NOI18N
                        e.setModifiers(Modifier.PUBLIC);
                        e.setBody(outerIsClass ? "\n" : null); // NOI18N
                        proxy.addMethod(e);
                        e = proxy.getMethods()[0];
                        MethodCustomizer cust = new MethodCustomizer(e);
                        if (openCustomizer(cust, "TIT_NewMethod") && cust.isOK()) {// NOI18N
			    if ((e.getModifiers() & (Modifier.ABSTRACT | Modifier.NATIVE)) > 0) {
				// abstract and native methods cannot have bodies.
				e.setBody(null);
			    }
                            newElement = e;
			}
                        break;
                    }
                case 4:
                    {
                        // Adding inner class
                        ClassElement e = new ClassElement();
                        e.setName(Identifier.create(outerName.getFullName() + ".InnerClass", "InnerClass")); // NOI18N
                        e.setModifiers(Modifier.PUBLIC);
                        e.setClassOrInterface(true);
                        proxy.addClass(e);
                        e = proxy.getClasses()[0];
                        ClassCustomizer cust = new ClassCustomizer(e);
                        if (openCustomizer(cust, "TIT_NewClass") && cust.isOK()) // NOI18N
                            newElement = e;
                        break;
                    }
                case 5:
                    {
                        // Adding inner interface
                        ClassElement e = new ClassElement();
                        e.setName(Identifier.create(outerName.getFullName() + ".InnerInterface", "InnerInterface")); // NOI18N
                        e.setModifiers(Modifier.PUBLIC);
                        e.setClassOrInterface(false);
                        proxy.addClass(e);
                        e = proxy.getClasses()[0];
                        ClassCustomizer cust = new ClassCustomizer(e);
                        if (openCustomizer(cust, "TIT_NewInterface") && cust.isOK()) // NOI18N
                            newElement = e;
                        break;
                    }
                }
            }
            catch (SourceException exc) {
                // shouldn't happen - memory implementation
                // is not based on java source.
            }

            if (newElement == null)
                return;

            final Element addingElement = newElement;
            SourceEditSupport.invokeAtomicAsUser(element, new SourceEditSupport.ExceptionalRunnable() {
                                                     public void run() throws SourceException {
                                                         switch (kind) {
                                                         case 0:
                                                             ((ClassElement)element).addInitializer((InitializerElement)addingElement);
                                                             return;
                                                         case 1:
                                                             ((ClassElement)element).addField((FieldElement)addingElement);
                                                             return;
                                                         case 2:
                                                             ((ClassElement)element).addConstructor((ConstructorElement)addingElement);
                                                             return;
                                                         case 3:
                                                             ((ClassElement)element).addMethod((MethodElement)addingElement);
                                                             return;
                                                         case 4:
                                                         case 5:
                                                             element.addClass((ClassElement)addingElement);
                                                             return;
                                                         }
                                                     }
                                                 });
        }
    }

    /** Show dialog and allow user to modify new element.
    * @param customizer The component to be displayed
    * @param titleKey the key to resource bundle for the title of dialog
    * @return true if user pressed OK button,
    *     otherwise false (for CANCEL)
    */
    static boolean openCustomizer(Component customizer, String titleKey) {
        NotifyDescriptor desriptor = new NotifyDescriptor(
                                         customizer,
                                         ElementNode.bundle.getString(titleKey),
                                         NotifyDescriptor.OK_CANCEL_OPTION,
                                         NotifyDescriptor.PLAIN_MESSAGE,
                                         null, null);

        Object ret = DialogDisplayer.getDefault().notify(desriptor);
        return (ret == NotifyDescriptor.OK_OPTION);
    }

    /** Invokes the runnable using SourceElement.runAtomicAsUser, if it can find
    * a source element for the given hierarchy element. If the SourceElement can't
    * be found, the runnable is executed without any protection.
    * @exception IOException If SourceException occured inside the runnable.
    */
    static void invokeAtomicAsUser(Element element, final ExceptionalRunnable exRun) throws IOException {
        try {
            runAsUser(element, exRun);
        } catch (SourceException.IO e) {
            ErrorManager.getDefault().annotate(e.getReason(),
                ErrorManager.USER, null, null, null, null);
            throw e.getReason();
                       }
                       catch (SourceException e) {
            if (Boolean.getBoolean("netbeans.debug.exceptions")) // NOI18N
                e.printStackTrace();
            IOException x = new IOException(e.getMessage());
            // #9512 -- this exception is expected, so lower its priority to "user".
            ErrorManager.getDefault().annotate(x, 
                ErrorManager.USER, null, null, e, null);
            throw x;
        }
    }
    
    static void runAsUser(Element ref, final ExceptionalRunnable exRun) throws SourceException {
        final SourceException ex[] = { null };
        SourceElement src = findSource(ref);
        boolean retry = false;
        do {
            ex[0] = null;
        if (src == null) {
            exRun.run();
        } else {
            src.runAtomicAsUser(new Runnable() {
                public void run() {
                    try {
                        exRun.run();
                    } catch (SourceException e) {
                        ex[0] = e;
                    }
                }
            });
        }
            if (ex[0] != null) {
                if (retry)
                    break;
                retry = true;
                if (ex[0] instanceof SourceException.IO) {
                    IOException iex = ((SourceException.IO)ex[0]).getReason();
                    if (iex instanceof UserQuestionException) {
                        UserQuestionException uex = (UserQuestionException)iex;
                        NotifyDescriptor.Confirmation nc = new NotifyDescriptor.Confirmation(uex.getLocalizedMessage(),
                                bundle.getString("TIT_CannotWriteFile"), NotifyDescriptor.Confirmation.YES_NO_OPTION);
                        Object o = DialogDisplayer.getDefault().notify(nc);
                        if (o == NotifyDescriptor.YES_OPTION) {
                            try {
                                uex.confirmed();
                                // HACK!! See issue #23407
                                continue;
                            } catch (IOException x) {
                                ex[0] = new SourceException.IO(x);
                                ErrorManager.getDefault().annotate(ex[0], ErrorManager.USER, null, null, x, null);
                            }
                        } else {
                            iex = new IOException("Cannot write");
                            ErrorManager.getDefault().annotate(iex, ErrorManager.USER, null, bundle.getString("ERR_CannotWriteFile"), uex, null);
                            ex[0] = new SourceException.IO(iex);
                            ErrorManager.getDefault().annotate(ex[0], ErrorManager.USER, null, bundle.getString("ERR_CannotWriteFile"), iex, null);
                        }
                    }
                }
                // not a UserQuestionException, unconfirmed UQE, exception from UQE.confirmed.
                break;
            }
        } while (ex[0] != null);

        if (ex[0] != null) 
            throw ex[0];
    }

    /** This interface is used like runnable, but its method run
    * could throw BadLocationException.
    * @exception SourceException
    */
    static interface ExceptionalRunnable {
        public void run() throws SourceException;
    }

    static boolean isWriteable(Element element) {
        SourceElement el = findSource(element);
	DataObject d = el == null ? null : (DataObject)el.getCookie(DataObject.class);
	if (d == null) {
	    return true;
	}
	return !d.getPrimaryFile().isReadOnly();
    }

    /** Find the source for the specifier element. 
    * @return instance of SourceElement or null, if the source can't be found.
    */
    static SourceElement findSource(Element element) {
        SourceElement source = null;
        ClassElement clazz = null;
        if (element instanceof ClassElement) {
            clazz = (ClassElement) element;
        }
        else if (element instanceof MemberElement) {
            clazz = ((MemberElement) element).getDeclaringClass();
        }
        else if (element instanceof InitializerElement) {
            clazz = ((InitializerElement) element).getDeclaringClass();
        }
        else if (element instanceof SourceElement) {
            return (SourceElement) element;
        }
        if (clazz != null) {
            source = clazz.getSource();
        }
        return source;
    }
    
    static void createJavaFile(ClassElement clazz, FileObject target) throws SourceException, IOException {
        DataObject targetObject;
        String name = clazz.getName().getSourceName();
        FileObject newFile;
        ClassPath cp = ClassPath.getClassPath(target, ClassPath.SOURCE);
        if (cp == null) {
            throw new IOException("No known package source root for " + target); // XXX I18N
        }
	String packageName = cp.getResourceName(target, '.', true);
        String newName;
        
	if ("".equals(packageName)) // NOI18N
	    packageName = null;
        newName = org.openide.filesystems.FileUtil.findFreeFileName(target, name, "java"); // NOI18N
        newFile = target.createData(name, "java"); // NOI18N
        SourceElement newSrc;
        SourceCookie cookie;
        try {
            targetObject = DataObject.find(newFile);
        } catch (org.openide.loaders.DataObjectNotFoundException e) {
            throw (IOException)ErrorManager.getDefault().annotate(
                new IOException(e.getMessage()), 
                ErrorManager.EXCEPTION, "Data object can't be created", // NOI18N
                bundle.getString("EXC_CREATE_SOURCE_FILE"),
                e, null
            );
        }
        cookie = (SourceCookie)targetObject.getCookie(SourceCookie.class);
        if (cookie == null) {
            // perhaps java sources not installed ?
            throw (SourceException)ErrorManager.getDefault().annotate(
                new SourceException("Source element cannot be found"), // NOI18N
                bundle.getString("EXC_CREATE_SOURCE_FILE")
            );
        }
        if (packageName != null) // NOI18N
	    cookie.getSource().setPackage(Identifier.create(packageName));
        cookie.getSource().addClass(clazz);
	
	ClassElement targetC = cookie.getSource().getClass(Identifier.create(clazz.getName().getSourceName()));
	int mods = targetC.getModifiers() & ~Modifier.STATIC;
	if ((mods & (Modifier.PROTECTED | Modifier.PRIVATE)) > 0) {
	    mods = (mods & ~(Modifier.PROTECTED | Modifier.PRIVATE)) | Modifier.PUBLIC;
	}
	targetC.setModifiers(mods);
    }
    
    static void removeClass(ClassElement clazz) throws SourceException {
        if (clazz.getDeclaringClass() != null) {
            clazz.getDeclaringClass().removeClass(clazz);
        } else {
            SourceElement src = SourceEditSupport.findSource(clazz);
            if (src == null) {
                throw (SourceException)ErrorManager.getDefault().annotate(
                    new SourceException("Element has no source"), // NOI18N
                    bundle.getString("EXC_NO_SOURCE")
                );
            }
            src.removeClass(clazz);
        }
    }

    /* default */static class PackagePaste implements NodeTransfer.Paste {
        private static PasteType[] EMPTY_TYPES = new PasteType[0];                         
        /** True, if the paste should remove the original class element.
        */
        private boolean deleteSelf;

        /** Class element to paste.
        */        
        private ClassElement element;
        
        PackagePaste(ClassElement cls, boolean deleteSelf) {
            this.deleteSelf = deleteSelf;
            this.element = cls;
        }
        
        public PasteType[] types(Node target) {
            DataObject obj = (DataObject)target.getCookie(DataObject.class);
            if (element == null || obj == null) 
                return EMPTY_TYPES;
            FileObject fob = obj.getPrimaryFile();
            if (!fob.isFolder()) {
                return EMPTY_TYPES;
            }
            return new PasteType[] {
                new Type(fob)
            };
        }

        private class Type extends PasteType {
            /** Target file folder
            */
            private FileObject target;

            Type(FileObject target) {
                this.target = target;
            }

            public String getName() {
                return bundle.getString("MENU_PASTE_AS_FILE");
            }

            public org.openide.util.HelpCtx getHelpCtx() {
                return super.getHelpCtx();
            }

            public Transferable paste() throws IOException {
                final ClassElement clazz = PackagePaste.this.element;
                final boolean del = PackagePaste.this.deleteSelf;

		try {                
            	    createJavaFile(clazz, target);
		} catch (SourceException ex) {
	            IOException x = new IOException(ex.getMessage());
    		    ErrorManager.getDefault().annotate(x, ex);
		    throw x;
		}
                if (del) {
                    final SourceException ex[] = { null };
                    SourceEditSupport.invokeAtomicAsUser(clazz, new SourceEditSupport.ExceptionalRunnable() {
                        public void run() throws SourceException {
                            try {
                                    removeClass(clazz);
                            } catch (SourceException e) {
                                ex[0] = e;
                            } 
                        }
                    });
                    if (ex[0] != null) {
	        	IOException x = new IOException(ex[0].getMessage());
    			ErrorManager.getDefault().annotate(x, ex[0]);
                        throw x;
                    }
                    PackagePaste.this.element = null;
                    return ExTransferable.EMPTY;
                } else {
                    return null;
                }
            }
        }
    }
    
    static class ClassMultiPasteType extends PasteType {
        ClassElementNode target;
        Collection members;
        boolean delete;

        ClassMultiPasteType(ClassElementNode target, Collection members, boolean delete) {
            this.target = target;
            this.members = members;
            this.delete = delete;
        }

        public Transferable paste() throws IOException {
            for (Iterator it = members.iterator(); it.hasNext(); ) {
                target.pasteElement((Element)it.next(), delete);
            }
            if (delete) 
                return ExTransferable.EMPTY;
            else
                return null;
        }
    }
}
... 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.