blob: 31f06efeb879398dab0074fa399414ca289a22a7 [file] [log] [blame]
/*
* 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(" ", "&nbsp;").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;
}
}