CppSwitchAction.java :  » IDE-Netbeans » cnd » org » netbeans » modules » cnd » navigation » switchfiles » Java Open Source

Java Open Source » IDE Netbeans » cnd 
cnd » org » netbeans » modules » cnd » navigation » switchfiles » CppSwitchAction.java
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common
 * Development and Distribution License("CDDL") (collectively, the
 * "License"). You may not use this file except in compliance with the
 * License. You can obtain a copy of the License at
 * http://www.netbeans.org/cddl-gplv2.html
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 * specific language governing permissions and limitations under the
 * License.  When distributing the software, include this License Header
 * Notice in each file and include the License file at
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the GPL Version 2 section of the License file that
 * accompanied this code. If applicable, add the following below the
 * License Header, with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Contributor(s):
 *
 * The Original Software is NetBeans. The Initial Developer of the Original
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
 * Microsystems, Inc. All Rights Reserved.
 *
 * If you wish your version of this file to be governed by only the CDDL
 * or only the GPL Version 2, indicate your decision by adding
 * "[Contributor] elects to include this software in this distribution
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 * single choice of license, a recipient has the option to distribute
 * your version of this file under either the CDDL, the GPL Version 2 or
 * to extend the choice of license to its licensees as provided above.
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 * Version 2 license, then the option applies only if the new code is
 * made subject to such option by the copyright holder.
 */

package org.netbeans.modules.cnd.navigation.switchfiles;

import java.awt.event.ActionEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import javax.swing.text.JTextComponent;
import org.netbeans.api.editor.EditorRegistry;
import org.netbeans.editor.BaseAction;
import org.netbeans.editor.JumpList;
import org.netbeans.modules.cnd.api.model.CsmFile;
import org.netbeans.modules.cnd.api.model.CsmInclude;
import org.netbeans.modules.cnd.api.model.xref.CsmIncludeHierarchyResolver;
import org.netbeans.modules.cnd.api.utils.IpeUtils;
import org.netbeans.modules.cnd.loaders.CCDataLoader;
import org.netbeans.modules.cnd.loaders.CCDataObject;
import org.netbeans.modules.cnd.loaders.CDataLoader;
import org.netbeans.modules.cnd.loaders.CDataObject;
import org.netbeans.modules.cnd.loaders.HDataLoader;
import org.netbeans.modules.cnd.loaders.HDataObject;
import org.netbeans.modules.cnd.modelutil.CsmUtilities;
import org.openide.awt.StatusDisplayer;
import org.openide.cookies.OpenCookie;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.loaders.ExtensionList;
import org.openide.nodes.Node;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.windows.TopComponent;

public final class CppSwitchAction extends BaseAction {

    private static final String actionName = "cpp-switch-header-source"; // NOI18N
    private static final String ICON = "org/netbeans/modules/cnd/navigation/resources/header_source_icon.png"; // NOI18N
    private static CppSwitchAction instance;

    public static synchronized CppSwitchAction getInstance() {
        if (instance == null) {
            instance = new CppSwitchAction();
        }
        return instance;
    }

    public CppSwitchAction() {
        super(actionName);
        putValue("noIconInMenu", Boolean.TRUE); //NOI18N
        putValue(BaseAction.ICON_RESOURCE_PROPERTY, ICON);
        putValue(SHORT_DESCRIPTION, getDefaultShortDescription());
    }

    public void actionPerformed(ActionEvent evt, JTextComponent txt) {
        final Node[] activatedNodes = TopComponent.getRegistry().getActivatedNodes();

        FileObject res = findToggleFile(activatedNodes);
        boolean isToggled = false;
        if (res != null) {
            doToggle(res);
            isToggled = true;
        } else {
            CsmFile target = getTarget(activatedNodes);
            if (target != null) {
                DataObject dob = CsmUtilities.getDataObject(target);
                if (dob != null) {
                    doToggle(dob);
                    isToggled = true;
                }
            }
        }
        if (!isToggled) {
            String status;
            switch (getTargetNodeKind(TopComponent.getRegistry().getActivatedNodes())) {
                case HEADER:
                    status = getMessage("cpp-switch-source-not-found"); //NOI18N
                    break;
                case SOURCE:
                    status = getMessage("cpp-switch-header-not-found"); //NOI18N
                    break;
                default:
                    status = getMessage("cpp-switch-file-not-found");
            }
            StatusDisplayer.getDefault().setStatusText(status); // NOI18N
        }
    }

