blob: 7a9e9963c67fd01fc03b48f999468fab0eef01cc [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed 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 com.jetbrains.python.documentation;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.intellij.openapi.components.*;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFunction;
import com.intellij.psi.util.QualifiedName;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author yole
*/
@State(
name = "PythonDocumentationMap",
storages = {
@Storage(
file = StoragePathMacros.APP_CONFIG + "/other.xml")
}
)
public class PythonDocumentationMap implements PersistentStateComponent<PythonDocumentationMap.State> {
public static PythonDocumentationMap getInstance() {
return ServiceManager.getService(PythonDocumentationMap.class);
}
public static class Entry {
private String myPrefix;
private String myUrlPattern;
public Entry() {
}
public Entry(String prefix, String urlPattern) {
myPrefix = prefix;
myUrlPattern = urlPattern;
}
public String getPrefix() {
return myPrefix;
}
public String getUrlPattern() {
return myUrlPattern;
}
public void setPrefix(String prefix) {
myPrefix = prefix;
}
public void setUrlPattern(String urlPattern) {
myUrlPattern = urlPattern;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Entry entry = (Entry)o;
if (!myPrefix.equals(entry.myPrefix)) return false;
if (!myUrlPattern.equals(entry.myUrlPattern)) return false;
return true;
}
@Override
public int hashCode() {
int result = myPrefix.hashCode();
result = 31 * result + myUrlPattern.hashCode();
return result;
}
}
public static class State {
private List<Entry> myEntries = new ArrayList<Entry>();
public State() {
addEntry("PyQt4", "http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/{class.name.lower}.html#{function.name}");
addEntry("PySide", "http://srinikom.github.com/pyside-docs/{module.name.slashes}/{class.name}.html#{module.name}.{element.qname}");
addEntry("gtk", "http://library.gnome.org/devel/pygtk/stable/class-gtk{class.name.lower}.html#method-gtk{class.name.lower}--{function.name.dashes}");
addEntry("wx", "http://www.wxpython.org/docs/api/{module.name}.{class.name}-class.html#{function.name}");
addEntry("numpy", "http://docs.scipy.org/doc/numpy/reference/{}generated/{module.name}.{element.name}.html");
addEntry("scipy", "http://docs.scipy.org/doc/scipy/reference/{}generated/{module.name}.{element.name}.html");
addEntry("kivy", "http://kivy.org/docs/api-{module.name}.html");
}
public List<Entry> getEntries() {
return myEntries;
}
public void setEntries(List<Entry> entries) {
myEntries = entries;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
State state = (State)o;
return Sets.newHashSet(myEntries).equals(Sets.newHashSet(state.getEntries()));
}
@Override
public int hashCode() {
return myEntries != null ? myEntries.hashCode() : 0;
}
private void addEntry(String qName, String pattern) {
myEntries.add(new Entry(qName, pattern));
}
}
private State myState = new State();
@Override
public State getState() {
return myState;
}
@Override
public void loadState(State state) {
myState = state;
}
public List<Entry> getEntries() {
return ImmutableList.copyOf(myState.getEntries());
}
public void setEntries(List<Entry> entries) {
myState.setEntries(entries);
}
@Nullable
public String urlFor(QualifiedName moduleQName, @Nullable PsiNamedElement element, String pyVersion) {
for (Entry entry : myState.myEntries) {
if (moduleQName.matchesPrefix(QualifiedName.fromDottedString(entry.myPrefix))) {
return transformPattern(entry.myUrlPattern, moduleQName, element, pyVersion);
}
}
return null;
}
@Nullable
public String rootUrlFor(QualifiedName moduleQName) {
for (Entry entry : myState.myEntries) {
if (moduleQName.matchesPrefix(QualifiedName.fromDottedString(entry.myPrefix))) {
return rootForPattern(entry.myUrlPattern);
}
}
return null;
}
private static String rootForPattern(String urlPattern) {
int pos = urlPattern.indexOf('{');
return pos >= 0 ? urlPattern.substring(0, pos) : urlPattern;
}
@Nullable
private static String transformPattern(@NotNull String urlPattern, QualifiedName moduleQName, @Nullable PsiNamedElement element,
String pyVersion) {
Map<String, String> macros = new HashMap<String, String>();
macros.put("element.name", element == null ? null : element.getName());
PyClass pyClass = element == null ? null : PsiTreeUtil.getParentOfType(element, PyClass.class, false);
macros.put("class.name", pyClass == null ? null : pyClass.getName());
if (element != null) {
StringBuilder qName = new StringBuilder(moduleQName.toString()).append(".");
if (element instanceof PyFunction && ((PyFunction)element).getContainingClass() != null) {
qName.append(((PyFunction)element).getContainingClass().getName()).append(".");
}
qName.append(element.getName());
macros.put("element.qname", qName.toString());
}
else {
macros.put("element.qname", "");
}
macros.put("function.name", element instanceof PyFunction ? element.getName() : "");
macros.put("module.name", moduleQName.toString());
macros.put("python.version", pyVersion);
final String pattern = transformPattern(urlPattern, macros);
if (pattern == null) {
return rootForPattern(urlPattern);
}
return pattern;
}
@Nullable
private static String transformPattern(@NotNull String urlPattern, Map<String, String> macroValues) {
for (Map.Entry<String, String> entry : macroValues.entrySet()) {
if (entry.getValue() == null) {
if (urlPattern.contains("{" + entry.getKey())) {
return null;
}
continue;
}
urlPattern = urlPattern
.replace("{" + entry.getKey() + "}", entry.getValue())
.replace("{" + entry.getKey() + ".lower}", entry.getValue().toLowerCase())
.replace("{" + entry.getKey() + ".slashes}", entry.getValue().replace(".", "/"))
.replace("{" + entry.getKey() + ".dashes}", entry.getValue().replace("_", "-"));
}
return urlPattern.replace("{}", "");
}
}