Package org.nbphpcouncil.modules.php.yii.editor

Source Code of org.nbphpcouncil.modules.php.yii.editor.YiiGoToFileHyperlinkProvider$MessageVisitor

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2013 Oracle and/or its affiliates. All rights reserved.
*
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.
*
* 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.  Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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]"
*
* 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.
*
* Contributor(s):
*
* Portions Copyrighted 2013 Sun Microsystems, Inc.
*/
package org.nbphpcouncil.modules.php.yii.editor;

import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import org.nbphpcouncil.modules.php.yii.YiiModule;
import org.nbphpcouncil.modules.php.yii.YiiModuleFactory;
import static org.nbphpcouncil.modules.php.yii.editor.YiiHyperlinkProviderExt.DEFAULT_OFFSET;
import org.nbphpcouncil.modules.php.yii.editor.navi.GoToDefaultItem;
import org.nbphpcouncil.modules.php.yii.editor.navi.GoToItem;
import org.nbphpcouncil.modules.php.yii.editor.navi.GoToPopup;
import org.nbphpcouncil.modules.php.yii.editor.navi.GoToTItem;
import org.nbphpcouncil.modules.php.yii.editor.navi.PopupUtil;
import org.nbphpcouncil.modules.php.yii.util.YiiDocUtils;
import org.nbphpcouncil.modules.php.yii.util.YiiPathAliasSupport;
import org.nbphpcouncil.modules.php.yii.util.YiiUtils;
import org.nbphpcouncil.modules.php.yii.util.YiiViewPathSupport;
import org.netbeans.api.editor.EditorRegistry;
import org.netbeans.api.editor.mimelookup.MimeRegistration;
import org.netbeans.api.lexer.Token;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.lib.editor.hyperlink.spi.HyperlinkProviderExt;
import org.netbeans.lib.editor.hyperlink.spi.HyperlinkType;
import org.netbeans.modules.csl.api.UiUtils;
import org.netbeans.modules.csl.spi.ParserResult;
import org.netbeans.modules.editor.NbEditorUtilities;
import org.netbeans.modules.parsing.api.ParserManager;
import org.netbeans.modules.parsing.api.ResultIterator;
import org.netbeans.modules.parsing.api.Source;
import org.netbeans.modules.parsing.api.UserTask;
import org.netbeans.modules.parsing.spi.ParseException;
import org.netbeans.modules.parsing.spi.indexing.support.QuerySupport;
import org.netbeans.modules.php.api.phpmodule.PhpModule;
import org.netbeans.modules.php.api.util.FileUtils;
import org.netbeans.modules.php.api.util.StringUtils;
import org.netbeans.modules.php.editor.api.ElementQuery;
import org.netbeans.modules.php.editor.api.ElementQueryFactory;
import org.netbeans.modules.php.editor.api.NameKind;
import org.netbeans.modules.php.editor.api.QuerySupportFactory;
import org.netbeans.modules.php.editor.api.elements.ClassElement;
import org.netbeans.modules.php.editor.lexer.PHPTokenId;
import org.netbeans.modules.php.editor.parser.api.Utils;
import org.netbeans.modules.php.editor.parser.astnodes.ArrayElement;
import org.netbeans.modules.php.editor.parser.astnodes.Expression;
import org.netbeans.modules.php.editor.parser.astnodes.Scalar;
import org.netbeans.modules.php.editor.parser.astnodes.visitors.DefaultVisitor;
import org.openide.filesystems.FileObject;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;

/**
*
* @author junichi11
*/
@MimeRegistration(mimeType = FileUtils.PHP_MIME_TYPE, service = HyperlinkProviderExt.class)
public class YiiGoToFileHyperlinkProvider extends YiiHyperlinkProviderExt {

    private static final Set<String> methods = new HashSet<String>();
    private static final String DEFAULT_MESSAGES_DIR_NAME = "messages"; // NOI18N
    private List<GoToItem> goToItems;
    private int paramCount;
    private static final Logger LOGGER = Logger.getLogger(YiiGoToFileHyperlinkProvider.class.getName());

    static {
        methods.add("widget"); // NOI18N
        methods.add("beginWidget"); // NOI18N
        methods.add("endWidget"); // NOI18N
        methods.add("createWidget"); // NOI18N
        methods.add("import"); // NOI18N
        methods.add("beginCache"); // NOI18N
        methods.add("beginContent"); // NOI18N
        methods.add("t"); // NOI18N
    }

