blob: 6334cfb4b026d5c49acb346a70d25783057350f0 [file] [log] [blame]
/*
* Copyright 2000-2009 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.intellij.uiDesigner.snapShooter;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.uiDesigner.XmlWriter;
import com.intellij.uiDesigner.radComponents.RadComponent;
import com.intellij.uiDesigner.radComponents.RadRootContainer;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NonNls;
import javax.accessibility.AccessibleContext;
import javax.swing.*;
import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* @author yole
*/
public class SnapShooterDaemon implements Runnable {
private final Map<Integer, Component> myIdMap = new HashMap<Integer, Component>();
private int myNextId = 1;
private final BlockingQueue<String> myCommandQueue = new ArrayBlockingQueue<String>(20);
private final BlockingQueue<String> myResponseQueue = new ArrayBlockingQueue<String>(20);
private final int myPort;
public SnapShooterDaemon(final int port) {
myPort = port;
}
public void run() {
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(myPort, 50, InetAddress.getLocalHost());
}
catch(IOException ex) {
System.out.println("Failed to open server socket: " + ex);
return;
}
System.out.println("SnapShooter listening on port " + myPort);
//noinspection InfiniteLoopStatement
while (true) {
processClientConnection(serverSocket);
}
}
private void processClientConnection(final ServerSocket serverSocket) {
Socket clientSocket;
try {
clientSocket = serverSocket.accept();
System.out.println("SnapShooter connection accepted");
InputStreamReader reader = new InputStreamReader(clientSocket.getInputStream(), CharsetToolkit.UTF8_CHARSET);
BufferedReader bufferedReader = new BufferedReader(reader);
OutputStreamWriter writer = new OutputStreamWriter(clientSocket.getOutputStream(), CharsetToolkit.UTF8_CHARSET);
while(true) {
String command;
try {
command = bufferedReader.readLine();
}
catch(IOException ex) {
break;
}
if (command == null) {
System.out.println("End of stream receiving command");
break;
}
processCommand(command, writer);
}
}
catch(IOException ex) {
System.out.println("Exception in SnapShooter connection: " + ex);
}
}
private void processCommand(@NonNls final String command, final OutputStreamWriter writer) throws IOException {
if (command.startsWith("S")) {
SwingUtilities.invokeLater(new SuspendSwingRunnable());
}
else {
myCommandQueue.add(command);
if (command.startsWith("L") || command.startsWith("X")) {
String response;
try {
response = myResponseQueue.take();
}
catch (InterruptedException e) {
writer.close();
return;
}
writer.write(response);
writer.flush();
}
}
}
private String[] getChildren(final int id) {
List<String> result = new ArrayList<String>();
List<Component> children = getChildList(id);
for(Component child: children) {
SnapShotRemoteComponent rc = new SnapShotRemoteComponent(assignId(child),
child.getClass().getName(),
getLayoutManagerClass(child),
getChildText(child));
result.add(rc.toProtocolString());
}
return ArrayUtil.toStringArray(result);
}
private static String getLayoutManagerClass(final Component component) {
if (component instanceof JPanel) {
LayoutManager layoutManager = ((Container) component).getLayout();
if (layoutManager != null) {
Class layoutManagerClass = layoutManager.getClass();
while(!layoutManagerClass.getSuperclass().equals(Object.class)) {
layoutManagerClass = layoutManagerClass.getSuperclass();
}
return layoutManagerClass.getName();
}
}
return "";
}
private List<Component> getChildList(final int id) {
List<Component> children = new ArrayList<Component>();
if (id == 0) {
children = getRootWindows();
}
else {
Component parent = myIdMap.get(id);
if (parent instanceof RootPaneContainer) {
RootPaneContainer rpc = (RootPaneContainer) parent;
children.add(rpc.getContentPane());
}
else if (parent instanceof JSplitPane) {
JSplitPane splitPane = (JSplitPane) parent;
if (splitPane.getLeftComponent() != null) {
children.add(splitPane.getLeftComponent());
}
if (splitPane.getRightComponent() != null) {
children.add(splitPane.getRightComponent());
}
}
else if (parent instanceof JScrollPane) {
JScrollPane scrollPane = (JScrollPane) parent;
children.add(scrollPane.getViewport().getView());
}
else if (parent instanceof Container) {
Collections.addAll(children, ((Container) parent).getComponents());
}
}
return children;
}
private static String getChildText(final Component component) {
if (component instanceof Frame) {
Frame frame = (Frame) component;
return frame.getTitle();
}
final AccessibleContext accessibleContext = component.getAccessibleContext();
if (accessibleContext != null) {
final String text = accessibleContext.getAccessibleName();
if (text != null && text.length() > 0) {
return text;
}
}
return "";
}
private int assignId(final Component child) {
int result = myNextId;
myIdMap.put(result, child);
myNextId++;
return result;
}
private static List<Component> getRootWindows() {
List<Component> result = new ArrayList<Component>();
for(Frame frame: Frame.getFrames()) {
//noinspection HardCodedStringLiteral
if (!frame.getClass().getName().endsWith("SwingUtilities$SharedOwnerFrame")) {
result.add(frame);
}
for(Window window: frame.getOwnedWindows()) {
if (window.isVisible()) {
result.add(window);
}
}
}
return result;
}
private class SuspendSwingRunnable implements Runnable {
public void run() {
while(true) {
String command;
try {
command = myCommandQueue.take();
}
catch (InterruptedException e) {
break;
}
if (command.startsWith("R")) {
break;
}
String response = "";
if (command.startsWith("L")) {
response = doListCommand(command);
}
else if (command.startsWith("X")) {
response = doSnapshotCommand(command);
}
if (response.length() > 0) {
System.out.println("Sending response: " + response);
try {
myResponseQueue.put(response);
}
catch (InterruptedException e) {
break;
}
}
}
}
@NonNls
private String doSnapshotCommand(final String command) {
int id = Integer.parseInt(command.substring(1));
Component component = myIdMap.get(id);
XmlWriter xmlWriter = new XmlWriter();
RadRootContainer rootContainer = null;
try {
rootContainer = createFormSnapshot((JComponent) component);
}
catch (Exception ex) {
ex.printStackTrace();
return "E:" + ex.getMessage() + "\n";
}
rootContainer.write(xmlWriter);
return xmlWriter.getText();
}
private RadRootContainer createFormSnapshot(final JComponent component) {
SnapshotContext context = new SnapshotContext();
final RadComponent radComponent = RadComponent.createSnapshotComponent(context, component);
if (radComponent != null) {
radComponent.setBounds(new Rectangle(new Point(10, 10), component.getPreferredSize()));
context.getRootContainer().addComponent(radComponent);
context.postProcess();
}
return context.getRootContainer();
}
@NonNls
private String doListCommand(final String command) {
int id = Integer.parseInt(command.substring(1));
String[] children = getChildren(id);
StringBuilder result = new StringBuilder();
for(String child: children) {
result.append(child).append("\n");
}
result.append(".\n");
return result.toString();
}
}
}