    public @Override String getPopupMenuText(JTextComponent target) {
        String trimmedNameKey = "goto-cpp-switch-file"; //NOI18N
        switch (getTargetNodeKind(TopComponent.getRegistry().getActivatedNodes())) {
            case HEADER:
                trimmedNameKey = "goto-cpp-header-file"; //NOI18N
                break;
            case SOURCE:
                trimmedNameKey = "goto-cpp-source-file"; //NOI18N
                break;
        }
        return getMessage(trimmedNameKey);
    }

    protected @Override Object getDefaultShortDescription() {
        return getMessage("cpp-switch-header-source"); //NOI18N
    }

    // File search functionality
    
    private enum NodeKind {

        HEADER, SOURCE, UNKNOWN
    }

    private static NodeKind getTargetNodeKind(Node[] activatedNodes) {
        if (activatedNodes != null && activatedNodes.length == 1) {
            if (activatedNodes[0].getLookup().lookup(HDataObject.class) != null) {
                return NodeKind.SOURCE;
            } else if (activatedNodes[0].getLookup().lookup(CCDataObject.class) != null || activatedNodes[0].getLookup().lookup(CDataObject.class) != null) {
                return NodeKind.HEADER;
            }
        }
        return NodeKind.UNKNOWN;
    }

    private static CsmFile getTarget(Node[] activatedNodes) {
        CsmFile f = CsmUtilities.getCsmFile(activatedNodes[0], false);
        CsmFile target = null;
        if (f != null) {
            if (f.isHeaderFile()) {
                target = findSource(f);
            } else if (f.isSourceFile()) {
                target = findHeader(f);
            }
        }
        return target;
    }

    /*package*/ static CsmFile findHeader(CsmFile source) {
        String name = getName(source.getAbsolutePath().toString());
        // first look at the list of includes
        for (CsmInclude h : source.getIncludes()) {
            if (h.getIncludeFile() != null 
                    && IpeUtils.areFilenamesEqual( name, getName(h.getIncludeFile().getAbsolutePath().toString())) ) {
                return h.getIncludeFile();
            }
        }

        String path = trimExtension(source.getAbsolutePath().toString());
        CsmFile namesake = null;
        for (CsmFile f : source.getProject().getHeaderFiles()) {
            if (IpeUtils.areFilenamesEqual( getName(f.getAbsolutePath().toString()), name)) {
                if (IpeUtils.areFilenamesEqual( path, trimExtension(f.getAbsolutePath().toString()) )) {
                    // we got namesake in the same directory. Best hit.
                    // TODO: actually this is pretty common issue, should we
                    // make a special check for such files?
                    return f;
                }
                if (namesake == null) {
                    // we don't care which namesake to take if it's not in
                    // the same directory
                    namesake = f;
                }
            }
        }
        return namesake;
    }

    /*package*/ static CsmFile findSource(CsmFile header) {
        String name = getName(header.getAbsolutePath().toString());

        Collection<CsmFile> includers = CsmIncludeHierarchyResolver.getDefault().getFiles(header);

        for (CsmFile f : includers) {
            if (IpeUtils.areFilenamesEqual( getName(f.getAbsolutePath().toString()), name )) {
                // we found source file with the same name
                // as header and with dependency to it. Best shot.
                return f;
            }
        }

        // look for random namesake
        for (CsmFile f : header.getProject().getSourceFiles()) {
            if (IpeUtils.areFilenamesEqual( getName(f.getAbsolutePath().toString()), name )) {
                return f;
            }
        }

        return null;
    }