    @Override
    public void performClickAction(Document doc, int offset, HyperlinkType type) {
        // open file
        if (goToItems.size() == 1) {
            GoToItem item = goToItems.get(0);
            UiUtils.open(item.getFileObject(), DEFAULT_OFFSET);
        }
        // i18n
        JTextComponent editor = EditorRegistry.lastFocusedComponent();
        // show popup
        if (editor != null && !goToItems.isEmpty()) {
            try {
                Rectangle rectangle = editor.modelToView(offset);
                final Point point = new Point(rectangle.x, rectangle.y + rectangle.height);
                SwingUtilities.convertPointToScreen(point, editor);
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        PopupUtil.showPopup(new GoToPopup(" Select LocaleID ", goToItems), " Select LocaleID ", point.x, point.y, true, 0);
                    }
                });
            } catch (BadLocationException ex) {
                Exceptions.printStackTrace(ex);
            }
        }
    }

    @Override
    protected boolean verifyState(Document doc, int offset, HyperlinkType type) {
        // get TokenSequence
        TokenSequence<PHPTokenId> ts = YiiDocUtils.getTokenSequence(doc);
        if (ts == null) {
            return false;
        }

        // get current positon text
        target = getCurrentPositionString(ts, offset);
        if (target == null) {
            return false;
        }

        // set span
        targetStart = ts.offset() + 1;
        targetEnd = targetStart + target.length();

        // create go to items
        PhpModule phpModule = PhpModule.Factory.inferPhpModule();
        goToItems = createGoToItems(phpModule, doc, offset);
        return !goToItems.isEmpty();
    }

    @Override
    @NbBundle.Messages({
        "LBL.NotFoundFile=Not found file",
        "LBL.I18N.Message=If you click, go to list is displayed"
    })
    public String getTooltipText(Document doc, int offset, HyperlinkType type) {
        if (goToItems.size() == 1) {
            return goToItems.get(0).getFileObject().getPath();
        }
        if (!goToItems.isEmpty()) {
            return Bundle.LBL_I18N_Message();
        }
        return Bundle.LBL_NotFoundFile();
    }

    /**
     * Get method name of current caret position.
     *
     * @param ts TokenSequence
     * @return method name.
     */
    private String getMethodName(TokenSequence<PHPTokenId> ts) {
        String name = null;
        paramCount = 1;
        while (ts.movePrevious()) {
            Token<PHPTokenId> token = ts.token();
            String text = token.text().toString();
            PHPTokenId id = token.id();
            if (id == PHPTokenId.PHP_TOKEN && text.equals(",")) { // NOI18N
                // XXX this is not exact
                paramCount++;
                continue;
            }
            if (id == PHPTokenId.PHP_SEMICOLON || id == PHPTokenId.PHP_FUNCTION || id == PHPTokenId.PHP_OPENTAG) {
                break;
            }
            if (id == PHPTokenId.PHP_STRING) {
                name = text;
                break;
            }
        }
        return name;
    }

    /**
     * Get class file if target is class name.
     *
     * @param phpModule PhpModule
     * @return FileObject for class if file exists, otherwise null.
     */
    private FileObject getClassFileObject(PhpModule phpModule, String target) {
        if (!StringUtils.isEmpty(target) && !target.contains(" ")) { // NOI18N
            ElementQuery.Index indexQuery = ElementQueryFactory.createIndexQuery(QuerySupportFactory.get(phpModule.getSourceDirectory()));
            Set<ClassElement> classElements = indexQuery.getClasses(NameKind.create(target, QuerySupport.Kind.EXACT));
            for (ClassElement element : classElements) {
                if (element.getName().equals(target)) {
                    return element.getFileObject();
                }
            }
        }
        return null;
    }

    /**
     * Create GoToItems.
     *
     * @param phpModule
     * @param doc
     * @param offset
     * @return GoToItem list
     */
    private List<GoToItem> createGoToItems(PhpModule phpModule, Document doc, int offset) {
        List<GoToItem> items = new ArrayList<GoToItem>();
        // check whether target is class name
        FileObject classFile = getClassFileObject(phpModule, target);
        if (classFile != null) {
            items.add(new GoToDefaultItem(classFile, DEFAULT_OFFSET));
            return items;
        }

        // get method name
        TokenSequence<PHPTokenId> ts = YiiDocUtils.getTokenSequence(doc);
        ts.move(offset);
        ts.moveNext();
        String methodName = getMethodName(ts);
        if (!methods.contains(methodName)) {
            return items;
        }

        // for i18n
        if (methodName.equals("t")) { // NOI18N
            return createGoToTItems(phpModule, ts, offset);
        }

        // for absolute view path
        FileObject file = null;
        if (YiiViewPathSupport.isAbsoluteViewPath(target)) {
            // for application's view path
            FileObject currentFile = NbEditorUtilities.getFileObject(doc);
            file = YiiViewPathSupport.getAbsoluteViewFile(target, currentFile);
        }

        // for path alias
        if (file == null) {
            file = YiiPathAliasSupport.getFileObject(phpModule, target);
        }
        if (file != null && file.isData()) {
            items.add(new GoToDefaultItem(file, DEFAULT_OFFSET));
        }
        return items;
    }

    /**
     * Create GoToTItems.
     *
     * @param phpModule
     * @return true if item list is not empty, otherwise false.
     */
    private List<GoToItem> createGoToTItems(PhpModule phpModule, TokenSequence<PHPTokenId> ts, int offset) {
        List<GoToItem> items = new ArrayList<GoToItem>();

        YiiModule yiiModule = YiiModuleFactory.create(phpModule);
        FileObject application = yiiModule.getApplication();
        if (application == null || paramCount > 2) {
            return items;
        }

        // move to offset of message for second parameter
        String message = ""; // NOI18N
        if (paramCount == 2) {
            String tFirstParam = getTFirstParam(ts, offset);
            if (tFirstParam == null) {
                return items;
            }
            message = target;
            target = tFirstParam;
        }

        // get messages directory
        FileObject messagesDirectory = null;
        String messagesDirectoryName = DEFAULT_MESSAGES_DIR_NAME;

        // check whether text contains class name
        String className = ""; // NOI18N
        if (target.contains(".")) { // NOI18N
            int dotIndex = target.indexOf("."); // NOI18N
            className = target.substring(0, dotIndex);
            String categoryName = target.substring(dotIndex + 1);
            target = categoryName;
            FileObject classFile = getClassFileObject(phpModule, className);
            if (classFile != null) {
                FileObject parent = classFile.getParent();
                if (parent != null && parent.isFolder()) {
                    messagesDirectory = parent.getFileObject(messagesDirectoryName);
                }
            }
        }

        if (className.isEmpty() && messagesDirectory == null) {
            messagesDirectory = yiiModule.getMessages();
        }
        if (messagesDirectory == null) {
            return items;
        }

        // sort
        FileObject[] children = messagesDirectory.getChildren();
        YiiUtils.sort(children);

        // add item
        for (FileObject child : children) {
            // create GoToItem
            int messageOffset = DEFAULT_OFFSET;
            final Set<Integer> mOffset = new HashSet<Integer>();
            final String messageKey = message;
            FileObject messageFile = child.getFileObject(target + ".php"); // NOI18N
            if (messageFile != null) {
                // click second parameter
                if (!StringUtils.isEmpty(messageKey)) {
                    // get offset for massage
                    try {
                        ParserManager.parse(Collections.singleton(Source.create(messageFile)), new UserTask() {
                            @Override
                            public void run(ResultIterator resultIterator) throws Exception {
                                ParserResult parseResult = (ParserResult) resultIterator.getParserResult();
                                if (parseResult == null) {
                                    return;
                                }
                                final MessageVisitor messageVisitor = new MessageVisitor(messageKey);
                                messageVisitor.scan(Utils.getRoot(parseResult));
                                mOffset.add(messageVisitor.getOffset());
                            }
                        });
                    } catch (ParseException ex) {
                        LOGGER.log(Level.WARNING, null, ex);
                    }
                }

                // set offset
                if (!mOffset.isEmpty()) {
                    for (Integer o : mOffset) {
                        messageOffset = o.intValue();
                        break;
                    }
                }
                items.add(new GoToTItem(messageFile, messageOffset, child.getName()));
            }
        }
        return items;
    }

    /**
     * Get first parameter for t method. It is valid for only second parameter.
     *
     * @param ts TokenSequence
     * @param offset current offset
     * @return first parameter for t method.
     */
    private String getTFirstParam(TokenSequence<PHPTokenId> ts, int offset) {
        if (paramCount == 2) {
            ts.move(offset);
            ts.moveNext();

            // search method
            while (ts.movePrevious()) {
                Token<PHPTokenId> token = ts.token();
                if (token.id() == PHPTokenId.PHP_STRING) {
                    break;
                }
            }

            // search first parameter
            while (ts.moveNext()) {
                Token<PHPTokenId> token = ts.token();
                PHPTokenId id = token.id();
                if (id == PHPTokenId.PHP_SEMICOLON) {
                    break;
                }
                if (id == PHPTokenId.PHP_CONSTANT_ENCAPSED_STRING) {
                    String text = token.text().toString();
                    return text.substring(1, text.length() - 1);
                }
            }
        }
        return null;
    }

    //~ inner class
    private class MessageVisitor extends DefaultVisitor {

        private int offset;
        private final String message;

        public MessageVisitor(String message) {
            this.message = message;
        }

        @Override
        public void visit(ArrayElement node) {
            super.visit(node);
            Expression key = node.getKey();
            if (key instanceof Scalar) {
                Scalar s = (Scalar) key;
                if (s.getScalarType() == Scalar.Type.STRING) {
                    String keyValue = s.getStringValue();
                    keyValue = keyValue.substring(1, keyValue.length() - 1);
                    if (keyValue.equals(message)) {
                        offset = node.getStartOffset();
                    }
                }
            }
        }

        public int getOffset() {
            return offset;
        }
    }
}
TOP

Related Classes of org.nbphpcouncil.modules.php.yii.editor.YiiGoToFileHyperlinkProvider$MessageVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.