/*******************************************************************************
* Copyright (c) 2014 Pivotal Software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springsource.ide.eclipse.commons.browser.javafx;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.lang.StringUtils;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.statushandlers.StatusManager;
import org.springsource.ide.eclipse.commons.browser.BrowserExtensions;
import org.springsource.ide.eclipse.commons.browser.BrowserPlugin;
import org.springsource.ide.eclipse.commons.browser.IBrowserToEclipseFunction;
import org.springsource.ide.eclipse.commons.browser.IEclipseToBrowserFunction;
import javafx.application.Platform;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import netscape.javascript.JSObject;
/**
*
* @author Miles Parker
*/
public class JavaFxBrowserManager {
private WebEngine engine;
private WebView view;
private boolean disposed;
private static final boolean DEBUG = false;
private Collection<IEclipseToBrowserFunction> onLoadFunctions;
private String currentUrl;
public void setClient(WebView view) {
this.view = view;
this.engine = view.getEngine();
JSObject window = (JSObject) engine.executeScript("window");
window.setMember("ide", this);
onLoadFunctions = new ArrayList<IEclipseToBrowserFunction>();
currentUrl = view.getEngine().locationProperty().get();
//Need to remove any query parameters that might break pattern matching for extensions
currentUrl = StringUtils.substringBeforeLast(currentUrl, "?");
currentUrl = StringUtils.substringBeforeLast(currentUrl, "&");
loadInitialFunctions();
}
private void loadInitialFunctions() {
IConfigurationElement[] extensions = BrowserExtensions.getExtensions(
BrowserExtensions.EXTENSION_ID_ECLIPSE_TO_BROWSER, null, currentUrl);
for (IConfigurationElement element : extensions) {
try {
String onLoad = element.getAttribute(BrowserExtensions.ELEMENT_ONLOAD);
if (onLoad != null && onLoad.equals("true")) {
onLoadFunctions.add((IEclipseToBrowserFunction) WorkbenchPlugin.createExtension(element,
BrowserExtensions.ELEMENT_CLASS));
}
}
catch (CoreException ex) {
StatusManager.getManager().handle(
new Status(IStatus.ERROR, BrowserPlugin.PLUGIN_ID,
"Could not instantiate browser element provider extension.", ex));
return;
}
}
callOnBrowser(onLoadFunctions);
}
/**
* Handle calls <i>from</i> Javascript functions on the browser. (This is
* called by reflection by JavaFx so there won't be any apparent usages for
* this method.)
* @param functionId
* @param argument
*/
public void call(String functionId, String argument) {
try {
IConfigurationElement element = BrowserExtensions.getExtension(
BrowserExtensions.EXTENSION_ID_BROWSER_TO_ECLIPSE, functionId, currentUrl);
if (element != null) {
IBrowserToEclipseFunction function = (IBrowserToEclipseFunction) WorkbenchPlugin.createExtension(
element, BrowserExtensions.ELEMENT_CLASS);
function.call(argument);
}
else {
StatusManager.getManager().handle(
new Status(IStatus.ERROR, BrowserPlugin.PLUGIN_ID,
"Could not instantiate browser function extension: " + functionId));
}
}
catch (CoreException ex) {
StatusManager.getManager().handle(
new Status(IStatus.ERROR, BrowserPlugin.PLUGIN_ID, "Could not find dashboard extension", ex));
return;
}
}
private void doCall(final IEclipseToBrowserFunction function) {
Platform.runLater(new Runnable() {
@Override
public void run() {
JSObject js = (JSObject) getEngine().executeScript("window");
IEclipseToBrowserFunction provider = function;
if (provider!=null) {
String fname = provider.getFunctionName();
Object[] fargs = provider.getArguments();
js.call(fname, fargs);
}
getView().requestLayout();
getView().setVisible(true);
if (DEBUG) {
printPageHtml();
}
}
});
}
/**
* Calls Javascript functions <i>to</i> the browser, refreshing the browser
* after each call
*/
public void callOnBrowser(final Collection<IEclipseToBrowserFunction> functions) {
final Collection<IEclipseToBrowserFunction> waitingFunctions = new CopyOnWriteArrayList<IEclipseToBrowserFunction>();
IEclipseToBrowserFunction.Callback callback = new IEclipseToBrowserFunction.Callback() {
@Override
public void ready(final IEclipseToBrowserFunction function) {
if (waitingFunctions.remove(function)) {
if (!disposed) {
doCall(function);
}
}
}
};
for (IEclipseToBrowserFunction function : functions) {
if (!function.isReady()) {
waitingFunctions.add(function);
function.setCallback(callback);
} else {
doCall(function);
}
}
callback.ready(null);
}
/**
* For debugging..
*/
private void printPageHtml() {
StringWriter sw = new StringWriter();
try {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.setOutputProperty(OutputKeys.METHOD, "html");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.transform(new DOMSource(getView().getEngine().getDocument()), new StreamResult(sw));
System.out.println(sw.toString());
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
public void dispose() {
disposed = true;
for (IEclipseToBrowserFunction function : onLoadFunctions) {
function.dispose();
}
}
/**
* @return the view
*/
public WebView getView() {
return view;
}
/**
* @return the engine
*/
public WebEngine getEngine() {
return engine;
}
}