    private static void doToggle(final DataObject toggled) {
        // check if the data object has possibility to be opened in editor
        final OpenCookie oc = toggled.getCookie(OpenCookie.class);
        if (oc != null) {
            // remember current caret position
            JTextComponent textComponent = EditorRegistry.lastFocusedComponent();
            JumpList.checkAddEntry(textComponent);
            // try to open ASAP, but better not in EQ
            RequestProcessor.getDefault().post(new Runnable() {

                public void run() {
                    // open component
                    oc.open();
                }
            }, 0, Thread.MAX_PRIORITY);
        }
    }

    private static void doToggle(FileObject fo) {
        assert (fo != null);
        try {
            // find a data object for the input file object
            DataObject toggled = DataObject.find(fo);
            if (toggled != null) {
                doToggle(toggled);
            }
        } catch (DataObjectNotFoundException ex) {
            // may be error message?
        }
    }

    private static FileObject findToggleFile(final Node[] activatedNodes) {
        FileObject res = null;
        // check whether current file is C++ Source file
        DataObject dob = activatedNodes[0].getLookup().lookup(CCDataObject.class);
        if (dob == null) {
            // check whether current file is C Source file
            dob = activatedNodes[0].getLookup().lookup(CDataObject.class);
        }
        if (dob != null) {
            // it was Source file, find Header
            res = findBrother(dob, getSuffices(HDataLoader.getInstance().getExtensions()));
        } else {
            // check whether current file is Header file
            dob = activatedNodes[0].getLookup().lookup(HDataObject.class);
            if (dob != null) {
                // try to find C++ Source file
                res = findBrother(dob, getSuffices(CCDataLoader.getInstance().getExtensions()));
                if (res == null) {
                    // try to find C Source file
                    res = findBrother(dob, getSuffices(CDataLoader.getInstance().getExtensions()));
                }
            }
        }
        return res;
    }

    private static FileObject findBrother(DataObject dob, String[] ext) {
        assert (dob != null);
        assert (dob.getPrimaryFile() != null);
        if (ext != null && ext.length > 0) {
            // get a file object associated with the data object
            FileObject fo = dob.getPrimaryFile();
            FileObject[] childs = fo.getParent().getChildren();

            // try to find a file with the same name and one of passed extensions
            for (int i = 0; i < ext.length; i++) {
                // use FileUtilities to find brother of the file object
                // FileObject res = FileUtil.findBrother(fo, ext[i]);

                // IZ117750. Netbeans don't recognize MAC FS as case-insensitive
                // so FileObject.getFileObject(name, extension) can create
                // separate FileObjects for name.h and name.H although they are names
                // of the same file. So FileUtil.findBrother can't be used for now.
                
                String ne = fo.getName() + '.' + ext[i];
                for (int j = 0; j < childs.length; j++) {
                    FileObject fileObject = childs[j];
                    if ( IpeUtils.areFilenamesEqual( fileObject.getNameExt(), ne )) {
                        return fileObject;
                    }
                }
            }
        }
        return null;
    }

    private static String[] getSuffices(ExtensionList list) {
        List<String> suffixes = new ArrayList<String>();
        for (Enumeration e = list.extensions(); e != null && e.hasMoreElements();) {
            String ex = (String) e.nextElement();
            suffixes.add(ex);
        }
        return suffixes.toArray(new String[suffixes.size()]);
    }

    // Utility
    
    private static String getName(String path) {
        int idxSlash = path.lastIndexOf(File.separatorChar);
        String name = path.substring(idxSlash == -1 ? 0 : idxSlash + 1);
        int idxDot = name.lastIndexOf('.');
        return name.substring(0, idxDot == -1 ? name.length() : idxDot);
    }

    private static String trimExtension(String path) {
        // I hardly believe we can meet file w/o extension here but lets play safe
        int idxDot = path.lastIndexOf('.');
        return idxDot == -1 ? path : path.substring(0, idxDot);
    }

    private static String getMessage(String key) {
        return NbBundle.getMessage(CppSwitchAction.class, key);
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.