package librepdf;
import librepdf.document.Factory;
import librepdf.document.Document;
import java.io.Closeable;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.net.ConnectException;
import org.jruby.*;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.javasupport.JavaEmbedUtils;
import com.sun.star.beans.XPropertySet;
import com.sun.star.beans.PropertyValue;
import com.sun.star.bridge.XBridge;
import com.sun.star.bridge.XBridgeFactory;
import com.sun.star.comp.helper.Bootstrap;
import com.sun.star.connection.NoConnectException;
import com.sun.star.connection.XConnection;
import com.sun.star.connection.XConnector;
import com.sun.star.frame.XComponentLoader;
import com.sun.star.frame.XStorable;
import com.sun.star.lang.EventObject;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XEventListener;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.task.ErrorCodeIOException;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
public final class Connection implements Closeable, Finalizable
{
public static String DEFAULT_HOST = "127.0.0.1";
public static int DEFAULT_PORT = 8100;
private final Ruby runtime;
private final String host;
private final int port;
private boolean closed = false;
private XComponent bridgeComponent;
private XComponentContext componentContext;
private XMultiComponentFactory serviceManager;
public Connection() throws ConnectException {
this(new HashMap<String, Object>(), null);
}
public Connection(RubyProc proc) throws ConnectException {
this(new HashMap<String, Object>(), proc);
}
public Connection(Map<String, Object> options) throws ConnectException {
this(options, null);
}
public Connection(Map<String, Object> options, RubyProc proc) throws ConnectException {
this.runtime = Ruby.getGlobalRuntime();
this.runtime.addInternalFinalizer(this);
if (options.containsKey("host")) {
this.host = options.get("host").toString();
} else {
this.host = DEFAULT_HOST;
}
if (options.containsKey("port")) {
final Object p = options.get("port");
if (!(p instanceof Long)) {
throw new IllegalArgumentException("The port required the number between 0-65535.");
}
final Long l = (Long)p;
if (!(0 < l && l < 65536)) {
throw new IllegalArgumentException("The port required the number between 0-65535.");
}
this.port = l.intValue();
} else {
this.port = DEFAULT_PORT;
}
this.open();
if (proc != null) {
try {
final ThreadContext context = this.runtime.getCurrentContext();
final IRubyObject[] args = { JavaEmbedUtils.javaToRuby(this.runtime, this) };
proc.call(context, args);
} finally {
try {
this.close();
} catch (Exception e) {
}
}
}
}
@Override
public void finalize() throws Throwable {
try {
this.close();
} catch(IOException e) {}
}
@Override
public synchronized void close() throws IOException {
if (!this.closed) {
this.bridgeComponent.dispose();
this.runtime.removeInternalFinalizer(this);
this.closed = true;
}
}
@Override
public String toString() {
return "#<Librepdf::Connection host=" + this.host + ",port=" + this.port + ">";
}
public boolean isClosed() {
return this.closed;
}
public String inspect() {
return this.toString();
}
private synchronized void open() throws ConnectException {
final String connectionString = "socket,host=" + this.host + ",port=" + this.port + ",tcpNoDelay=1";
try {
final XComponentContext localContext = Bootstrap.createInitialComponentContext(null);
final XMultiComponentFactory localServiceManager = localContext.getServiceManager();
final XConnector connector = (XConnector) UnoRuntime.queryInterface(XConnector.class, localServiceManager
.createInstanceWithContext("com.sun.star.connection.Connector", localContext));
final XConnection connection = connector.connect(connectionString);
final XBridgeFactory bridgeFactory = (XBridgeFactory) UnoRuntime.queryInterface(XBridgeFactory.class,
localServiceManager.createInstanceWithContext("com.sun.star.bridge.BridgeFactory", localContext));
final XBridge bridge = bridgeFactory.createBridge("", "urp", connection, null);
this.bridgeComponent = (XComponent) UnoRuntime.queryInterface(XComponent.class, bridge);
this.serviceManager = (XMultiComponentFactory) UnoRuntime.queryInterface(XMultiComponentFactory.class,
bridge.getInstance("StarOffice.ServiceManager"));
final XPropertySet properties = (XPropertySet) UnoRuntime.queryInterface(XPropertySet.class,
this.serviceManager);
this.componentContext = (XComponentContext) UnoRuntime.queryInterface(XComponentContext.class, properties
.getPropertyValue("DefaultContext"));
} catch (Exception e) {
throw new ConnectException("connection failed: " + connectionString + ": " + e.getMessage());
}
}
public Document load(String inputUrl) {
return this.load(inputUrl, new HashMap<String, Object>());
}
public Document load(String inputUrl, Map<String, Object> options) {
if (inputUrl == null) {
throw new IllegalArgumentException("Null connot be set for inputUrl.");
}
if (this.closed) {
throw new IllegalArgumentException("The connection has not established it.");
}
options.put("Hidden", true);
options.put("ReadOnly", true);
final PropertyValue[] inputProperties = Utils.toPropertyValues(options);
final XComponent document = this.loadInternal(inputUrl, inputProperties);
return Factory.factory(this, this.runtime, document);
}
public Document load(String inputUrl, RubyProc proc) {
return this.load(inputUrl, new HashMap<String, Object>(), proc);
}
public Document load(String inputUrl, Map<String, Object> options, RubyProc proc) {
if (inputUrl == null) {
throw new IllegalArgumentException("Null connot be set for inputUrl.");
}
if (this.closed) {
throw new IllegalArgumentException("The connection has not established it.");
}
options.put("Hidden", true);
options.put("ReadOnly", true);
final PropertyValue[] inputProperties = Utils.toPropertyValues(options);
final XComponent document = this.loadInternal(inputUrl, inputProperties);
return Factory.factory(this, this.runtime, document, proc);
}
private XComponentLoader getDesktop() {
return (XComponentLoader) UnoRuntime.queryInterface(XComponentLoader.class,
this.getService("com.sun.star.frame.Desktop"));
}
private Object getService(String className) {
if (className == null) {
throw new IllegalArgumentException("Null connot be set for className.");
}
try {
return this.serviceManager.createInstanceWithContext(className, this.componentContext);
} catch (Exception e) {
throw new RuntimeException("could not obtain service: " + className, e);
}
}
private XComponent loadInternal(String inputUrl, PropertyValue[] properties) {
final XComponentLoader desktop = this.getDesktop();
try {
return desktop.loadComponentFromURL(inputUrl, "_blank", 0, properties);
} catch (ErrorCodeIOException e) {
throw new RuntimeException("could not load input document. " + e.ErrCode, e);
} catch (Exception e) {
throw new RuntimeException("could not load input document.", e);
}
}
}