Skip to content
Browse files

initial import

  • Loading branch information...
0 parents commit e88ab6dcc5b6bdf4d79677c074535f7564476062 @armhold committed Feb 3, 2012
10 .gitignore
@@ -0,0 +1,10 @@
+*~
+
+# maven build artifacts
+target/**
+
+# Intelij IDEA work dir
+out/**
+
+# Intellij project files
+.idea/**
24 META-INF/plugin.xml
@@ -0,0 +1,24 @@
+<idea-plugin version="2">
+ <name>Wicket Source</name>
+ <description>In concert with the 42lines wicket-source Firefox/Chrome extension, this plugin allows you to navigate easily between a deployed Wicket application and the corresponding Java source files</description>
+ <version>1.0</version>
+ <vendor>George Armhold</vendor>
+ <idea-version since-build="8000"/>
+
+ <application-components>
+ <component>
+ <implementation-class>com.armhold.wicketsource.WicketSourceApplicationComponent</implementation-class>
+ </component>
+ </application-components>
+
+ <project-components>
+ </project-components>
+
+ <actions>
+ </actions>
+
+ <extensions defaultExtensionNs="com.intellij">
+ <applicationConfigurable instance="com.armhold.wicketsource.WicketSourceApplicationComponent"/>
+ </extensions>
+
+</idea-plugin>
96 src/com/armhold/wicketsource/FileNavigator.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2012 George Armhold
+ *
+ * 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.
+ * under the License.
+ */
+package com.armhold.wicketsource;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.WindowManager;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.search.GlobalSearchScope;
+
+/**
+ * supports navigation to the java source file in IDEA by classname + line number
+ *
+ * NB: This code was heavily inspired by the RemoteCall plugin by Alexander Zolotov
+ * @see https://github.com/zolotov/RemoteCall
+ */
+public class FileNavigator
+{
+ private static final Logger log = Logger.getInstance(FileNavigator.class);
+
+ public void navigateTo(final String className, final int line)
+ {
+ ApplicationManager.getApplication().invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ Project[] projects = ProjectManager.getInstance().getOpenProjects();
+
+ for (Project project : projects)
+ {
+ VirtualFile file = classNameToVirtualFile(project, className);
+ if (file != null)
+ {
+ navigate(project, file, line);
+ return;
+ }
+ }
+ }
+ });
+ }
+
+ private void navigate(Project project, VirtualFile file, int line)
+ {
+ OpenFileDescriptor openFileDescriptor = new OpenFileDescriptor(project, file, line, 0);
+ if (openFileDescriptor.canNavigate())
+ {
+ log.info("Trying to navigate to " + file.getPath() + ":" + line);
+ openFileDescriptor.navigate(true);
+ WindowManager.getInstance().suggestParentWindow(project).toFront();
+ }
+ else
+ {
+ log.info("Cannot navigate");
+ }
+ }
+
+ public static VirtualFile classNameToVirtualFile(Project project, String className)
+ {
+ GlobalSearchScope scope = GlobalSearchScope.allScope(project);
+
+ PsiClass psiClass = JavaPsiFacade.getInstance(project).findClass(className, scope);
+ if (null == psiClass)
+ {
+ return null;
+ }
+
+ PsiElement psiClassSource = psiClass.getNavigationElement();
+
+ // to avoid viewing the decompiled class
+ PsiFile psiFile = psiClassSource.getContainingFile();
+
+ return psiFile.getVirtualFile();
+ }
+
+}
163 src/com/armhold/wicketsource/Listener.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2012 George Armhold
+ *
+ * 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.
+ * under the License.
+ */
+package com.armhold.wicketsource;
+
+import com.intellij.openapi.diagnostic.Logger;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.*;
+
+import static java.net.URLDecoder.decode;
+
+/**
+ * A kind of dumbed-down http server that listens for GET requests.
+ *
+ * When a request comes in the "src" parameter is parsed and used to locate and
+ * navigate to a particular Java class in the IDEA editor.
+ *
+ * NB: This code was heavily inspired by the RemoteCall plugin by Alexander Zolotov
+ * @see https://github.com/zolotov/RemoteCall
+ */
+public class Listener implements Runnable
+{
+ private static final Logger log = Logger.getInstance(Listener.class);
+ private ServerSocket serverSocket;
+ private static final String CRLF = "\r\n";
+ private static final String NL = "\n";
+ private FileNavigator fileNavigator = new FileNavigator();
+
+ public Listener(ServerSocket serverSocket)
+ {
+ this.serverSocket = serverSocket;
+ }
+
+ public void run()
+ {
+ while (true)
+ {
+ Socket clientSocket;
+
+ try
+ {
+ clientSocket = serverSocket.accept();
+ }
+ catch (IOException e)
+ {
+ if (serverSocket.isClosed())
+ {
+ break;
+ }
+ else
+ {
+ log.error("Error while accepting", e);
+ continue;
+ }
+ }
+
+ try
+ {
+ BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
+
+ StringBuilder buf = new StringBuilder();
+ String inputLine;
+
+ while ((inputLine = in.readLine()) != null && ! inputLine.equals(CRLF) && ! inputLine.equals(NL) && ! inputLine.isEmpty())
+ {
+ buf.append(inputLine);
+ }
+ clientSocket.getOutputStream().write(("HTTP/1.1 200 OK" + CRLF + CRLF).getBytes());
+ clientSocket.close();
+
+ String requestString = buf.toString();
+
+ StringTokenizer tokenizer = new StringTokenizer(requestString);
+ String method = tokenizer.hasMoreElements() ? tokenizer.nextToken() : "";
+ if (!method.equals("GET"))
+ {
+ log.warn("Only GET requests allowed");
+ continue;
+ }
+
+ log.info("Received request " + requestString);
+ Map<String, String> parameters = getParametersFromUrl(tokenizer.nextToken());
+
+ String src = parameters.get("src") != null ? decode(parameters.get("src").trim(), "UTF-8") : "";
+ String password = parameters.get("p") != null ? decode(parameters.get("p").trim(), "UTF-8") : "";
+
+ String actualPassword = WicketSourceApplicationComponent.getPassword();
+ if (actualPassword != null && ! actualPassword.equals(""))
+ {
+ if (! actualPassword.equals(password))
+ {
+ log.warn("password does not match");
+ return;
+ }
+ }
+
+ // TODO: support password validation
+
+ log.info("Received message " + src);
+ handleMessage(src);
+ }
+ catch (IOException e)
+ {
+ log.error("Error", e);
+ }
+ }
+ }
+
+ public void handleMessage(String src)
+ {
+ // e.g.: com.example.mypackage:Foo:42
+ //
+ String[] pieces = src.split(":");
+ if (pieces.length != 3)
+ {
+ log.warn("expected 3 parts, got: " + src);
+ return;
+ }
+
+ String packageName = pieces[0];
+ String fileName = pieces[1].replace(".java", "");
+ int lineNumber = Integer.parseInt(pieces[2]);
+
+ String className = packageName + "." + fileName;
+ fileNavigator.navigateTo(className, lineNumber);
+ }
+
+ private Map<String, String> getParametersFromUrl(String url)
+ {
+ String parametersString = url.substring(url.indexOf('?') + 1);
+ Map<String, String> parameters = new HashMap<String, String>();
+ StringTokenizer tokenizer = new StringTokenizer(parametersString, "&");
+ while (tokenizer.hasMoreElements())
+ {
+ String[] parametersPair = tokenizer.nextToken().split("=", 2);
+ if (parametersPair.length > 1)
+ {
+ parameters.put(parametersPair[0], parametersPair[1]);
+ }
+ }
+
+ return parameters;
+ }
+
+}
59 src/com/armhold/wicketsource/MyUIForm.form
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.armhold.wicketsource.WicketSourceApplicationComponent">
+ <grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <margin top="0" left="0" bottom="0" right="0"/>
+ <constraints>
+ <xy x="20" y="20" width="500" height="400"/>
+ </constraints>
+ <properties/>
+ <border type="none"/>
+ <children>
+ <component id="574ae" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <font name="Segoe UI" size="14"/>
+ <text value="Current menu font settings:"/>
+ </properties>
+ </component>
+ <vspacer id="e99c">
+ <constraints>
+ <grid row="3" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
+ <component id="20f5e" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Port:"/>
+ </properties>
+ </component>
+ <component id="defdb" class="javax.swing.JLabel">
+ <constraints>
+ <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value="Password:"/>
+ </properties>
+ </component>
+ <component id="91f1d" class="javax.swing.JTextField" binding="portNumberField" default-binding="true">
+ <constraints>
+ <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ <component id="a610e" class="javax.swing.JPasswordField" binding="passwordField" default-binding="true">
+ <constraints>
+ <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+ <preferred-size width="150" height="-1"/>
+ </grid>
+ </constraints>
+ <properties/>
+ </component>
+ </children>
+ </grid>
+</form>
200 src/com/armhold/wicketsource/WicketSourceApplicationComponent.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2012 George Armhold
+ *
+ * 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.
+ * under the License.
+ */
+package com.armhold.wicketsource;
+
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.components.ApplicationComponent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.options.Configurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.ui.Messages;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+
+/**
+ * main entrypoint for the plugin
+ *
+ * NB: This code was heavily inspired by the RemoteCall plugin by Alexander Zolotov
+ * @see https://github.com/zolotov/RemoteCall
+ */
+public class WicketSourceApplicationComponent implements ApplicationComponent, Configurable
+{
+ private static final Logger log = Logger.getInstance(WicketSourceApplicationComponent.class);
+ public static final String PORT_KEY = "com.armhold.wicketsource.port";
+ public static final String PASSWORD_KEY = "com.armhold.wicketsource.password";
+ public static final int DEFAULT_PORT = 9123;
+
+ private static ServerSocket serverSocket;
+ private static Thread listener;
+
+ private JComponent myComponent;
+ private JPanel myPanel;
+ private JTextField portNumberField;
+ private JTextField passwordField;
+
+ public WicketSourceApplicationComponent()
+ {
+ log.info("starting");
+ }
+
+ public void initComponent()
+ {
+ initListenerAndSocket();
+ }
+
+ private void initListenerAndSocket()
+ {
+ final int port = getPort();
+
+ try
+ {
+ serverSocket = new ServerSocket();
+ serverSocket.bind(new InetSocketAddress("localhost", port));
+ log.info("WicketSource plugin listening on port: " + port);
+ }
+ catch (IOException e)
+ {
+ ApplicationManager.getApplication().invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ Messages.showMessageDialog("WicketSource plugin: can't bind to port: " + port, "WicketSource Plugin Error", Messages.getErrorIcon());
+ }
+ });
+
+ return;
+ }
+
+ Listener messageNotifier = new Listener(serverSocket);
+ listener = new Thread(messageNotifier);
+ listener.start();
+ }
+
+
+ private void disposeListenerAndSocket()
+ {
+ try
+ {
+ if (serverSocket != null)
+ {
+ serverSocket.close();
+ }
+
+ if (listener != null)
+ {
+ listener.interrupt();
+ }
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ public void disposeComponent()
+ {
+ disposeListenerAndSocket();
+ }
+
+ @NotNull
+ public String getComponentName()
+ {
+ return "WicketSource";
+ }
+
+ @Nls
+ @Override
+ public String getDisplayName()
+ {
+ return "WicketSource";
+ }
+
+ @Override
+ public Icon getIcon()
+ {
+ return null;
+ }
+
+ @Override
+ public String getHelpTopic()
+ {
+ return "preferences.lookFeel";
+ }
+
+ @Override
+ public JComponent createComponent()
+ {
+ // Define a set of possible values for combo boxes.
+ PropertiesComponent.getInstance().getValue("", "");
+
+ portNumberField.setText(Integer.toString(getPort()));
+ passwordField.setText(getPassword());
+
+ myComponent = myPanel;
+ return myComponent;
+ }
+
+ public static int getPort()
+ {
+ String result = PropertiesComponent.getInstance().getValue(PORT_KEY, Integer.toString(DEFAULT_PORT));
+ return Integer.parseInt(result);
+ }
+
+ public static String getPassword()
+ {
+ return PropertiesComponent.getInstance().getValue(PASSWORD_KEY, "");
+ }
+
+ @Override
+ public boolean isModified()
+ {
+ return true;
+ }
+
+ @Override
+ public void apply() throws ConfigurationException
+ {
+ String port = portNumberField.getText();
+ String password = passwordField.getText();
+
+ PropertiesComponent props = PropertiesComponent.getInstance();
+ props.setValue(PORT_KEY, port);
+ props.setValue(PASSWORD_KEY, password);
+
+ disposeListenerAndSocket();
+ initListenerAndSocket();
+ }
+
+ @Override
+ public void reset()
+ {
+
+ }
+
+ @Override
+ public void disposeUIResources()
+ {
+
+ }
+}
13 wicket-source-intellij.iml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="PLUGIN_MODULE" version="4">
+ <component name="DevKit.ModuleBuildProperties" url="file://$MODULE_DIR$/META-INF/plugin.xml" />
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ </content>
+ <orderEntry type="jdk" jdkName="IDEA IU-111.277" jdkType="IDEA JDK" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
+

0 comments on commit e88ab6d

Please sign in to comment.
Something went wrong with that request. Please try again.