| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.netbeans.modules.refactoring.php; |
| |
| import java.awt.Color; |
| import java.io.CharConversionException; |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.URISyntaxException; |
| import java.net.URL; |
| import java.util.Collections; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Stack; |
| import javax.swing.text.AttributeSet; |
| import javax.swing.text.Document; |
| import javax.swing.text.JTextComponent; |
| import javax.swing.text.StyleConstants; |
| import org.netbeans.api.editor.mimelookup.MimeLookup; |
| import org.netbeans.api.editor.mimelookup.MimePath; |
| import org.netbeans.api.editor.settings.FontColorSettings; |
| import org.netbeans.api.java.classpath.ClassPath; |
| import org.netbeans.api.lexer.Language; |
| import org.netbeans.api.lexer.Token; |
| import org.netbeans.api.lexer.TokenHierarchy; |
| import org.netbeans.api.lexer.TokenId; |
| import org.netbeans.api.lexer.TokenSequence; |
| import org.netbeans.api.project.Project; |
| import org.netbeans.api.project.ProjectUtils; |
| import org.netbeans.api.project.SourceGroup; |
| import org.netbeans.api.project.Sources; |
| import org.netbeans.api.project.ui.OpenProjects; |
| import org.netbeans.editor.BaseDocument; |
| import org.netbeans.modules.csl.spi.ParserResult; |
| import org.netbeans.modules.parsing.api.Source; |
| import org.netbeans.modules.php.api.util.FileUtils; |
| import org.netbeans.modules.php.editor.lexer.LexUtilities; |
| import org.netbeans.modules.php.editor.lexer.PHPDocCommentTokenId; |
| import org.netbeans.modules.php.editor.lexer.PHPTokenId; |
| import org.netbeans.modules.php.editor.parser.PHPParseResult; |
| import org.netbeans.modules.php.editor.parser.astnodes.ASTNode; |
| import org.netbeans.modules.php.editor.parser.astnodes.Expression; |
| import org.netbeans.modules.php.editor.parser.astnodes.Include; |
| import org.netbeans.modules.php.editor.parser.astnodes.ParenthesisExpression; |
| import org.netbeans.modules.php.editor.parser.astnodes.Program; |
| import org.netbeans.modules.php.editor.parser.astnodes.Scalar; |
| import org.netbeans.modules.php.editor.parser.astnodes.Scalar.Type; |
| import org.netbeans.modules.php.editor.parser.astnodes.visitors.DefaultVisitor; |
| import org.netbeans.modules.php.project.api.PhpSourcePath; |
| import org.netbeans.spi.project.ui.support.ProjectConvertors; |
| import org.openide.cookies.EditorCookie; |
| import org.openide.filesystems.FileObject; |
| import org.openide.filesystems.FileUtil; |
| import org.openide.filesystems.URLMapper; |
| import org.openide.loaders.DataObject; |
| import org.openide.loaders.DataObjectNotFoundException; |
| import org.openide.text.CloneableEditorSupport; |
| import org.openide.util.Exceptions; |
| import org.openide.util.Lookup; |
| import org.openide.util.Utilities; |
| import org.openide.windows.TopComponent; |
| import org.openide.xml.XMLUtil; |
| |
| /** |
| * Various utilies related to Php refactoring; the generic ones are based |
| * on the ones from the Java refactoring module. |
| * |
| * @author Jan Becicka, Tor Norbye, Jan Lahoda, Radek Matous |
| */ |
| public final class RefactoringUtils { |
| |
| private RefactoringUtils() { |
| } |
| |
| public static Program getRoot(ParserResult info) { |
| return (info instanceof PHPParseResult) ? ((PHPParseResult) info).getProgram() : null; |
| } |
| |
| public static Source getSource(Document doc) { |
| Source source = Source.create(doc); |
| return source; |
| } |
| |
| public static CloneableEditorSupport findCloneableEditorSupport(FileObject fo) { |
| DataObject dob = null; |
| try { |
| dob = DataObject.find(fo); |
| } catch (DataObjectNotFoundException ex) { |
| Exceptions.printStackTrace(ex); |
| } |
| return dob == null ? null : RefactoringUtils.findCloneableEditorSupport(dob); |
| } |
| |
| public static CloneableEditorSupport findCloneableEditorSupport(DataObject dob) { |
| Object obj = dob.getLookup().lookup(org.openide.cookies.OpenCookie.class); |
| if (obj instanceof CloneableEditorSupport) { |
| return (CloneableEditorSupport) obj; |
| } |
| obj = dob.getLookup().lookup(org.openide.cookies.EditorCookie.class); |
| if (obj instanceof CloneableEditorSupport) { |
| return (CloneableEditorSupport) obj; |
| } |
| return null; |
| } |
| |
| public static String htmlize(String input) { |
| try { |
| return XMLUtil.toElementContent(input); |
| } catch (CharConversionException cce) { |
| Exceptions.printStackTrace(cce); |
| return input; |
| } |
| } |
| |
| public static String getHtml(String text) { |
| StringBuilder buf = new StringBuilder(); |
| // TODO - check whether we need Js highlighting or rhtml highlighting |
| TokenHierarchy tokenH = TokenHierarchy.create(text, PHPTokenId.language()); |
| Lookup lookup = MimeLookup.getLookup(MimePath.get(FileUtils.PHP_MIME_TYPE)); |
| FontColorSettings settings = lookup.lookup(FontColorSettings.class); |
| @SuppressWarnings("unchecked") |
| TokenSequence<? extends TokenId> tok = tokenH.tokenSequence(); |
| while (tok.moveNext()) { |
| Token<? extends TokenId> token = tok.token(); |
| String category = token.id().name(); |
| AttributeSet set = settings.getTokenFontColors(category); |
| if (set == null) { |
| category = token.id().primaryCategory(); |
| if (category == null) { |
| category = "whitespace"; //NOI18N |
| |
| } |
| set = settings.getTokenFontColors(category); |
| } |
| String tokenText = htmlize(token.text().toString()); |
| buf.append(color(tokenText, set)); |
| } |
| return buf.toString(); |
| } |
| |
| private static String color(String string, AttributeSet set) { |
| if (set == null) { |
| return string; |
| } |
| if (string.trim().length() == 0) { |
| return string.replace(" ", " ").replace("\n", "<br>"); //NOI18N |
| |
| } |
| StringBuilder buf = new StringBuilder(string); |
| if (StyleConstants.isBold(set)) { |
| buf.insert(0, "<b>"); //NOI18N |
| |
| buf.append("</b>"); //NOI18N |
| |
| } |
| if (StyleConstants.isItalic(set)) { |
| buf.insert(0, "<i>"); //NOI18N |
| |
| buf.append("</i>"); //NOI18N |
| |
| } |
| if (StyleConstants.isStrikeThrough(set)) { |
| buf.insert(0, "<s>"); // NOI18N |
| |
| buf.append("</s>"); // NOI18N |
| |
| } |
| buf.insert(0, "<font color=" + getHTMLColor(StyleConstants.getForeground(set)) + ">"); //NOI18N |
| |
| buf.append("</font>"); //NOI18N |
| |
| return buf.toString(); |
| } |
| |
| private static String getHTMLColor(Color c) { |
| String colorR = "0" + Integer.toHexString(c.getRed()); //NOI18N |
| |
| colorR = colorR.substring(colorR.length() - 2); |
| String colorG = "0" + Integer.toHexString(c.getGreen()); //NOI18N |
| |
| colorG = colorG.substring(colorG.length() - 2); |
| String colorB = "0" + Integer.toHexString(c.getBlue()); //NOI18N |
| |
| colorB = colorB.substring(colorB.length() - 2); |
| String htmlColor = "#" + colorR + colorG + colorB; //NOI18N |
| |
| return htmlColor; |
| } |
| |
| public static boolean isFileInOpenProject(FileObject file) { |
| assert file != null; |
| Project p = ProjectConvertors.getNonConvertorOwner(file); |
| return OpenProjects.getDefault().isProjectOpen(p); |
| } |
| |
| public static boolean isOnSourceClasspath(FileObject fo) { |
| Project p = ProjectConvertors.getNonConvertorOwner(fo); |
| if (p == null) { |
| return false; |
| } |
| Project[] opened = OpenProjects.getDefault().getOpenProjects(); |
| for (int i = 0; i < opened.length; i++) { |
| if (p.equals(opened[i]) || opened[i].equals(p)) { |
| //SourceGroup[] gr = ProjectUtils.getSources(p).getSourceGroups(JsProject.SOURCES_TYPE_Js); |
| SourceGroup[] gr = ProjectUtils.getSources(p).getSourceGroups(Sources.TYPE_GENERIC); |
| for (int j = 0; j < gr.length; j++) { |
| if (fo == gr[j].getRootFolder()) { |
| return true; |
| } |
| if (FileUtil.isParentOf(gr[j].getRootFolder(), fo)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| return false; |
| } |
| |
| public static boolean isClasspathRoot(FileObject fo) { |
| ClassPath cp = ClassPath.getClassPath(fo, ClassPath.SOURCE); |
| if (cp != null) { |
| FileObject f = cp.findOwnerRoot(fo); |
| if (f != null) { |
| return fo.equals(f); |
| } |
| } |
| |
| return false; |
| } |
| |
| public static boolean isRefactorable(FileObject file) { |
| return FileUtils.isPhpFile(file) && isFileInOpenProject(file) && isOnSourceClasspath(file); |
| } |
| |
| /** |
| * Creates or finds FileObject according to. |
| * @param url |
| * @return FileObject |
| */ |
| public static FileObject getOrCreateFolder(URL url) throws IOException { |
| try { |
| FileObject result = URLMapper.findFileObject(url); |
| if (result != null) { |
| return result; |
| } |
| File f = Utilities.toFile(url.toURI()); |
| |
| result = FileUtil.createFolder(f); |
| return result; |
| } catch (URISyntaxException ex) { |
| throw new IOException(ex); |
| } |
| } |
| |
| public static boolean isOutsidePhp(Lookup lookup, FileObject fo) { |
| return isOutsideLanguage(lookup, fo, PHPTokenId.language()); |
| } |
| |
| public static boolean isOutsidePHPDoc(Lookup lookup, FileObject fo) { |
| return isOutsideLanguage(lookup, fo, PHPDocCommentTokenId.language()); |
| } |
| |
| public static boolean isOutsideLanguage(Lookup lookup, FileObject fo, Language<? extends TokenId> language) { |
| boolean result = false; |
| if (FileUtils.isPhpFile(fo)) { |
| EditorCookie ec = lookup.lookup(EditorCookie.class); |
| if (isFromEditor(ec)) { |
| JTextComponent textC = ec.getOpenedPanes()[0]; |
| Document d = textC.getDocument(); |
| if (!(d instanceof BaseDocument)) { |
| result = true; |
| } else { |
| int caret = textC.getCaretPosition(); |
| result = LexUtilities.getMostEmbeddedTokenSequence(d, caret, true).language() != language; |
| } |
| } |
| } |
| return result; |
| } |
| |
| public static boolean isFromEditor(EditorCookie ec) { |
| if (ec != null && ec.getOpenedPanes() != null) { |
| TopComponent activetc = TopComponent.getRegistry().getActivated(); |
| if (activetc instanceof CloneableEditorSupport.Pane) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public static List<ASTNode> underCaret(ParserResult info, final int offset) { |
| class Result extends Error { |
| |
| private final Stack<ASTNode> result; |
| |
| public Result(Stack<ASTNode> result) { |
| this.result = result; |
| } |
| |
| @Override |
| public Throwable fillInStackTrace() { |
| return this; |
| } |
| } |
| try { |
| new DefaultVisitor() { |
| |
| private final Stack<ASTNode> s = new Stack<>(); |
| |
| @Override |
| public void scan(ASTNode node) { |
| if (node == null) { |
| return; |
| } |
| |
| if (node.getStartOffset() <= offset && offset <= node.getEndOffset()) { |
| s.push(node); |
| super.scan(node); |
| throw new Result(s); |
| } |
| } |
| }.scan(RefactoringUtils.getRoot(info)); |
| } catch (Result r) { |
| return new LinkedList<>(r.result); |
| } |
| |
| return Collections.emptyList(); |
| } |
| |
| public static boolean isQuoted(String value) { |
| return value.length() >= 2 |
| && (value.startsWith("\"") || value.startsWith("'")) |
| && (value.endsWith("\"") || value.endsWith("'")); |
| } |
| |
| public static String dequote(String value) { |
| assert isQuoted(value); |
| |
| return value.substring(1, value.length() - 1); |
| } |
| |
| public static FileObject resolveInclude(ParserResult info, Include include) { |
| Expression e = include.getExpression(); |
| if (e instanceof ParenthesisExpression) { |
| e = ((ParenthesisExpression) e).getExpression(); |
| } |
| if (e instanceof Scalar) { |
| Scalar s = (Scalar) e; |
| if (Type.STRING == s.getScalarType()) { |
| String fileName = s.getStringValue(); |
| fileName = fileName.length() >= 2 ? fileName.substring(1, fileName.length() - 1) : fileName; //TODO: not nice |
| return resolveRelativeFile(info, fileName); |
| } |
| } |
| return null; |
| } |
| |
| private static FileObject resolveRelativeFile(ParserResult info, String name) { |
| PhpSourcePath psp = null; |
| Project p = ProjectConvertors.getNonConvertorOwner(info.getSnapshot().getSource().getFileObject()); |
| |
| if (p != null) { |
| psp = p.getLookup().lookup(PhpSourcePath.class); |
| } |
| |
| while (true) { |
| FileObject result; |
| |
| if (psp != null) { |
| result = PhpSourcePath.resolveFile(info.getSnapshot().getSource().getFileObject().getParent(), name); |
| } else { |
| result = info.getSnapshot().getSource().getFileObject().getParent().getFileObject(name); |
| } |
| |
| if (result != null) { |
| return result; |
| } |
| |
| //try to strip a directory from the "name": |
| int slash = name.indexOf('/'); |
| |
| if (slash != (-1)) { |
| name = name.substring(slash + 1); |
| } else { |
| return null; |
| } |
| } |
| } |
| |
| public static FileObject getFile(Document doc) { |
| Object o = doc.getProperty(Document.StreamDescriptionProperty); |
| |
| if (o instanceof DataObject) { |
| DataObject od = (DataObject) o; |
| |
| return od.getPrimaryFile(); |
| } |
| |
| return null; |
| } |
| |
| public static boolean isUsersFile(FileObject fileObject) { |
| boolean result = false; |
| PhpSourcePath.FileType fileType = PhpSourcePath.getFileType(fileObject); |
| if (fileType != PhpSourcePath.FileType.INCLUDE && fileType != PhpSourcePath.FileType.INTERNAL) { |
| result = true; |
| } |
| return result; |
| } |
| } |