From df1d4d475d6fd2ee4386eb1e583e2d0c60eecae6 Mon Sep 17 00:00:00 2001 From: totetmatt Date: Wed, 6 Jan 2016 14:19:13 +0100 Subject: [PATCH 01/30] Project migration and API migration --- build.xml | 14 + modules/DesktopStreaming/pom.xml | 96 + .../gephi/desktop/streaming/ReportPanel.form | 172 ++ .../gephi/desktop/streaming/ReportPanel.java | 400 +++++ .../streaming/StreamingClientPanel.form | 144 ++ .../streaming/StreamingClientPanel.java | 242 +++ .../streaming/StreamingConnectionNode.java | 234 +++ .../desktop/streaming/StreamingModel.java | 283 +++ .../streaming/StreamingServerNode.java | 139 ++ .../streaming/StreamingSettingsPanel.form | 195 ++ .../streaming/StreamingSettingsPanel.java | 266 +++ .../streaming/StreamingTopComponent.form | 128 ++ .../streaming/StreamingTopComponent.java | 280 +++ .../streaming/StreamingUIController.java | 396 +++++ .../DesktopStreaming/src/main/nbm/manifest.mf | 2 + .../gephi/desktop/streaming/Bundle.properties | 51 + .../desktop/streaming/resources/critical.png | Bin 0 -> 653 bytes .../streaming/resources/dot_connected.png | Bin 0 -> 269 bytes .../streaming/resources/dot_disconnected.png | Bin 0 -> 265 bytes .../desktop/streaming/resources/dot_error.png | Bin 0 -> 284 bytes .../streaming/resources/dot_sendrec.png | Bin 0 -> 239 bytes .../desktop/streaming/resources/empty.png | Bin 0 -> 178 bytes .../streaming/resources/gephiclient.png | Bin 0 -> 927 bytes .../streaming/resources/gephimaster.png | Bin 0 -> 976 bytes .../desktop/streaming/resources/info.png | Bin 0 -> 652 bytes .../streaming/resources/masterconnection.jpg | Bin 0 -> 460 bytes .../streaming/resources/media-stream.png | Bin 0 -> 960 bytes .../desktop/streaming/resources/minus.jpg | Bin 0 -> 763 bytes .../desktop/streaming/resources/plus.jpg | Bin 0 -> 703 bytes .../desktop/streaming/resources/severe.png | Bin 0 -> 723 bytes .../desktop/streaming/resources/warning.gif | Bin 0 -> 585 bytes modules/GraphStreaming/pom.xml | 37 + .../GraphStreaming/src/main/nbm/manifest.mf | 2 + .../gephi/graphstreaming/Bundle.properties | 8 + modules/JettyWrapper/pom.xml | 71 + modules/JettyWrapper/src/main/nbm/manifest.mf | 2 + .../org/gephi/lib/jetty/Bundle.properties | 2 + modules/StreamingAPI/pom.xml | 46 + .../api/CompositeGraphEventHandler.java | 99 ++ .../streaming/api/Graph2EventListener.java | 285 +++ .../streaming/api/GraphEventHandler.java | 69 + .../api/GraphUpdaterEventHandler.java | 557 ++++++ .../java/org/gephi/streaming/api/Issue.java | 101 ++ .../streaming/api/PropertiesAssociations.java | 159 ++ .../streaming/api/PropertyAssociation.java | 94 + .../java/org/gephi/streaming/api/Report.java | 201 +++ .../org/gephi/streaming/api/StreamReader.java | 151 ++ .../streaming/api/StreamReaderFactory.java | 75 + .../org/gephi/streaming/api/StreamType.java | 67 + .../org/gephi/streaming/api/StreamWriter.java | 86 + .../streaming/api/StreamWriterFactory.java | 71 + .../streaming/api/StreamingConnection.java | 124 ++ .../streaming/api/StreamingController.java | 105 ++ .../streaming/api/StreamingEndpoint.java | 143 ++ .../streaming/api/event/EdgeAddedEvent.java | 120 ++ .../streaming/api/event/ElementEvent.java | 106 ++ .../streaming/api/event/ElementType.java | 61 + .../gephi/streaming/api/event/EventType.java | 61 + .../streaming/api/event/FilterEvent.java | 92 + .../gephi/streaming/api/event/GraphEvent.java | 148 ++ .../api/event/GraphEventBuilder.java | 98 + modules/StreamingAPI/src/main/nbm/manifest.mf | 2 + .../org/gephi/streaming/api/Bundle.properties | 5 + .../org/gephi/streaming/api/layer.xml | 4 + modules/StreamingImpl/pom.xml | 61 + .../gephi/streaming/impl/FilterFactory.java | 204 +++ .../streaming/impl/GraphEventContainer.java | 143 ++ .../impl/StreamReaderFactoryImpl.java | 105 ++ .../impl/StreamWriterFactoryImpl.java | 101 ++ .../impl/StreamingConnectionImpl.java | 244 +++ .../impl/StreamingControllerImpl.java | 279 +++ .../gephi/streaming/impl/dgs/BaseParser.java | 811 +++++++++ .../gephi/streaming/impl/dgs/DGSParser.java | 471 +++++ .../streaming/impl/dgs/DGSParserListener.java | 71 + .../streaming/impl/dgs/DGSStreamReader.java | 157 ++ .../streaming/impl/dgs/DGSStreamType.java | 75 + .../streaming/impl/dgs/DGSStreamWriter.java | 311 ++++ .../streaming/impl/json/JSONStreamReader.java | 365 ++++ .../streaming/impl/json/JSONStreamType.java | 75 + .../streaming/impl/json/JSONStreamWriter.java | 332 ++++ .../gephi/streaming/impl/json/parser/CDL.java | 279 +++ .../streaming/impl/json/parser/Cookie.java | 169 ++ .../impl/json/parser/CookieList.java | 90 + .../streaming/impl/json/parser/HTTP.java | 163 ++ .../impl/json/parser/HTTPTokener.java | 77 + .../streaming/impl/json/parser/JSONArray.java | 918 ++++++++++ .../impl/json/parser/JSONConstants.java | 65 + .../impl/json/parser/JSONException.java | 31 + .../streaming/impl/json/parser/JSONML.java | 455 +++++ .../impl/json/parser/JSONObject.java | 1584 +++++++++++++++++ .../impl/json/parser/JSONString.java | 18 + .../impl/json/parser/JSONStringer.java | 78 + .../impl/json/parser/JSONTokener.java | 435 +++++ .../impl/json/parser/JSONWriter.java | 323 ++++ .../gephi/streaming/impl/json/parser/XML.java | 441 +++++ .../impl/json/parser/XMLTokener.java | 365 ++++ .../StreamingImpl/src/main/nbm/manifest.mf | 2 + .../gephi/streaming/impl/Bundle.properties | 5 + .../org/gephi/streaming/impl/cacerts | Bin 0 -> 1266 bytes .../org/gephi/streaming/impl/layer.xml | 4 + modules/StreamingServer/pom.xml | 69 + .../server/AuthenticationFilter.java | 113 ++ .../gephi/streaming/server/ClientManager.java | 68 + .../streaming/server/ServerController.java | 76 + .../server/ServerControllerFactory.java | 64 + .../streaming/server/StreamingServer.java | 103 ++ .../server/StreamingServerConfig.java | 170 ++ .../gephi/streaming/server/impl/Base64.java | 575 ++++++ .../impl/BasicAuthenticationFilter.java | 134 ++ .../server/impl/ClientManagerImpl.java | 133 ++ .../server/impl/DynamicGraphWriter.java | 349 ++++ .../impl/FilteredGraphEventHandler.java | 70 + .../impl/GraphBufferedEventHandler.java | 78 + .../streaming/server/impl/GraphWriter.java | 172 ++ .../impl/ServerControllerFactoryImpl.java | 64 + .../server/impl/ServerControllerImpl.java | 210 +++ .../server/impl/ServerOperationExecutor.java | 274 +++ .../server/impl/jetty/GephiWebSocket.java | 119 ++ .../impl/jetty/GephiWebSocketAcceptor.java | 72 + .../impl/jetty/StreamingServerImpl.java | 347 ++++ .../StreamingServer/src/main/nbm/manifest.mf | 2 + .../gephi/streaming/server/Bundle.properties | 5 + .../gephi/streaming/server/impl/localhost.p12 | Bin 0 -> 2805 bytes .../org/gephi/streaming/server/layer.xml | 4 + pom.xml | 8 +- 125 files changed, 18879 insertions(+), 1 deletion(-) create mode 100644 build.xml create mode 100644 modules/DesktopStreaming/pom.xml create mode 100644 modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/ReportPanel.form create mode 100644 modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/ReportPanel.java create mode 100644 modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingClientPanel.form create mode 100644 modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingClientPanel.java create mode 100644 modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingConnectionNode.java create mode 100644 modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingModel.java create mode 100644 modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingServerNode.java create mode 100644 modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingSettingsPanel.form create mode 100644 modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingSettingsPanel.java create mode 100644 modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingTopComponent.form create mode 100644 modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingTopComponent.java create mode 100644 modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingUIController.java create mode 100644 modules/DesktopStreaming/src/main/nbm/manifest.mf create mode 100644 modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/Bundle.properties create mode 100644 modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/critical.png create mode 100644 modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/dot_connected.png create mode 100644 modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/dot_disconnected.png create mode 100644 modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/dot_error.png create mode 100644 modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/dot_sendrec.png create mode 100644 modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/empty.png create mode 100644 modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/gephiclient.png create mode 100644 modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/gephimaster.png create mode 100644 modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/info.png create mode 100644 modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/masterconnection.jpg create mode 100644 modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/media-stream.png create mode 100644 modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/minus.jpg create mode 100644 modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/plus.jpg create mode 100644 modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/severe.png create mode 100644 modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/warning.gif create mode 100644 modules/GraphStreaming/pom.xml create mode 100644 modules/GraphStreaming/src/main/nbm/manifest.mf create mode 100644 modules/GraphStreaming/src/main/resources/org/gephi/graphstreaming/Bundle.properties create mode 100644 modules/JettyWrapper/pom.xml create mode 100644 modules/JettyWrapper/src/main/nbm/manifest.mf create mode 100644 modules/JettyWrapper/src/main/resources/org/gephi/lib/jetty/Bundle.properties create mode 100644 modules/StreamingAPI/pom.xml create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/CompositeGraphEventHandler.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Graph2EventListener.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphEventHandler.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Issue.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/PropertiesAssociations.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/PropertyAssociation.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Report.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamReader.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamReaderFactory.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamType.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamWriter.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamWriterFactory.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamingConnection.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamingController.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamingEndpoint.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/EdgeAddedEvent.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/ElementEvent.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/ElementType.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/EventType.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/FilterEvent.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/GraphEvent.java create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/GraphEventBuilder.java create mode 100644 modules/StreamingAPI/src/main/nbm/manifest.mf create mode 100644 modules/StreamingAPI/src/main/resources/org/gephi/streaming/api/Bundle.properties create mode 100644 modules/StreamingAPI/src/main/resources/org/gephi/streaming/api/layer.xml create mode 100644 modules/StreamingImpl/pom.xml create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/FilterFactory.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/GraphEventContainer.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamReaderFactoryImpl.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamWriterFactoryImpl.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamingConnectionImpl.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamingControllerImpl.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/BaseParser.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSParser.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSParserListener.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSStreamReader.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSStreamType.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSStreamWriter.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/JSONStreamReader.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/JSONStreamType.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/JSONStreamWriter.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/CDL.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/Cookie.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/CookieList.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/HTTP.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/HTTPTokener.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONArray.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONConstants.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONException.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONML.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONObject.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONString.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONStringer.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONTokener.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONWriter.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/XML.java create mode 100644 modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/XMLTokener.java create mode 100644 modules/StreamingImpl/src/main/nbm/manifest.mf create mode 100644 modules/StreamingImpl/src/main/resources/org/gephi/streaming/impl/Bundle.properties create mode 100644 modules/StreamingImpl/src/main/resources/org/gephi/streaming/impl/cacerts create mode 100644 modules/StreamingImpl/src/main/resources/org/gephi/streaming/impl/layer.xml create mode 100644 modules/StreamingServer/pom.xml create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/AuthenticationFilter.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/ClientManager.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/ServerController.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/ServerControllerFactory.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/StreamingServer.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/StreamingServerConfig.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/Base64.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/BasicAuthenticationFilter.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ClientManagerImpl.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/DynamicGraphWriter.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/FilteredGraphEventHandler.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphBufferedEventHandler.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphWriter.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerControllerFactoryImpl.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerControllerImpl.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerOperationExecutor.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/jetty/GephiWebSocket.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/jetty/GephiWebSocketAcceptor.java create mode 100644 modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/jetty/StreamingServerImpl.java create mode 100644 modules/StreamingServer/src/main/nbm/manifest.mf create mode 100644 modules/StreamingServer/src/main/resources/org/gephi/streaming/server/Bundle.properties create mode 100644 modules/StreamingServer/src/main/resources/org/gephi/streaming/server/impl/localhost.p12 create mode 100644 modules/StreamingServer/src/main/resources/org/gephi/streaming/server/layer.xml diff --git a/build.xml b/build.xml new file mode 100644 index 0000000000..5ccfd4c882 --- /dev/null +++ b/build.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/modules/DesktopStreaming/pom.xml b/modules/DesktopStreaming/pom.xml new file mode 100644 index 0000000000..1d6c5dce8c --- /dev/null +++ b/modules/DesktopStreaming/pom.xml @@ -0,0 +1,96 @@ + + + 4.0.0 + + gephi-plugin-parent + org.gephi + 0.9.0 + + + org.gephi.desktop + streaming + 1.0.0 + nbm + + DesktopStreaming + + + + + org.gephi + ui-components + + + org.netbeans.api + org-openide-dialogs + + + org.netbeans.api + org-openide-awt + + + org.netbeans.api + org-openide-explorer + + + org.netbeans.api + org-openide-windows + + + org.netbeans.api + org-netbeans-swing-outline + + + org.netbeans.api + org-netbeans-modules-settings + + + org.netbeans.api + org-openide-nodes + + + org.netbeans.api + org-openide-util-lookup + + + org.gephi + graph-api + + + org.netbeans.api + org-openide-util + + + org.gephi + project-api + + + + org.gephi.streaming + api + 1.0.0 + + + org.gephi.streaming + server + 1.0.0 + + + + + + + + org.codehaus.mojo + nbm-maven-plugin + + Unknown + $sourcecode_url + + + + + + + + diff --git a/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/ReportPanel.form b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/ReportPanel.form new file mode 100644 index 0000000000..c14cf3fa3d --- /dev/null +++ b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/ReportPanel.form @@ -0,0 +1,172 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/ReportPanel.java b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/ReportPanel.java new file mode 100644 index 0000000000..f532a0c803 --- /dev/null +++ b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/ReportPanel.java @@ -0,0 +1,400 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ + +package org.gephi.desktop.streaming; + +import java.awt.Color; +import java.util.List; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.event.TreeModelListener; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; +import org.gephi.streaming.api.Issue; +import org.gephi.streaming.api.Report; +import org.gephi.ui.components.BusyUtils; +import org.netbeans.swing.outline.DefaultOutlineModel; +import org.netbeans.swing.outline.OutlineModel; +import org.netbeans.swing.outline.RenderDataProvider; +import org.netbeans.swing.outline.RowModel; +import org.openide.util.NbPreferences; + +/** + * A JPanel to show Report information. + * @see Report + * + * @author Andre Panisson + */ +public class ReportPanel extends javax.swing.JPanel { + + //Preferences + private final static String SHOW_ISSUES = "ReportPanel_Show_Issues"; + private final static String SHOW_REPORT = "ReportPanel_Show_Report"; + private ThreadGroup fillingThreads; + //Icons + private ImageIcon infoIcon; + private ImageIcon warningIcon; + private ImageIcon severeIcon; + private ImageIcon criticalIcon; + + private long lastRefresh = 0; + + public ReportPanel() { + initComponents(); + initIcons(); + + fillingThreads = new ThreadGroup("Report Panel Issues"); + } + + public void initIcons() { + infoIcon = new javax.swing.ImageIcon(getClass().getResource("/org/gephi/desktop/streaming/resources/info.png")); + warningIcon = new javax.swing.ImageIcon(getClass().getResource("/org/gephi/desktop/streaming/resources/warning.gif")); + severeIcon = new javax.swing.ImageIcon(getClass().getResource("/org/gephi/desktop/streaming/resources/severe.png")); + criticalIcon = new javax.swing.ImageIcon(getClass().getResource("/org/gephi/desktop/streaming/resources/critical.png")); + } + + public void setData(Report report) { + fillIssues(report); + fillReport(report); + fillStats(report); + lastRefresh = System.currentTimeMillis(); + } + + public void refreshData(Report report) { + long now = System.currentTimeMillis(); + if (lastRefresh < now-500) { + fillIssues(report); + fillReport(report); + fillStats(report); + } + } + + private void fillIssues(Report report) { + final List issues = report.getIssues(); + if (issues.isEmpty()) { + JLabel label = new JLabel("No issue found"); + label.setHorizontalAlignment(SwingConstants.CENTER); + tab1ScrollPane.setViewportView(label); + } else { + //Busy label + final BusyUtils.BusyLabel busyLabel = BusyUtils.createCenteredBusyLabel(tab1ScrollPane, "Retrieving issues...", issuesOutline); + + //Thread + Thread thread = new Thread(fillingThreads, new Runnable() { + + public void run() { + busyLabel.setBusy(true); + final TreeModel treeMdl = new IssueTreeModel(issues); + final OutlineModel mdl = DefaultOutlineModel.createOutlineModel(treeMdl, new IssueRowModel(), true); + + SwingUtilities.invokeLater(new Runnable() { + + public void run() { + issuesOutline.setRootVisible(false); + issuesOutline.setRenderDataProvider(new IssueRenderer()); + issuesOutline.setModel(mdl); + busyLabel.setBusy(false); + } + }); + } + }, "Report Panel Issues Outline"); + if (NbPreferences.forModule(ReportPanel.class).getBoolean(SHOW_ISSUES, true)) { + thread.start(); + } + } + } + + private void fillReport(final Report report) { + Thread thread = new Thread(fillingThreads, new Runnable() { + + public void run() { + final String str = report.getText(); + SwingUtilities.invokeLater(new Runnable() { + + public void run() { + reportEditor.setText(str); + } + }); + } + }, "Report Panel Issues Report"); + if (NbPreferences.forModule(ReportPanel.class).getBoolean(SHOW_REPORT, true)) { + thread.start(); + } + } + + private void fillStats(final Report report) { + sourceLabel.setText(report.getSource()); + + int eventCount = report.getEventCounter(); + eventCountLabel.setText("" + eventCount); + } + + public void destroy() { + fillingThreads.interrupt(); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + processorStrategyRadio = new javax.swing.ButtonGroup(); + labelSrc = new javax.swing.JLabel(); + sourceLabel = new javax.swing.JLabel(); + tabbedPane = new javax.swing.JTabbedPane(); + tab1ScrollPane = new javax.swing.JScrollPane(); + issuesOutline = new org.netbeans.swing.outline.Outline(); + tab2ScrollPane = new javax.swing.JScrollPane(); + reportEditor = new javax.swing.JEditorPane(); + jPanel1 = new javax.swing.JPanel(); + labelEventCount = new javax.swing.JLabel(); + eventCountLabel = new javax.swing.JLabel(); + jLabel1 = new javax.swing.JLabel(); + + labelSrc.setText(org.openide.util.NbBundle.getMessage(ReportPanel.class, "ReportPanel.labelSrc.text")); // NOI18N + + sourceLabel.setText(org.openide.util.NbBundle.getMessage(ReportPanel.class, "ReportPanel.sourceLabel.text")); // NOI18N + + tab1ScrollPane.setViewportView(issuesOutline); + + tabbedPane.addTab(org.openide.util.NbBundle.getMessage(ReportPanel.class, "ReportPanel.tab1ScrollPane.TabConstraints.tabTitle"), tab1ScrollPane); // NOI18N + + tab2ScrollPane.setViewportView(reportEditor); + + tabbedPane.addTab(org.openide.util.NbBundle.getMessage(ReportPanel.class, "ReportPanel.tab2ScrollPane.TabConstraints.tabTitle"), tab2ScrollPane); // NOI18N + + jPanel1.setLayout(new java.awt.GridBagLayout()); + + labelEventCount.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N + labelEventCount.setText(org.openide.util.NbBundle.getMessage(ReportPanel.class, "ReportPanel.labelEventCount.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(10, 0, 6, 0); + jPanel1.add(labelEventCount, gridBagConstraints); + + eventCountLabel.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N + eventCountLabel.setText(org.openide.util.NbBundle.getMessage(ReportPanel.class, "ReportPanel.eventCountLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(10, 10, 6, 0); + jPanel1.add(eventCountLabel, gridBagConstraints); + + jLabel1.setText(org.openide.util.NbBundle.getMessage(ReportPanel.class, "ReportPanel.jLabel1.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 4; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.weighty = 1.0; + jPanel1.add(jLabel1, gridBagConstraints); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(tabbedPane, javax.swing.GroupLayout.DEFAULT_SIZE, 608, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(labelSrc) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(sourceLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 553, Short.MAX_VALUE))) + .addContainerGap()) + .addGroup(layout.createSequentialGroup() + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, 395, Short.MAX_VALUE) + .addGap(225, 225, 225)))) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(labelSrc) + .addComponent(sourceLabel)) + .addGap(18, 18, 18) + .addComponent(tabbedPane, javax.swing.GroupLayout.PREFERRED_SIZE, 175, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel eventCountLabel; + private org.netbeans.swing.outline.Outline issuesOutline; + private javax.swing.JLabel jLabel1; + private javax.swing.JPanel jPanel1; + private javax.swing.JLabel labelEventCount; + private javax.swing.JLabel labelSrc; + private javax.swing.ButtonGroup processorStrategyRadio; + private javax.swing.JEditorPane reportEditor; + private javax.swing.JLabel sourceLabel; + private javax.swing.JScrollPane tab1ScrollPane; + private javax.swing.JScrollPane tab2ScrollPane; + private javax.swing.JTabbedPane tabbedPane; + // End of variables declaration//GEN-END:variables + + private class IssueTreeModel implements TreeModel { + + private List issues; + + public IssueTreeModel(List issues) { + this.issues = issues; + } + + public Object getRoot() { + return "root"; + } + + public Object getChild(Object parent, int index) { + return issues.get(index); + } + + public int getChildCount(Object parent) { + return issues.size(); + } + + public boolean isLeaf(Object node) { + if (node instanceof Issue) { + return true; + } + return false; + } + + public void valueForPathChanged(TreePath path, Object newValue) { + } + + public int getIndexOfChild(Object parent, Object child) { + return issues.indexOf(child); + } + + public void addTreeModelListener(TreeModelListener l) { + } + + public void removeTreeModelListener(TreeModelListener l) { + } + } + + private class IssueRowModel implements RowModel { + + public int getColumnCount() { + return 1; + } + + public Object getValueFor(Object node, int column) { + if (node instanceof Issue) { + Issue issue = (Issue) node; + return issue.getLevel().toString(); + } + return ""; + } + + public Class getColumnClass(int column) { + return String.class; + } + + public boolean isCellEditable(Object node, int column) { + return false; + } + + public void setValueFor(Object node, int column, Object value) { + } + + public String getColumnName(int column) { + return "Issues"; + } + } + + private class IssueRenderer implements RenderDataProvider { + + public String getDisplayName(Object o) { + Issue issue = (Issue) o; + return issue.getMessage(); + } + + public boolean isHtmlDisplayName(Object o) { + return false; + } + + public Color getBackground(Object o) { + return null; + } + + public Color getForeground(Object o) { + return null; + } + + public String getTooltipText(Object o) { + return ""; + } + + public Icon getIcon(Object o) { + Issue issue = (Issue) o; + switch (issue.getLevel()) { + case INFO: + return infoIcon; + case WARNING: + return warningIcon; + case SEVERE: + return severeIcon; + case CRITICAL: + return criticalIcon; + } + return null; + } + } +} diff --git a/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingClientPanel.form b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingClientPanel.form new file mode 100644 index 0000000000..bf4e03cf36 --- /dev/null +++ b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingClientPanel.form @@ -0,0 +1,144 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingClientPanel.java b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingClientPanel.java new file mode 100644 index 0000000000..afb39758e0 --- /dev/null +++ b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingClientPanel.java @@ -0,0 +1,242 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ + +package org.gephi.desktop.streaming; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collection; +import javax.swing.ComboBoxModel; +import javax.swing.DefaultComboBoxModel; +import org.gephi.streaming.api.StreamingEndpoint; +import org.gephi.streaming.api.StreamType; +import org.netbeans.validation.api.builtin.Validators; +import org.netbeans.validation.api.ui.ValidationGroup; +import org.netbeans.validation.api.ui.ValidationPanel; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; + +/** + * A JPanel to fill StreamingEndpoint information in order to connect to a stream. + * @see StreamingEndpoint + * + * @author panisson + */ +public class StreamingClientPanel extends javax.swing.JPanel { + + // Default selected type is JSON + private static final String DEFAULT_FORMAT = "JSON"; + + private ComboBoxModel streamTypeComboBoxModel; + + /** Creates new form StreamingClientPanel */ + public StreamingClientPanel() { + + streamTypeComboBoxModel = new DefaultComboBoxModel(getStreamTypes()); + + // Sets the default selected format + for (int i=0; i streamTypes = Lookup.getDefault().lookupAll(StreamType.class); + return streamTypes.toArray(new StreamType[0]); + } + + public static ValidationPanel createValidationPanel(final StreamingClientPanel innerPanel) { + ValidationPanel validationPanel = new ValidationPanel(); + if (innerPanel == null) { + throw new NullPointerException(); + } + validationPanel.setInnerComponent(innerPanel); + + ValidationGroup group = validationPanel.getValidationGroup(); + group.add(innerPanel.streamUrlTextField, Validators.REQUIRE_NON_EMPTY_STRING); + group.add(innerPanel.streamUrlTextField, Validators.URL_MUST_BE_VALID); + + return validationPanel; + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + streamUrlLabel = new javax.swing.JLabel(); + streamUrlTextField = new javax.swing.JTextField(); + streamTypeLabel = new javax.swing.JLabel(); + streamTypeComboBox = new javax.swing.JComboBox(); + basicAuthCheckBox = new javax.swing.JCheckBox(); + usernameLabel = new javax.swing.JLabel(); + usernameTextField = new javax.swing.JTextField(); + passwordField = new javax.swing.JPasswordField(); + passwordLabel = new javax.swing.JLabel(); + + streamUrlLabel.setText(org.openide.util.NbBundle.getMessage(StreamingClientPanel.class, "StreamingClientPanel.streamUrlLabel.text")); // NOI18N + + streamUrlTextField.setText(org.openide.util.NbBundle.getMessage(StreamingClientPanel.class, "StreamingClientPanel.sourceURL.text")); // NOI18N + streamUrlTextField.setName("sourceURL"); // NOI18N + + streamTypeLabel.setText(org.openide.util.NbBundle.getMessage(StreamingClientPanel.class, "StreamingClientPanel.streamTypeLabel.text")); // NOI18N + + streamTypeComboBox.setModel(streamTypeComboBoxModel); + + basicAuthCheckBox.setText(org.openide.util.NbBundle.getMessage(StreamingClientPanel.class, "StreamingClientPanel.basicAuthCheckBox.text")); // NOI18N + basicAuthCheckBox.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + basicAuthCheckBoxStateChanged(evt); + } + }); + + usernameLabel.setText(org.openide.util.NbBundle.getMessage(StreamingClientPanel.class, "StreamingClientPanel.usernameLabel.text")); // NOI18N + usernameLabel.setEnabled(false); + + usernameTextField.setText(org.openide.util.NbBundle.getMessage(StreamingClientPanel.class, "StreamingClientPanel.usernameTextField.text")); // NOI18N + usernameTextField.setEnabled(false); + + passwordField.setText(org.openide.util.NbBundle.getMessage(StreamingClientPanel.class, "StreamingClientPanel.passwordField.text")); // NOI18N + passwordField.setEnabled(false); + + passwordLabel.setText(org.openide.util.NbBundle.getMessage(StreamingClientPanel.class, "StreamingClientPanel.passwordLabel.text")); // NOI18N + passwordLabel.setEnabled(false); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(streamUrlTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 378, Short.MAX_VALUE) + .addComponent(streamUrlLabel) + .addGroup(layout.createSequentialGroup() + .addComponent(streamTypeLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(streamTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 124, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(basicAuthCheckBox) + .addGroup(layout.createSequentialGroup() + .addGap(23, 23, 23) + .addComponent(usernameLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(usernameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 92, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(7, 7, 7) + .addComponent(passwordLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(passwordField, javax.swing.GroupLayout.PREFERRED_SIZE, 99, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(streamUrlLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(streamUrlTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(streamTypeLabel) + .addComponent(streamTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(basicAuthCheckBox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(usernameLabel) + .addComponent(usernameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(passwordLabel) + .addComponent(passwordField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + private void basicAuthCheckBoxStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_basicAuthCheckBoxStateChanged + usernameLabel.setEnabled(basicAuthCheckBox.isSelected()); + usernameTextField.setEnabled(basicAuthCheckBox.isSelected()); + passwordLabel.setEnabled(basicAuthCheckBox.isSelected()); + passwordField.setEnabled(basicAuthCheckBox.isSelected()); +}//GEN-LAST:event_basicAuthCheckBoxStateChanged + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JCheckBox basicAuthCheckBox; + private javax.swing.JPasswordField passwordField; + private javax.swing.JLabel passwordLabel; + private javax.swing.JComboBox streamTypeComboBox; + private javax.swing.JLabel streamTypeLabel; + private javax.swing.JLabel streamUrlLabel; + private javax.swing.JTextField streamUrlTextField; + private javax.swing.JLabel usernameLabel; + private javax.swing.JTextField usernameTextField; + // End of variables declaration//GEN-END:variables + +} diff --git a/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingConnectionNode.java b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingConnectionNode.java new file mode 100644 index 0000000000..76e151f532 --- /dev/null +++ b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingConnectionNode.java @@ -0,0 +1,234 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.desktop.streaming; + +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.io.IOException; +import java.util.Timer; +import java.util.TimerTask; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.SwingUtilities; +import org.gephi.desktop.streaming.StreamingModel.ClientNode; +import org.gephi.streaming.api.StreamingConnection; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.util.Exceptions; +import org.openide.util.ImageUtilities; +import org.openide.util.Lookup; + +/** + * A Node representing the connection to the stream. + * + * @see Node + * + * @author panisson + */ +public class StreamingConnectionNode extends AbstractNode { + + private enum ConnectionState { + CONNECTED, + CLOSED, + ERROR, + RECEIVING + } + + private static Image connectedImage = ImageUtilities.loadImage("org/gephi/desktop/streaming/resources/dot_connected.png", true); + private static Image closedImage = ImageUtilities.loadImage("org/gephi/desktop/streaming/resources/dot_disconnected.png", true); + private static Image sendrecImage = ImageUtilities.loadImage("org/gephi/desktop/streaming/resources/dot_sendrec.png", true); + private static Image errorImage = ImageUtilities.loadImage("org/gephi/desktop/streaming/resources/dot_error.png", true); + + private Action closeConnectionAction; + private Action showReportAction; + private Action removeFromViewAction; + private Action synchronizeAction; + private ConnectionState state; + private Timer timer; + private final StreamingConnection connection; + + /** + * Create a Node from the given connection. + * + * @param connection - the connection to take the information + */ + public StreamingConnectionNode(final StreamingConnection connection) { + super(Children.LEAF); + this.connection = connection; + timer = new Timer(); + setDisplayName(connection.getStreamingEndpoint().getUrl().toString()); + connection.addStatusListener( + new StreamingConnection.StatusListener() { + + public void onConnectionClosed(StreamingConnection connection) { + setState(ConnectionState.CLOSED); + } + + public void onDataReceived(StreamingConnection connection) { + setState(ConnectionState.RECEIVING); + } + + public void onError(StreamingConnection connection) { + setState(ConnectionState.ERROR); + } + }); + + closeConnectionAction = new AbstractAction("Close connection") { + + public void actionPerformed(ActionEvent e) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + closeConnection(); + } + }); + } + }; + + showReportAction = new AbstractAction("Show Report") { + + public void actionPerformed(ActionEvent e) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + StreamingUIController controller = Lookup.getDefault().lookup(StreamingUIController.class); + controller.showReport(connection); + } + }); + } + }; + + removeFromViewAction = new AbstractAction("Remove from view") { + + public void actionPerformed(ActionEvent e) { + ClientNode parent = (ClientNode)getParentNode(); + parent.removeConnectionNode(StreamingConnectionNode.this); + } + }; + + synchronizeAction = new AbstractAction("Synchronize") { + public void actionPerformed(ActionEvent e) { + ClientNode parent = (ClientNode)getParentNode(); + parent.removeConnectionNode(StreamingConnectionNode.this); + StreamingUIController controller = Lookup.getDefault().lookup(StreamingUIController.class); + controller.synchronize(connection); + } + }; + + if (!connection.isClosed()) { + state = ConnectionState.CONNECTED; + } else { + state = ConnectionState.CLOSED; + } + } + + /** + * Close the underlying connection and put the state in CLOSED. + */ + public void closeConnection() { + if (state!=ConnectionState.CLOSED) { + try { + connection.close(); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + setState(ConnectionState.CLOSED); + } + } + + @Override + public Image getIcon(int type) { + switch (state) { + case CONNECTED: return connectedImage; + case RECEIVING: return sendrecImage; + case ERROR: return errorImage; + case CLOSED: return closedImage; + default: return closedImage; + } + } + + @Override + public Image getOpenedIcon(int i) { + return getIcon(i); + } + + @Override + public Action[] getActions(boolean popup) { + if (state != ConnectionState.CLOSED) { + return new Action[]{synchronizeAction, closeConnectionAction, showReportAction}; + } else { + return new Action[]{showReportAction, removeFromViewAction}; + } + } + + /** + * Sets the state of this node. + * @param newstate the new state + */ + private void setState(ConnectionState newstate) { + if (newstate == state) return; + if (state == ConnectionState.CLOSED) return; + + switch (newstate) { + case CONNECTED: + case ERROR: + state = newstate; + fireIconChange(); + break; + case CLOSED: + state = newstate; + fireIconChange(); + timer.cancel(); + timer = null; + break; + case RECEIVING: + state = newstate; + fireIconChange(); + timer.schedule(new TimerTask() { + @Override + public void run() { + setState(ConnectionState.CONNECTED); + } + }, 100); + break; + } + } +} diff --git a/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingModel.java b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingModel.java new file mode 100644 index 0000000000..dfea9735df --- /dev/null +++ b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingModel.java @@ -0,0 +1,283 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.desktop.streaming; + +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.SwingUtilities; + +import org.gephi.streaming.api.StreamingConnection; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.util.ImageUtilities; +import org.openide.util.Lookup; + +/** + * Object used to group all information about the GUI state: The Client node, + * the Master node, the outgoing streaming connections and the ingoing server + * connections. + * + * @author panisson + * + */ +public class StreamingModel { + + private static final Logger logger = Logger.getLogger(StreamingModel.class.getName()); + + private Image clientImage = ImageUtilities.loadImage("org/gephi/desktop/streaming/resources/gephiclient.png", true); + private Image masterImage = ImageUtilities.loadImage("org/gephi/desktop/streaming/resources/gephimaster.png", true); + private Image masterconnImage = ImageUtilities.loadImage("org/gephi/desktop/streaming/resources/masterconnection.jpg", true); + + private boolean masterRunning; + private String serverContext; + + private MasterNode masterNode; + private Node clientNode; + + /** + * Create a StreamingModel object with information about the GUI state: + * The Client node, + * the Master node, the outgoing streaming connections and the ingoing server + * connections. + */ + public StreamingModel() { + clientNode = new ClientNode(); + masterNode = new MasterNode(); + } + + /** + * Add a StreamingConnection to the list of outgoing connections. + * @param connection + */ + public void addConnection(StreamingConnection connection) { + StreamingConnectionNode node = new StreamingConnectionNode(connection); + clientNode.getChildren().add(new Node[]{node}); + } + + /** + * Remove all outgoing connections + */ + public void removeAllConnections() { + for (Node node: clientNode.getChildren().getNodes()) { + StreamingConnectionNode connNode = (StreamingConnectionNode)node; + connNode.closeConnection(); + } + clientNode.getChildren().remove(clientNode.getChildren().getNodes()); + } + + private Map connectedMap = new HashMap(); + + /** + * Add an ingoing client connection to the master list + * @param client the client description to add + */ + public void addConnected(String client) { + Node node = new AbstractNode(Children.LEAF) { + @Override + public Action[] getActions(boolean popup) { + return new Action[]{}; + } + @Override + public Image getIcon(int type) { + return masterconnImage; + } + + @Override + public Image getOpenedIcon(int i) { + return getIcon(i); + } + }; + node.setDisplayName(client); + masterNode.getChildren().add(new Node[]{node}); + connectedMap.put(client, node); + } + + /** + * Remove an ingoing client connection to the master list + * @param client the client description to remove + */ + public void removeConnected(String client) { + Node node = connectedMap.remove(client); + if (node!=null) { + masterNode.getChildren().remove(new Node[]{node}); + } + } + + /** + * Used to verify if the master is running. + * @return true if the master is running, false otherwise + */ + public boolean isMasterRunning() { + return masterRunning; + } + + /** + * Used to set the information about the master is running. + * + * @param masterRunning true if the master is running, false otherwise + */ + public void setMasterRunning(boolean masterRunning) { + this.masterRunning = masterRunning; + } + + /** + * @return the serverContext + */ + public String getServerContext() { + return serverContext; + } + + /** + * @param serverContext the serverContext to set + */ + public void setServerContext(String serverContext) { + this.serverContext = serverContext; + } + + /** + * Used to get the node that represents the Client + * @return the Client node + */ + public Node getClientNode() { + return clientNode; + } + + /** + * Used to get the node that represents the Master + * @return the Master node + */ + public MasterNode getMasterNode() { + return masterNode; + } + + /** + * A class that represents the Client node information. + */ + public class ClientNode extends AbstractNode { + + private final Action addConnectionAction; + + public ClientNode() { + super(new Children.Array()); + setDisplayName("Client"); + + addConnectionAction = new AbstractAction("Connect to Stream") { + + public void actionPerformed(ActionEvent e) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + StreamingUIController controller = Lookup.getDefault().lookup(StreamingUIController.class); + controller.connectToStream(); + } + }); + } + }; + } + @Override + public Action[] getActions(boolean popup) { + return new Action[]{addConnectionAction}; + } + @Override + public Image getIcon(int type) { + return clientImage; + } + + @Override + public Image getOpenedIcon(int i) { + return getIcon(i); + } + @Override + public String getHtmlDisplayName() { + return "Client"; + } + + public void addConnectionNode(StreamingConnectionNode node) { + getChildren().add(new Node[]{node}); + } + + public void removeConnectionNode(StreamingConnectionNode node) { + getChildren().remove(new Node[]{node}); + } + } + + /** + * A class that represents the Master node information. + */ + public class MasterNode extends AbstractNode { + + StreamingServerNode streamingServerNode; + + public MasterNode() { + super(new Children.Array()); + setDisplayName("Master"); + streamingServerNode = new StreamingServerNode(); + this.getChildren().add(new Node[]{streamingServerNode}); + } + + public StreamingServerNode getStreamingServerNode() { + return streamingServerNode; + } + + @Override + public Action[] getActions(boolean popup) { + return new Action[]{}; + } + @Override + public Image getIcon(int type) { + return masterImage; + } + @Override + public Image getOpenedIcon(int i) { + return getIcon(i); + } + @Override + public String getHtmlDisplayName() { + return "Master"; + } + } +} diff --git a/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingServerNode.java b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingServerNode.java new file mode 100644 index 0000000000..2a58b978c0 --- /dev/null +++ b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingServerNode.java @@ -0,0 +1,139 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.desktop.streaming; + +import java.awt.Image; +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.SwingUtilities; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; +import org.openide.util.ImageUtilities; +import org.openide.util.Lookup; + +/** + * A Node that represents the Master Server and its state. + * + * @author panisson + */ +public class StreamingServerNode extends AbstractNode { + + private static Image connectedImage = ImageUtilities.loadImage("org/gephi/desktop/streaming/resources/dot_connected.png", true); + private static Image disconnectedImage = ImageUtilities.loadImage("org/gephi/desktop/streaming/resources/dot_disconnected.png", true); + + private boolean masterStarted; + private Action startMasterAction; + private Action stopMasterAction; + + public StreamingServerNode() { + super(Children.LEAF); + setDisplayName("Master Server"); + + masterStarted = false; + + startMasterAction = new AbstractAction("Start") { + + public void actionPerformed(ActionEvent e) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + StreamingUIController controller = + Lookup.getDefault().lookup(StreamingUIController.class); + controller.startMaster(); + } + }); + + } + }; + + stopMasterAction = new AbstractAction("Stop") { + + public void actionPerformed(ActionEvent e) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + StreamingUIController controller = + Lookup.getDefault().lookup(StreamingUIController.class); + controller.stopMaster(); + } + }); + + } + }; + } + + public void start() { + if (!masterStarted) { + masterStarted = true; + fireIconChange(); + } + } + + public void stop() { + if (masterStarted) { + masterStarted = false; + fireIconChange(); + } + } + + @Override + public Image getIcon(int type) { + if (masterStarted) { + return connectedImage; + } else { + return disconnectedImage; + } + } + + @Override + public Image getOpenedIcon(int i) { + return getIcon(i); + } + + @Override + public Action[] getActions(boolean popup) { + if (masterStarted) { + return new Action[]{stopMasterAction}; + } else { + return new Action[]{startMasterAction}; + } + } +} diff --git a/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingSettingsPanel.form b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingSettingsPanel.form new file mode 100644 index 0000000000..6d9cc7cf68 --- /dev/null +++ b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingSettingsPanel.form @@ -0,0 +1,195 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingSettingsPanel.java b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingSettingsPanel.java new file mode 100644 index 0000000000..2da710822d --- /dev/null +++ b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingSettingsPanel.java @@ -0,0 +1,266 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.desktop.streaming; + +import org.gephi.streaming.server.StreamingServerConfig; +import org.netbeans.validation.api.builtin.Validators; +import org.netbeans.validation.api.ui.ValidationGroup; +import org.netbeans.validation.api.ui.ValidationPanel; + +/** + * A JPanel implementation used to change the Streaming Settings. + * + * @author panisson + */ +public class StreamingSettingsPanel extends javax.swing.JPanel { + + private StreamingServerConfig settings; + + /** Creates new form StreamingSettingsPanel */ + public StreamingSettingsPanel(StreamingServerConfig settings) { + initComponents(); + this.settings = settings; + } + + public static ValidationPanel createValidationPanel(final StreamingSettingsPanel innerPanel) { + ValidationPanel validationPanel = new ValidationPanel(); + if (innerPanel == null) { + throw new NullPointerException(); + } + validationPanel.setInnerComponent(innerPanel); + + ValidationGroup group = validationPanel.getValidationGroup(); + group.add(innerPanel.portTextField, Validators.REQUIRE_VALID_INTEGER); + group.add(innerPanel.sslPortTextField, Validators.REQUIRE_VALID_INTEGER); + + return validationPanel; + } + + public void setup() { + int port = settings.getPort(); + this.portTextField.setText(port+""); + + // Basic Authentication + boolean filterEnabled = settings.isBasicAuthentication(); + basicAuthCheckBox.setSelected(filterEnabled); + usernameLabel.setEnabled(basicAuthCheckBox.isSelected()); + usernameTextField.setEnabled(basicAuthCheckBox.isSelected()); + passwordLabel.setEnabled(basicAuthCheckBox.isSelected()); + passwordField.setEnabled(basicAuthCheckBox.isSelected()); + usernameTextField.setText(settings.getUser()); + passwordField.setText(settings.getPassword()); + + // SSL + boolean sslEnabled = settings.isUseSSL(); + int sslPort = settings.getSslPort(); + sslCheckBox.setSelected(sslEnabled); + sslPortLabel.setEnabled(sslCheckBox.isSelected()); + sslPortTextField.setEnabled(sslCheckBox.isSelected()); + sslPortTextField.setText(sslPort+""); + } + + public void unsetup() { + int port = Integer.valueOf(portTextField.getText()); + settings.setPort(port); + + settings.setBasicAuthentication(basicAuthCheckBox.isSelected()); + settings.setUser(usernameTextField.getText()); + settings.setPassword(new String(passwordField.getPassword())); + + settings.setUseSSL(sslCheckBox.isSelected()); + int sslPort = Integer.valueOf(sslPortTextField.getText()); + settings.setSslPort(sslPort); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + httpServerPanel = new javax.swing.JPanel(); + portLabel = new javax.swing.JLabel(); + basicAuthCheckBox = new javax.swing.JCheckBox(); + portTextField = new javax.swing.JTextField(); + usernameLabel = new javax.swing.JLabel(); + usernameTextField = new javax.swing.JTextField(); + passwordLabel = new javax.swing.JLabel(); + passwordField = new javax.swing.JPasswordField(); + sslCheckBox = new javax.swing.JCheckBox(); + sslPortLabel = new javax.swing.JLabel(); + sslPortTextField = new javax.swing.JTextField(); + + httpServerPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(StreamingSettingsPanel.class, "StreamingSettingsPanel.httpServerPanel.border.title"))); // NOI18N + + portLabel.setText(org.openide.util.NbBundle.getMessage(StreamingSettingsPanel.class, "StreamingSettingsPanel.portLabel.text")); // NOI18N + + basicAuthCheckBox.setText(org.openide.util.NbBundle.getMessage(StreamingSettingsPanel.class, "StreamingSettingsPanel.basicAuthCheckBox.text")); // NOI18N + basicAuthCheckBox.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + basicAuthCheckBoxStateChanged(evt); + } + }); + + portTextField.setText(org.openide.util.NbBundle.getMessage(StreamingSettingsPanel.class, "StreamingSettingsPanel.portTextField.text")); // NOI18N + + usernameLabel.setText(org.openide.util.NbBundle.getMessage(StreamingSettingsPanel.class, "StreamingSettingsPanel.usernameLabel.text")); // NOI18N + + usernameTextField.setText(org.openide.util.NbBundle.getMessage(StreamingSettingsPanel.class, "StreamingSettingsPanel.usernameTextField.text")); // NOI18N + + passwordLabel.setText(org.openide.util.NbBundle.getMessage(StreamingSettingsPanel.class, "StreamingSettingsPanel.passwordLabel.text")); // NOI18N + + passwordField.setText(org.openide.util.NbBundle.getMessage(StreamingSettingsPanel.class, "StreamingSettingsPanel.passwordField.text")); // NOI18N + + sslCheckBox.setText(org.openide.util.NbBundle.getMessage(StreamingSettingsPanel.class, "StreamingSettingsPanel.sslCheckBox.text")); // NOI18N + sslCheckBox.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + sslCheckBoxStateChanged(evt); + } + }); + + sslPortLabel.setText(org.openide.util.NbBundle.getMessage(StreamingSettingsPanel.class, "StreamingSettingsPanel.sslPortLabel.text")); // NOI18N + + sslPortTextField.setText(org.openide.util.NbBundle.getMessage(StreamingSettingsPanel.class, "StreamingSettingsPanel.sslPortTextField.text")); // NOI18N + + javax.swing.GroupLayout httpServerPanelLayout = new javax.swing.GroupLayout(httpServerPanel); + httpServerPanel.setLayout(httpServerPanelLayout); + httpServerPanelLayout.setHorizontalGroup( + httpServerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(httpServerPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(httpServerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(httpServerPanelLayout.createSequentialGroup() + .addComponent(portLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(portTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 52, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(httpServerPanelLayout.createSequentialGroup() + .addComponent(sslCheckBox) + .addGap(39, 39, 39) + .addComponent(sslPortLabel) + .addGap(7, 7, 7) + .addComponent(sslPortTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 51, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(basicAuthCheckBox) + .addGroup(httpServerPanelLayout.createSequentialGroup() + .addGap(23, 23, 23) + .addComponent(usernameLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(usernameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 92, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(7, 7, 7) + .addComponent(passwordLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(passwordField, javax.swing.GroupLayout.PREFERRED_SIZE, 99, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap(79, Short.MAX_VALUE)) + ); + httpServerPanelLayout.setVerticalGroup( + httpServerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(httpServerPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(httpServerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(portLabel) + .addComponent(portTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addComponent(basicAuthCheckBox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(httpServerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(usernameLabel) + .addComponent(usernameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(passwordLabel) + .addComponent(passwordField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addGroup(httpServerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(sslCheckBox) + .addComponent(sslPortLabel) + .addComponent(sslPortTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(httpServerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(httpServerPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + httpServerPanel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(StreamingSettingsPanel.class, "StreamingSettingsPanel.jPanel1.AccessibleContext.accessibleName")); // NOI18N + }// //GEN-END:initComponents + + private void sslCheckBoxStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_sslCheckBoxStateChanged + sslPortLabel.setEnabled(sslCheckBox.isSelected()); + sslPortTextField.setEnabled(sslCheckBox.isSelected()); + }//GEN-LAST:event_sslCheckBoxStateChanged + + private void basicAuthCheckBoxStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_basicAuthCheckBoxStateChanged + usernameLabel.setEnabled(basicAuthCheckBox.isSelected()); + usernameTextField.setEnabled(basicAuthCheckBox.isSelected()); + passwordLabel.setEnabled(basicAuthCheckBox.isSelected()); + passwordField.setEnabled(basicAuthCheckBox.isSelected()); + }//GEN-LAST:event_basicAuthCheckBoxStateChanged + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JCheckBox basicAuthCheckBox; + private javax.swing.JPanel httpServerPanel; + private javax.swing.JPasswordField passwordField; + private javax.swing.JLabel passwordLabel; + private javax.swing.JLabel portLabel; + private javax.swing.JTextField portTextField; + private javax.swing.JCheckBox sslCheckBox; + private javax.swing.JLabel sslPortLabel; + private javax.swing.JTextField sslPortTextField; + private javax.swing.JLabel usernameLabel; + private javax.swing.JTextField usernameTextField; + // End of variables declaration//GEN-END:variables + +} diff --git a/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingTopComponent.form b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingTopComponent.form new file mode 100644 index 0000000000..f79c357d4b --- /dev/null +++ b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingTopComponent.form @@ -0,0 +1,128 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingTopComponent.java b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingTopComponent.java new file mode 100644 index 0000000000..7a422ea1c6 --- /dev/null +++ b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingTopComponent.java @@ -0,0 +1,280 @@ +/* + Copyright 2008-2010 Gephi + Authors : Mathieu Bastian , Andre Panisson + Website : http://www.gephi.org + + This file is part of Gephi. + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + Copyright 2011 Gephi Consortium. All rights reserved. + + The contents of this file are subject to the terms of either the GNU + General Public License Version 3 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with the + License. You can obtain a copy of the License at + http://gephi.org/about/legal/license-notice/ + or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the + specific language governing permissions and limitations under the + License. When distributing the software, include this License Header + Notice in each file and include the License files at + /cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the + License Header, with the fields enclosed by brackets [] replaced by + your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" + + If you wish your version of this file to be governed by only the CDDL + or only the GPL Version 3, indicate your decision by adding + "[Contributor] elects to include this software in this distribution + under the [CDDL or GPL Version 3] license." If you do not indicate a + single choice of license, a recipient has the option to distribute + your version of this file under either the CDDL, the GPL Version 3 or + to extend the choice of license to its licensees as provided above. + However, if you add GPL Version 3 code and therefore, elected the GPL + Version 3 license, then the option applies only if the new code is + made subject to such option by the copyright holder. + + Contributor(s): + + Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.desktop.streaming; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.Action; +import javax.swing.SwingUtilities; +import org.netbeans.api.settings.ConvertAsProperties; +import org.openide.awt.ActionID; +import org.openide.awt.ActionReference; +import org.openide.explorer.ExplorerManager; +import org.openide.explorer.ExplorerUtils; +import org.openide.explorer.view.BeanTreeView; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.openide.windows.TopComponent; + +/** + * Top component which displays the Streaming component. + */ +@ConvertAsProperties(dtd = "-//org.gephi.desktop.streaming//Streaming//EN", +autostore = false) +@TopComponent.Description(preferredID = "StreamingTopComponent", +iconBase = "org/gephi/desktop/streaming/resources/media-stream.png", +persistenceType = TopComponent.PERSISTENCE_ALWAYS) +@TopComponent.Registration(mode = "layoutmode", openAtStartup = true, roles = {"overview"}) +@ActionID(category = "Window", id = "org.gephi.desktop.streaming.StreamingTopComponent") +@ActionReference(path = "Menu/Window", position = 1400) +@TopComponent.OpenActionRegistration(displayName = "#CTL_StreamingTopComponent", +preferredID = "StreamingTopComponent") +public final class StreamingTopComponent extends TopComponent implements ExplorerManager.Provider { + + private transient StreamingUIController controller; + private transient Children clientMasterChildren; + private transient StreamingTreeView tree; + private transient StreamingConnectionNode selectedNode = null; + + public StreamingTopComponent() { + + tree = new StreamingTreeView(); + + initComponents(); + setName(NbBundle.getMessage(StreamingTopComponent.class, "CTL_StreamingTopComponent")); + + clientMasterChildren = new Children.Array(); + + associateLookup(ExplorerUtils.createLookup(mgr, getActionMap())); + AbstractNode topnode = new AbstractNode(clientMasterChildren) { + + @Override + public Action[] getActions(boolean context) { + return new Action[0]; + } + }; + mgr.setRootContext(topnode); + + controller = Lookup.getDefault().lookup(StreamingUIController.class); + controller.setTopComponent(this); + } + + private class StreamingTreeView extends BeanTreeView { + + public StreamingTreeView() { + super(); + setRootVisible(false); + } + } + + public synchronized void refreshModel(StreamingModel model) { + Node clientNode = model.getClientNode(); + Node masterNode = model.getMasterNode(); + + clientMasterChildren.remove(clientMasterChildren.getNodes()); + clientMasterChildren.add(new Node[]{clientNode, masterNode}); + tree.expandNode(clientNode); + tree.expandNode(masterNode); + + mgr.addPropertyChangeListener(new PropertyChangeListener() { + + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { + Node[] selected = (Node[]) evt.getNewValue(); + if (selected != null && selected.length == 1 && selected[0] instanceof StreamingConnectionNode) { + removeButton.setEnabled(true); + selectedNode = (StreamingConnectionNode) selected[0]; + } else { + removeButton.setEnabled(false); + selectedNode = null; + } + } + } + }); + + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + topPanel = new javax.swing.JPanel(); + addButton = new javax.swing.JButton(); + removeButton = new javax.swing.JButton(); + settingsButton = new javax.swing.JButton(); + separator = new javax.swing.JSeparator(); + javax.swing.JScrollPane treeView = tree; + + setLayout(new java.awt.GridBagLayout()); + + topPanel.setLayout(new java.awt.GridBagLayout()); + + addButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/gephi/desktop/streaming/resources/plus.jpg"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(addButton, org.openide.util.NbBundle.getMessage(StreamingTopComponent.class, "StreamingTopComponent.addButton.text")); // NOI18N + addButton.setToolTipText(org.openide.util.NbBundle.getMessage(StreamingTopComponent.class, "StreamingTopComponent.addButton.toolTipText")); // NOI18N + addButton.setMaximumSize(new java.awt.Dimension(29, 29)); + addButton.setMinimumSize(new java.awt.Dimension(29, 29)); + addButton.setPreferredSize(new java.awt.Dimension(29, 29)); + addButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + addActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + topPanel.add(addButton, gridBagConstraints); + + removeButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/gephi/desktop/streaming/resources/minus.jpg"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(removeButton, org.openide.util.NbBundle.getMessage(StreamingTopComponent.class, "StreamingTopComponent.removeButton.text")); // NOI18N + removeButton.setToolTipText(org.openide.util.NbBundle.getMessage(StreamingTopComponent.class, "StreamingTopComponent.removeButton.toolTipText")); // NOI18N + removeButton.setEnabled(false); + removeButton.setMaximumSize(new java.awt.Dimension(29, 29)); + removeButton.setMinimumSize(new java.awt.Dimension(29, 29)); + removeButton.setPreferredSize(new java.awt.Dimension(29, 29)); + removeButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + removeButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + topPanel.add(removeButton, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(settingsButton, org.openide.util.NbBundle.getMessage(StreamingTopComponent.class, "StreamingTopComponent.settingsButton.text")); // NOI18N + settingsButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + settingsActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 3; + gridBagConstraints.gridy = 0; + topPanel.add(settingsButton, gridBagConstraints); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 0; + gridBagConstraints.weightx = 1.0; + topPanel.add(separator, gridBagConstraints); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.ipadx = 5; + gridBagConstraints.ipady = 5; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; + gridBagConstraints.weightx = 1.0; + add(topPanel, gridBagConstraints); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + add(treeView, gridBagConstraints); + }// //GEN-END:initComponents + + private void settingsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_settingsActionPerformed + SwingUtilities.invokeLater(new Runnable() { + + public void run() { + controller.setSettings(); + } + }); + + }//GEN-LAST:event_settingsActionPerformed + + private void addActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addActionPerformed + SwingUtilities.invokeLater(new Runnable() { + + public void run() { + controller.connectToStream(); + } + }); + }//GEN-LAST:event_addActionPerformed + + private void removeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeButtonActionPerformed + SwingUtilities.invokeLater(new Runnable() { + + public void run() { + if (selectedNode != null) { + selectedNode.closeConnection(); + selectedNode.getParentNode().getChildren().remove(new Node[]{selectedNode}); + } + } + }); + }//GEN-LAST:event_removeButtonActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton addButton; + private javax.swing.JButton removeButton; + private javax.swing.JSeparator separator; + private javax.swing.JButton settingsButton; + private javax.swing.JPanel topPanel; + // End of variables declaration//GEN-END:variables + private final ExplorerManager mgr = new ExplorerManager(); + + public ExplorerManager getExplorerManager() { + return mgr; + } + + void writeProperties(java.util.Properties p) { + // better to version settings since initial version as advocated at + // http://wiki.apidesign.org/wiki/PropertyFiles + p.setProperty("version", "1.0"); + // TODO store your settings + } + + void readProperties(java.util.Properties p) { + String version = p.getProperty("version"); + // TODO read your settings according to their version + } +} diff --git a/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingUIController.java b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingUIController.java new file mode 100644 index 0000000000..df033129db --- /dev/null +++ b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingUIController.java @@ -0,0 +1,396 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.desktop.streaming; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphController; +import org.gephi.graph.api.GraphModel; +import org.gephi.project.api.Project; +import org.gephi.project.api.ProjectController; +import org.gephi.project.api.Workspace; +import org.gephi.project.api.WorkspaceInformation; +import org.gephi.project.api.WorkspaceListener; +import org.gephi.streaming.api.Report; +import org.gephi.streaming.api.StreamingEndpoint; +import org.gephi.streaming.api.StreamingConnection; +import org.gephi.streaming.api.StreamingController; +import org.gephi.streaming.server.ClientManager.ClientManagerListener; +import org.gephi.streaming.server.ServerController; +import org.gephi.streaming.server.ServerControllerFactory; +import org.gephi.streaming.server.StreamingServer; +import org.netbeans.validation.api.ui.ValidationPanel; +import org.openide.DialogDescriptor; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.openide.util.lookup.ServiceProvider; + +/** + * The UI controller to control the UI state. + * + * @author panisson + * + */ +@ServiceProvider(service = StreamingUIController.class) +public class StreamingUIController { + + private static final Logger logger = Logger.getLogger(StreamingUIController.class.getName()); + + private StreamingModel model; + private StreamingTopComponent component; + + public StreamingUIController() { + + //Workspace events + ProjectController pc = Lookup.getDefault().lookup(ProjectController.class); + pc.addWorkspaceListener(new WorkspaceListener() { + + public void initialize(Workspace workspace) { + model = new StreamingModel(); + workspace.add(model); + } + + public void select(Workspace workspace) { + model = workspace.getLookup().lookup(StreamingModel.class); + if (model == null) { + model = new StreamingModel(); + workspace.add(model); + } + refreshModel(); + } + + public void unselect(Workspace workspace) { + } + + public void close(Workspace workspace) { + model = workspace.getLookup().lookup(StreamingModel.class); + if (model != null) { + if (model.isMasterRunning()) { + stopMaster(); + } + model.removeAllConnections(); + refreshModel(); + } + } + + public void disable() { + model = null; + } + }); + + if (pc.getCurrentWorkspace() != null) { + model = pc.getCurrentWorkspace().getLookup().lookup(StreamingModel.class); + if (model == null) { + model = new StreamingModel(); + pc.getCurrentWorkspace().add(model); + } + } + } + + public void setTopComponent(StreamingTopComponent component) { + this.component = component; + refreshModel(); + } + + public void refreshModel() { + if (model != null) { + component.refreshModel(model); + } + } + + public StreamingModel getStreamingModel() { + return model; + } + + /** + * Shows the StreamingClientPanel and connect to a StreamingEndpoint using + * the entered info. + */ + public void connectToStream() { + StreamingClientPanel clientPanel = new StreamingClientPanel(); + + ValidationPanel vp = StreamingClientPanel.createValidationPanel(clientPanel); + final DialogDescriptor dd = new DialogDescriptor(vp, "Connect to Stream"); + vp.addChangeListener(new ChangeListener() { + + public void stateChanged(ChangeEvent e) { + dd.setValid(!((ValidationPanel) e.getSource()).isProblem()); + } + }); + + Object result = DialogDisplayer.getDefault().notify(dd); + if (!result.equals(NotifyDescriptor.OK_OPTION)) { + return; + } + StreamingEndpoint endpoint = clientPanel.getGraphStreamingEndpoint(); + connectToStream(endpoint); + } + + /** + * Synchronize with the Master: disconnect from it, clean the graph and + * reconnect. + * + * @param connection + */ + public void synchronize(StreamingConnection connection) { + + // Get active graph instance - Project and Graph API + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + Project project = projectController.getCurrentProject(); + if (project==null) + projectController.newProject(); + Workspace workspace = projectController.getCurrentWorkspace(); + if (workspace==null) + workspace = projectController.newWorkspace(projectController.getCurrentProject()); +// projectController.openWorkspace(workspace); + + GraphController graphController = Lookup.getDefault().lookup(GraphController.class); + GraphModel graphModel = graphController.getGraphModel(); + Graph graph = graphModel.getGraph(); + graph.clear(); + + try { + connection.close(); + + // Connect to stream - Streaming API + StreamingController controller = Lookup.getDefault().lookup(StreamingController.class); + + connection = controller.connect(connection.getStreamingEndpoint(), graph); + connection.asynchProcess(); + + model.addConnection(connection); + + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + + /** + * Show the StreamingSettingsPanel and update the settings with the + * entered info. + */ + public void setSettings() { + StreamingServer server = Lookup.getDefault().lookup(StreamingServer.class); + + StreamingSettingsPanel settingsPanel = new StreamingSettingsPanel(server.getServerSettings()); + settingsPanel.setup(); + ValidationPanel vp = StreamingSettingsPanel.createValidationPanel(settingsPanel); + + final DialogDescriptor dd = new DialogDescriptor(vp, "Settings"); + vp.addChangeListener(new ChangeListener() { + + public void stateChanged(ChangeEvent e) { + dd.setValid(!((ValidationPanel) e.getSource()).isProblem()); + } + }); + Object result = DialogDisplayer.getDefault().notify(dd); + if (!result.equals(NotifyDescriptor.OK_OPTION)) { + return; + } + settingsPanel.unsetup(); + + if (server.isStarted()) { + // TODO: show confirmation dialog before stop/start + try { + server.stop(); + server.start(); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + } + + /** + * Start the master associated with the current workspace. Uses the + * workspace name as the server context. + */ + public void startMaster() { + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + Project project = projectController.getCurrentProject(); + if (project==null) { + //TODO: Invalid project + return; + } + + Workspace workspace = projectController.getCurrentWorkspace(); + if (workspace==null) { + //TODO: Invalid workspace + return; + } + + GraphController graphController = Lookup.getDefault().lookup(GraphController.class); + Graph graph = graphController.getGraphModel().getGraph(); + + WorkspaceInformation wi = workspace.getLookup().lookup(WorkspaceInformation.class); + String context = "/"+wi.getName().replaceAll(" ", "").toLowerCase(); + + StreamingServer server = Lookup.getDefault().lookup(StreamingServer.class); + ServerControllerFactory controllerFactory = Lookup.getDefault().lookup(ServerControllerFactory.class); + ServerController serverController = controllerFactory.createServerController(graph); + + serverController.getClientManager().addClientManagerListener( + new ClientManagerListener() { + + public void clientConnected(String client) { + model.addConnected(client); + } + + public void clientDisconnected(String client) { + model.removeConnected(client); + } + }); + + server.register(serverController, context); + + model.setServerContext(context); + model.getMasterNode().getStreamingServerNode().start(); + model.setMasterRunning(true); + } + + /** + * Stop the master associated with the current workspace and + * unregister its context from the server. + */ + public void stopMaster() { + model.setMasterRunning(false); + StreamingServer server = Lookup.getDefault().lookup(StreamingServer.class); + server.unregister(model.getServerContext()); + model.getMasterNode().getStreamingServerNode().stop(); + } + + private void notifyError(String userMessage, Throwable t) { + if (t instanceof OutOfMemoryError) { + return; + } + String message = message = t.toString(); + NotifyDescriptor.Message msg = + new NotifyDescriptor.Message( + userMessage+"\n"+message, + NotifyDescriptor.WARNING_MESSAGE); + DialogDisplayer.getDefault().notify(msg); + //Logger.getLogger("").log(Level.WARNING, "", t.getCause()); + } + + /** + * Show the ReportPanel for the connection's Report. The ReportPanel + * is updated with the Report information, and when some activity occurs in + * the connection, the panel is updated. + * + * @param connection the StreamingConnection to get the Report. + */ + public void showReport(final StreamingConnection connection) { + final Report report = connection.getReport(); + final ReportPanel reportPanel = new ReportPanel(); + reportPanel.setData(report); + + final StreamingConnection.StatusListener listener = new StreamingConnection.StatusListener() { + + public void onConnectionClosed(StreamingConnection connection) { + reportPanel.refreshData(report); + } + + public void onDataReceived(StreamingConnection connection) { + reportPanel.refreshData(report); + } + + public void onError(StreamingConnection connection) { + reportPanel.refreshData(report); + } + }; + + connection.addStatusListener(listener); + + DialogDescriptor dd = + new DialogDescriptor(reportPanel, + NbBundle.getMessage(StreamingUIController.class, "ReportPanel.title"), + false, DialogDescriptor.OK_CANCEL_OPTION, null, new ActionListener() { + + public void actionPerformed(ActionEvent e) { + connection.removeStatusListener(listener); + } + }); + + DialogDisplayer.getDefault().notify(dd); + + } + + private void connectToStream(StreamingEndpoint endpoint) { + + // Get active graph instance - Project and Graph API + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + Project project = projectController.getCurrentProject(); + if (project==null) + projectController.newProject(); + Workspace workspace = projectController.getCurrentWorkspace(); + if (workspace==null) + workspace = projectController.newWorkspace(projectController.getCurrentProject()); +// projectController.openWorkspace(workspace); + + GraphController graphController = Lookup.getDefault().lookup(GraphController.class); + GraphModel graphModel = graphController.getGraphModel(); + Graph graph = graphModel.getGraph(); + + // Connect to stream - Streaming API + StreamingController controller = Lookup.getDefault().lookup(StreamingController.class); + try { + StreamingConnection connection = controller.connect(endpoint, graph); + connection.asynchProcess(); + + model.addConnection(connection); + + } catch (IOException ex) { + notifyError("Unable to connect to stream " + endpoint.getUrl().toString(), ex); + logger.log(Level.WARNING, "Unable to connect to stream", ex); + return; + } + } +} diff --git a/modules/DesktopStreaming/src/main/nbm/manifest.mf b/modules/DesktopStreaming/src/main/nbm/manifest.mf new file mode 100644 index 0000000000..bfeb5f83c1 --- /dev/null +++ b/modules/DesktopStreaming/src/main/nbm/manifest.mf @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +OpenIDE-Module-Localizing-Bundle: org/gephi/desktop/streaming/Bundle.properties diff --git a/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/Bundle.properties b/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/Bundle.properties new file mode 100644 index 0000000000..d8e71e158c --- /dev/null +++ b/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/Bundle.properties @@ -0,0 +1,51 @@ +CTL_ConnectToStream=Connect To Stream +CTL_StreamingAction=Streaming +CTL_StreamingTopComponent=Streaming +HINT_StreamingTopComponent=Streaming +OpenIDE-Module-Display-Category=Gephi UI +OpenIDE-Module-Long-Description=\ + Desktop Streaming +OpenIDE-Module-Name=Desktop Streaming +OpenIDE-Module-Short-Description=Desktop Streaming +StreamingEndpointPanel.ui.dialog.title=Connect to Stream +StreamingServerPanel.configureButton.text=Configure... +StreamingServerPanel.startStopButton.text=Start +StreamingServerPanel.startStopButton.toolTipText=Expose the Current Workspace as Stream +StreamingServerPanel.configureButton.toolTipText=Configure the Stream Server +StreamingServerPanel.streamingUrlTextPane.contentType=text/html +StreamingTopComponent1.clientLabel.text=Client +StreamingTopComponent1.serverLabel.text=Server +StreamingTopComponent.addButton.text= +StreamingTopComponent.removeButton.text= +StreamingTopComponent.settingsButton.text=Settings... +StreamingTopComponent.addButton.toolTipText=Connect to Stream +StreamingTopComponent.removeButton.toolTipText=Remove from client list +StreamingSettingsPanel.jPanel1.AccessibleContext.accessibleName=Server Settings +StreamingSettingsPanel.httpServerPanel.border.title=HTTP Server Settings +StreamingSettingsPanel.portLabel.text=Port: +StreamingSettingsPanel.portTextField.text=8080 +StreamingSettingsPanel.basicAuthCheckBox.text=Use Basic Authentication +StreamingSettingsPanel.usernameLabel.text=Username: +StreamingSettingsPanel.usernameTextField.text= +StreamingSettingsPanel.passwordLabel.text=Password: +StreamingSettingsPanel.passwordField.text= +StreamingSettingsPanel.sslCheckBox.text=Use SSL +StreamingSettingsPanel.sslPortLabel.text=SSL Port: +StreamingSettingsPanel.sslPortTextField.text=8443 + +ReportPanel.title = Streaming report +ReportPanel.labelSrc.text=Source: +ReportPanel.sourceLabel.text= +ReportPanel.tab1ScrollPane.TabConstraints.tabTitle=Issues +ReportPanel.tab2ScrollPane.TabConstraints.tabTitle=Report +StreamingClientPanel.streamTypeLabel.text=Stream Type: +StreamingClientPanel.streamUrlLabel.text=Source URL: +StreamingClientPanel.basicAuthCheckBox.text=Use Basic Authentication +StreamingClientPanel.passwordLabel.text=Password: +StreamingClientPanel.passwordField.text= +StreamingClientPanel.usernameTextField.text= +StreamingClientPanel.usernameLabel.text=Username: +StreamingClientPanel.sourceURL.text= +ReportPanel.jLabel1.text= +ReportPanel.labelEventCount.text=# of Events: +ReportPanel.eventCountLabel.text= diff --git a/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/critical.png b/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/critical.png new file mode 100644 index 0000000000000000000000000000000000000000..3bbbb4a0d3dc2f6bfb653bd25e80169e96c6c833 GIT binary patch literal 653 zcmV;80&@L{P)&6kU&^O z2QZQrnrLJAuq31{O{kZd49Zz&wx!#=YkfunsI` zVC#CnxgmZ#911N=CX)oiVSEDvs4AVaGtSFpiiHBzW;2_C#oquqcsHF)z8smE;c|N$ z=kydD2USH3SVMR5Pfb$Y-Q`oUxSD}i0KTsCbTaw+UMxm?a}#YeKm=7mML`5$&hffQ81ak$9mc{~;YnD+zeVE8^iK72$)87qJ1PqlB~L8>(Z z4<7-f{lK`@KZuGO*xyP-QSD%Q0s!L{aFzDI0Py-$1pq%#>eSCB6oab6myr>-LBzdL zH#|30MBN~@*#an8!20>g3C13}BL5=pI*KP&!5SQ-U8@1CTfox(-X4K?+$GC(kvBT+ zPD4;*@JAw)4i5pAtPE_`TCHrQkf&$rsSCO0ig&9h7(-7qM&sasdb^#?z?KhS8CITG zDzmoF7ad3>a2gHdqJ8xf6)<*)-uMKKa+$*M@oEO%+`3G5U*E!$(NO}njp^-0MR1x; nI`ukVkB+*|i??p$|E%**!bfHvxmb3000000NkvXXu0mjfWCIe6 literal 0 HcmV?d00001 diff --git a/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/dot_connected.png b/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/dot_connected.png new file mode 100644 index 0000000000000000000000000000000000000000..b317dec1bc870067b07c6f772414285767eec2e1 GIT binary patch literal 269 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0WW zg+Z8+Vb&Z8pdfpRr>`sfEp}l+PK)fD)gU3s64!_l=ltB<)VvY~=c3falGGH1^30M9 z1$R&1fbd2>aiF3qPZ!4!i_>o>Dhf3i@UV#M)O&oa;ZZ z57Q4-?g!j||A{Iz%67O}tO{paGSi!hc|*oa7O~gNq5XMJc@-~YOf6jrbOnQ_tDnm{ Hr-UW|NI6x* literal 0 HcmV?d00001 diff --git a/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/dot_disconnected.png b/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/dot_disconnected.png new file mode 100644 index 0000000000000000000000000000000000000000..78a3263d98345d7fbb983139049879ae7ba588b0 GIT binary patch literal 265 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0WW zg+Z8+Vb&Z8pdfpRr>`sfEp}l+PMPpq^G^bWBuiW)N}Tg^b5rw57@Uhz6H8K46v{J8 zG8EiBeFMT9`NV;W$~;{hLo80ey||F~uz^U+!}X~%7v114?eaJ_bHR;C%U?JhWt_Qi z%M-=dnx2(>k?Hr>e?J$qEA`oIPEq}w7a9Kh+8a3PKS-<#n<7}`&$hxdqN3Eq_`sgc zpK91XOgD14UwQvLLzIY`=0;hjMNgYl*XW*8{e8^)4+Fot@TKSHBN~A&VDNPHb6Mw< G&;$UIQC%$n literal 0 HcmV?d00001 diff --git a/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/dot_error.png b/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/dot_error.png new file mode 100644 index 0000000000000000000000000000000000000000..4b96a923c26b55c970219a9f1be221e10a63fea6 GIT binary patch literal 284 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgg4b`f4~k;iw$uKwc6x=<11Hv2m#DR*+JzX3_EKa|@xRLjefk?|k{SqI?x4IG< zld{zoYQL70(Y)*K0-AbW|YuPgg4b`c&4k>-y!g+L+664!_l=ltB< z)VvY~=c3falGGH1^30M91$R&1fbd2>aiAhEPZ!4!i_^&o608;n>iyW-{)_VP{Qqxk z@TZ*g%L>S1@O1Ta JS?83{1OOFIE4csw literal 0 HcmV?d00001 diff --git a/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/gephiclient.png b/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/gephiclient.png new file mode 100644 index 0000000000000000000000000000000000000000..71893bf4e1d15cea43a1389c6d8bdea829167d49 GIT binary patch literal 927 zcmV;Q17Q4#P)Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igY~ z4mB5(Un{`?00SRML_t(I%XQP+Ynufa$MNr*^P6Nzmn?0Uv`ulW>7ZRZw6%ys*HuyJ zqyvYDs5hR#J7G+ndgG0HA)Q{;1Bl4Pc1*<~iif&`FwM5MS(;Uwq^G2L)4Vx1Y5L~m zU0qOrf53<5;dy=nw?!`3-(HSMr~L8~iDCwaqlNBP4?|Y7!2d)5NY<6-eUZ?_%v^{% z7S^?E%;wLMuNlcL4)f6dW4!!mEA6^Cg38r^sQ~_vzMuGL`gMxCqx9W3qlr%{!W|^U`le8J1p(>=vsV?&4 zZ@+W)hZz!eW%71FGxg09i;^32-zHZ>=UJNfBIx?DyIvv`)1tBlP~>lE77cv;*~dIS z*mI%*nEz8CS?;E)6tD>~s)7coJj$g@C2DFBVLgKmD`FLrsR|3LO2Z18LM=hQU_ll0 zkk4)*Md~PSHCn6~cV$@gPh!x;DV6u37}Nxm*+#&q*HFzu^m;oTb{7V9m=*6^bUKd_ z-B^Qa9hY^q3MGmTXrAhn{Q)6vUoZ+t>+t3Xj| zArcC*&tPf1!tvo=Y!wN0I!vuppsG|!hf3(TH8dHW%-zUi&~-6*r;7)V4iMi6lFsGt z*a7754er^SrQNWFS`JWI@gcT9&9-@j*jkFsnuD}p{FsadvgtJUAAW*}l=$bBZ~A<7t!P9_ zXPG?r73Q`1)zziQmN>2 z7}s7qqCq4l$4~hEe$X3GRCY9C3!i@ZD+ZfFe(N$0o0E|ThUsXs2|I1P=JnPi(FkK> z$A$4TXX126oEP)Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igY~ z4mS(6XcY_q00U4-L_t(I%bn9(Y*Tdr$MNs!>1n%_m2I%qwR0C0$uV zZ8XFQi7~!#@qvgYge4A%VnR@3B>E!JIK|+U!4o)jgG~$@g6@{MmF?_eyN+Jg+q!nA z*S}9D7=7~d^vmzz_x6MP2ocl^Ax`)c+W6J};nWX(x3!LUKN(L5aU!wwrJnyF{T)IP z|2Ea-8}9tzLxWJv*fu{wB9TL#h!XJ3vd^(2_5ShwPfL$+xG|IxQlO(AECJ3f!XdL;0uWI^89W0=A4FHHZuSq3n31)z4Xf7B~_I^>pf4U z%}hM>Cu<2Wr&`~j<@`sae*AoVtN7-Uy}*lrff?q+KOfYh5+=Ho3}*n{GKejaJGlKU5_KOIlLY( z&Ra8-?tB~Hy&$=QO~ltmk)so&R_sLPZsve##b|2RwKX@7Ie(iIrqUK-;X<_NB64v) z^41+V{evt7hFJXNJ#c=C{h5wRFPNM#Jgn3`!VD*N)QD8s=(9|o4wNv)gzLou4V6r-u^{_Zbd&5qTF?Q>#iwVey zu``GZbA!cSzWvJZ=+#e#i#K0=`1|jF>HSU7b3oz%0R+~l=({*sh>v5AorydHHzymz z^H(1j=GHlYH6}M5V2~6OVEFp|7sH`54;kKl`jK*fbL?Dz0Af<`p0CWv@VCc6OOoNk zm%j|R9)D(d@$LuQ`5-n(3?v6q15yjp0}wzg|Ns3>P>~lC`11Wf(9|CcU%!3@YjDii z0@uvQ{DfPfnRgm80nGralmli|mY9}G^$VhqihW^hdw zi5nT1SpI_9-+%sP-~*}!=>Z5JrvLx{YJC6A$ng8mKL%zNHim7-o-)ML?T2dwi6di> zT96)q0Al+8_cz!XFaWas`uz_KxBy5kNDn{&F#)Z;@$<)b24Vq7El3YQ00A}rUGwAH z2L>i4CNTc~{X2uUk`P=Whz$~h$%E8_^Z*19(2su>ynXfT!{0wY88|sP!FW-lJzOJ* z4HE~c0jUM)0SF*)0+6t2O6KL`pChlN#lXtWh1H3_e|}?lc<&m+=T9F~UL2{L3lKo? zBqwfFm%_%%)-9u;Dj*@Pz#t$f0v7-9{td&6rw+*F(M~& mamyOKiTvg97I-2D2rvLLbP6IX&**~y0000C6s5G z&D1&iDn2BG>l+ul!gd`-^-cZ4YnMK;d$=LhFk)4~z5PXJ)G|dJP1B?EZNl6&To!M1 ze|4YtqU9UuhO~O08y_ddTrIfqxM*5g%hpHxu30U0+LGKnbHDtJP16Dvd_MlU`9b~v Fn*b%_bX))c literal 0 HcmV?d00001 diff --git a/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/media-stream.png b/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/media-stream.png new file mode 100644 index 0000000000000000000000000000000000000000..0e17a5ca5e8b82482675481f0cef6cd8bbb03cd7 GIT binary patch literal 960 zcmV;x13&zUP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igV= z05}XBa!5n~00TftL_t(I%UzD$Yt?lefS=Fz_i|qLdZx6=a;7y{n>w7csCGsnf)`5S zg%^S07zx2vgtyTUVIkp74nn<9N{E8q9SCg(ZlEb`)iyBK5q0EpYiBR#_4oUo-}!XW zyy^J^p63zP8NYXiu5Bmye(If;TxsFxYGLkR5awDy%D`RV2jH{*9@ecaBCIAqC z+Yc_VXUkjs^yjH7nd0KUoImeoy#HM(&<$wT&}#@1If41!)PxBKukfX{|p5z)E3`pC5d}^!%4t zmWx-)vTehw`ZzN!sua}WqIwn_qjE9frwSj;T;!nJuYUB_+f@bd@Jpn-k@`HFRF@&-~|~FNas_G-2DK*oWa*+ z&vvZ%?EUok_PY499BIEyL-h^-iopukQ>SUH>7>4DJL$qb=2s`VvoMTnRUl%a>dl%U z%wLT;3EjEr2;WQ}=j-2l0f^c)y!6bwG}d(Dg;}1id4V`46kf`j~AQXM#PX>9A)b9SH$dUjImHvEtP#Kgy9URqz=)#|N>DCb+S@$PV}`~%=ub}e0AnH|pbB6a z7srm`no1lihHF(4b85B1sgszqVQg>nK+ovxNlahY1ZT%~(Vv{`28MwEz=(yx!7?_M ziQrgqT&psOx^=^QntHn@=00U_^8f~*KRLxy10yptGb1ZA zFI1Y5iJ66!O^|_I(U3z(*zuB5;AIg}F>xakvzAGdr!3qE)Fi_8bs zK?Wv97A7QFpm7W=tb#&{hK|A$7yiG+zyov%lOVGogFQpe{qKK6SI;Unlldv2e5m}O zf0Ka7Rt8R$m0F)I7?K+&pL|dl{^}Y2L4D7Tr_ee)_ik!>i1D5AQCx*SOZ~)^TyxEpBC@It#vO z?#ut(!GGjx{7l8qzT$-PdiBVC zY?t$FzKR@O?pn}aa)ejtoB4##zjyR~40K$+X93sdzF8V_4^;#Hy{zPx$+>21yIWgS z`bEWx?Rj$prEkZtaVR|&pR{DUWGVA4r%5dW3!fgmsu_Cr;FNuTy1SygzOUr?^4c-( zcWKbnBA->k3%8rj?B-jk`eb27dBmkqnCBc|1`UP*U_XR*j{x?ML{xGa3pPVc*6)0VI-4Vdfk zZ%g&p;)2JstL0~BUtQF@b;B!@?(XNant6|S>uPSB>D`zW7-qCo@BjlJgSzFX<3<{3 bKSlQaTKc5u$Hi2gvOkWA>-PQ)|9=wzDoh1Q literal 0 HcmV?d00001 diff --git a/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/plus.jpg b/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/plus.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a44de965f82caef5e6721d7a434e6757bd238bcb GIT binary patch literal 703 zcmex=kFazC+1X$SEnOK1`<^l|iOsvcdY%Kgx zSw<#i1{PK}L3SZUVG%>e#)}-HV&clGPR_20Ny&vplO|7DcnPRcl97Rl8O2Bj7NALN zKq+}aMh0dEMiyqceIT`hLc)fDipmH7-(uhaYGe{*7G$tz*qrtCZPe216MdGP;S9T- zqnX9Y>ZShi4U>@V!#L)b%j>;r7Mv}p+*oTjsleIujQH}h*i(0py05YI{IO?+?WO6_ z%87oC$KHJJd-3Fb?$?{FpU!OGS-oKMgt^XpI8Lm!a{R6NsZClv@b$7)NA`OD+jhI( z?B<;bd?H(xTguP269`)wDXtp!e#YD-mz*cHE@9YWam!KB=7Wd*7607O<+nIZ-bo7Q z@ZI0%687xJyYQ8dC*NtgH0|-sy>+FTS7ttW-Z16kLs?zjE8SavuWNRixk~x!?8|z~ ze(g?vaEODsK&Ff@S#Dy8SIgEXZzF@wYXzlp-gqP#G|5ADc00r0Kpl<9pPl`jr9YkL zr$w!N|IMu9kI?-!Rg+6EF(LDA~gr4BO?Fb1OPPS?ri`7 literal 0 HcmV?d00001 diff --git a/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/severe.png b/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/severe.png new file mode 100644 index 0000000000000000000000000000000000000000..e6bafc0fb33815e232ce1319ff86851257832efe GIT binary patch literal 723 zcmV;^0xbQBP)004R=004l4008;_004mK004C`008P>0026d000+nm#LZ50007B zNkl*6RSkMLXB7A~?lot?N1Fs~a(Z5^YzVrdr^Z*Td2{Qm7fRJ5K z;;pG$;;v)Nz`(@F!0_h}1H;ds44kYS3_9Y{3|h==Oe-JXEjj+}Ge~VIOcOu=F~OXz zBOz7fu5ZG?AS%MZU}w+3@a-D|!Lw+zY1cfj#Nk}n-atkpiFtHRty$J>Y0mKBQ zy<8O4nHZRu85p#67#M_shPZh!Fns(3)EvyfAT72L^^8 zKY^|Q`a2<+fk9pYqFGUqfuXP%5~zRwFnsv;mjN2JU;q$6FawU>`S2R5IRT>i-aVkr zoj|2`fc60`tf*pO`1haT*8e{Y&~yO?009IuVB5YIPyhY>0(Ab$l|Y+;n%i3$7`_8z zpt+HO;r49?hIw-segkbk^6&RQkb0N_00D%Ykh(27c}hY#`50J!{suc2WFrGq{`dcX z49oxhU^w&dHz*;&(*ZyLAt$7s$TASS{}@B@GV0{~pV(E4hGkgWg!002ovPDHLk FV1h;|DI)*? literal 0 HcmV?d00001 diff --git a/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/warning.gif b/modules/DesktopStreaming/src/main/resources/org/gephi/desktop/streaming/resources/warning.gif new file mode 100644 index 0000000000000000000000000000000000000000..518bc52784df4c95749bf2cbc125abf27d2be333 GIT binary patch literal 585 zcmV-P0=E5$P)HPWg%;@R>0tlz=M`nor|M+z8|Noya{r~Z3&i~cj5&u8FdBTsb4j_QA zy5Q7O;}#}X9)?ek9x&`)HjUx+Q$_}9SxJVITc>8Cs|5%koPPiM>cs!meaZiAbw&P9 zs|^E(#j1`ljIaO*ASO(|`}PTmYcp{1iZcj_NrS~y6lB1(v=j%!k#(JIyUy)qgsBGz zAPfUOy*O2&$bH;zEL?0J9lgY+Wrrs@auI3Ww~Drhd0kG0dWBWh!K_-CY5^p&x`tl z5lj27Su;3k9mNozw2XxzuX_h0Kmf6z3u1zk3|B6&;LHjD0mMjYZUhJ*CI$)tK!5=N X1rz+?Z_i|w00000NkvXXu0mjfZEN(( literal 0 HcmV?d00001 diff --git a/modules/GraphStreaming/pom.xml b/modules/GraphStreaming/pom.xml new file mode 100644 index 0000000000..c7be7a344d --- /dev/null +++ b/modules/GraphStreaming/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + gephi-plugin-parent + org.gephi + 0.9.0 + + + org.gephi + graphstreaming + 1.0.0 + nbm + + GraphStreaming + + + + + + + + + org.codehaus.mojo + nbm-maven-plugin + + Andre Panisson + $sourcecode_url + gephi.org + + + + + + + + diff --git a/modules/GraphStreaming/src/main/nbm/manifest.mf b/modules/GraphStreaming/src/main/nbm/manifest.mf new file mode 100644 index 0000000000..9c8f602036 --- /dev/null +++ b/modules/GraphStreaming/src/main/nbm/manifest.mf @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +OpenIDE-Module-Localizing-Bundle: org/gephi/graphstreaming/Bundle.properties diff --git a/modules/GraphStreaming/src/main/resources/org/gephi/graphstreaming/Bundle.properties b/modules/GraphStreaming/src/main/resources/org/gephi/graphstreaming/Bundle.properties new file mode 100644 index 0000000000..4b19337127 --- /dev/null +++ b/modules/GraphStreaming/src/main/resources/org/gephi/graphstreaming/Bundle.properties @@ -0,0 +1,8 @@ +OpenIDE-Module-Display-Category=Plugin +OpenIDE-Module-Long-Description=\ + The purpose of the Graph Streaming API and Plugins is to build a unified framework for streaming graph objects. \ + Gephi\u2019s data structure and visualization engine has been built with the idea that a graph is not static and might change continuously. \ + By connecting Gephi with external data-sources, we leverage its power to visualize and monitor complex systems or enterprise data in real-time. \ + Moreover, the idea of streaming graph data goes beyond Gephi, and a unified and standardized API could bring interoperability with other available tools for graph and network analysis, as they could start to interoperate with other tools in a distributed and cooperative fashion. +OpenIDE-Module-Name=Graph Streaming +OpenIDE-Module-Short-Description=Graph Streaming Plugin diff --git a/modules/JettyWrapper/pom.xml b/modules/JettyWrapper/pom.xml new file mode 100644 index 0000000000..c12e5ce053 --- /dev/null +++ b/modules/JettyWrapper/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + gephi-plugin-parent + org.gephi + 0.9.0 + + + org.gephi.lib + jetty + 1.0.0 + nbm + + JettyWrapper + + + + + + + + + org.codehaus.mojo + nbm-maven-plugin + + Unknown + $sourcecode_url + + + javax.servlet + javax.servlet.annotation + javax.servlet.descriptor + javax.servlet.http + org.eclipse.jetty.continuation + org.eclipse.jetty.http + org.eclipse.jetty.http.gzip + org.eclipse.jetty.http.ssl + org.eclipse.jetty.io + org.eclipse.jetty.io.bio + org.eclipse.jetty.io.nio + org.eclipse.jetty.security + org.eclipse.jetty.security.authentication + org.eclipse.jetty.server + org.eclipse.jetty.server.bio + org.eclipse.jetty.server.handler + org.eclipse.jetty.server.handler.jmx + org.eclipse.jetty.server.jmx + org.eclipse.jetty.server.nio + org.eclipse.jetty.server.session + org.eclipse.jetty.server.session.jmx + org.eclipse.jetty.server.ssl + org.eclipse.jetty.servlet + org.eclipse.jetty.servlet.jmx + org.eclipse.jetty.servlet.listener + org.eclipse.jetty.util + org.eclipse.jetty.util.ajax + org.eclipse.jetty.util.component + org.eclipse.jetty.util.log + org.eclipse.jetty.util.resource + org.eclipse.jetty.util.security + org.eclipse.jetty.util.ssl + org.eclipse.jetty.util.statistic + org.eclipse.jetty.util.thread + org.eclipse.jetty.websocket + + + + + + diff --git a/modules/JettyWrapper/src/main/nbm/manifest.mf b/modules/JettyWrapper/src/main/nbm/manifest.mf new file mode 100644 index 0000000000..213417b83d --- /dev/null +++ b/modules/JettyWrapper/src/main/nbm/manifest.mf @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +OpenIDE-Module-Localizing-Bundle: org/gephi/lib/jetty/Bundle.properties diff --git a/modules/JettyWrapper/src/main/resources/org/gephi/lib/jetty/Bundle.properties b/modules/JettyWrapper/src/main/resources/org/gephi/lib/jetty/Bundle.properties new file mode 100644 index 0000000000..576a5755b1 --- /dev/null +++ b/modules/JettyWrapper/src/main/resources/org/gephi/lib/jetty/Bundle.properties @@ -0,0 +1,2 @@ +OpenIDE-Module-Display-Category=Libraries +OpenIDE-Module-Name=Jetty Wrapper diff --git a/modules/StreamingAPI/pom.xml b/modules/StreamingAPI/pom.xml new file mode 100644 index 0000000000..51f0115255 --- /dev/null +++ b/modules/StreamingAPI/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + gephi-plugin-parent + org.gephi + 0.9.0 + + + org.gephi.streaming + api + 1.0.0 + nbm + + StreamingAPI + + + + + org.gephi + filters-api + + + org.gephi + graph-api + + + + + + + org.codehaus.mojo + nbm-maven-plugin + + Unknown + $sourcecode_url + + + org.gephi.streaming.api + org.gephi.streaming.api.event + + + + + + diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/CompositeGraphEventHandler.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/CompositeGraphEventHandler.java new file mode 100644 index 0000000000..38dedca377 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/CompositeGraphEventHandler.java @@ -0,0 +1,99 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.gephi.streaming.api.event.GraphEvent; + +/** + * A simple GraphEventHandler implementation that is able to + * loop through a list of GraphEventHandler and delegate the events + * to them. + * + * @author Andre' Panisson + * + */ +public class CompositeGraphEventHandler implements GraphEventHandler { + + /** + * the list of handlers that will receive the events + */ + private List handlers = new ArrayList(); + + /** + * Create a new event handler that loops through a list of handlers, + * sending the events to them. + * + * @param handlers + */ + public CompositeGraphEventHandler(GraphEventHandler ... handlers) { + this.handlers.addAll(Arrays.asList(handlers)); + } + + /** + * Add an GraphEventHandler to the list of handlers to delegate the events. + * + * @param handler the GraphEventHandler to add + */ + public void addHandler(GraphEventHandler handler) { + handlers.add(handler); + } + + /** + * Remove an GraphEventHandler from the list of handlers + * + * @param handler the GraphEventHandler to remove + */ + public void removeHandler(GraphEventHandler handler) { + handlers.remove(handler); + } + + @Override + public void handleGraphEvent(GraphEvent event) { + for (GraphEventHandler handler: handlers.toArray(new GraphEventHandler[0])) { + handler.handleGraphEvent(event); + } + } + +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Graph2EventListener.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Graph2EventListener.java new file mode 100644 index 0000000000..75f167612e --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Graph2EventListener.java @@ -0,0 +1,285 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ + +package org.gephi.streaming.api; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.gephi.data.attributes.api.AttributeEvent; +import org.gephi.data.attributes.api.AttributeListener; +import org.gephi.data.attributes.api.AttributeRow; +import org.gephi.data.attributes.api.AttributeValue; +import org.gephi.data.properties.PropertiesColumn; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.EdgeData; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphEvent; +import org.gephi.graph.api.GraphListener; +import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Node; +import org.gephi.graph.api.NodeData; +import org.gephi.streaming.api.event.ElementType; +import org.gephi.streaming.api.event.EventType; +import org.gephi.streaming.api.event.GraphEventBuilder; + +/** + * + * @author panisson + */ +public class Graph2EventListener implements GraphListener, AttributeListener { + + private GraphEventHandler eventHandler; + private GraphEventBuilder eventBuilder; + private Graph graph; + private boolean sendVizData = true; + + public Graph2EventListener(Graph graph, GraphEventHandler eventHandler) { + this.graph = graph; + eventBuilder = new GraphEventBuilder(this); + this.eventHandler = eventHandler; + } + + @Override + public void graphChanged(GraphEvent event) { + + switch (event.getEventType()) { + case ADD_NODES_AND_EDGES: + + if (event.getData().addedEdges() != null) + for (Edge edge: event.getData().addedEdges()) { + String edgeId = edge.getEdgeData().getId(); + org.gephi.streaming.api.event.GraphEvent e = + eventBuilder.edgeAddedEvent(edgeId, edge.getSource().getNodeData().getId(), + edge.getTarget().getNodeData().getId(), edge.isDirected(), + getEdgeAttributes(edge)); + eventHandler.handleGraphEvent(e); + } + + if (event.getData().addedNodes() != null) + for (Node node: event.getData().addedNodes()) { + String nodeId = node.getNodeData().getId(); + org.gephi.streaming.api.event.GraphEvent e = + eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, nodeId, + getNodeAttributes(node)); + eventHandler.handleGraphEvent(e); + } + break; + case MOVE_NODES: + + for (Node node: event.getData().movedNodes()) { + String nodeId = node.getNodeData().getId(); + org.gephi.streaming.api.event.GraphEvent e = + eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, nodeId, + getNodeAttributes(node)); + eventHandler.handleGraphEvent(e); + } + break; + + case REMOVE_NODES_AND_EDGES: + + if (event.getData().removedEdges() != null) + for (Edge edge: event.getData().removedEdges()) { + String edgeId = edge.getEdgeData().getId(); + org.gephi.streaming.api.event.GraphEvent e = + eventBuilder.graphEvent(ElementType.EDGE, EventType.REMOVE, edgeId, null); + eventHandler.handleGraphEvent(e); + } + + if (event.getData().removedNodes() != null) + for (Node node: event.getData().removedNodes()) { + String nodeId = node.getNodeData().getId(); + org.gephi.streaming.api.event.GraphEvent e = + eventBuilder.graphEvent(ElementType.NODE, EventType.REMOVE, nodeId, null); + eventHandler.handleGraphEvent(e); + } + break; + } + + } + + @Override + public void attributesChanged(AttributeEvent event) { + switch (event.getEventType()) { + case ADD_COLUMN: + break; + case REMOVE_COLUMN: + break; + case SET_VALUE: + + Map> nodeChangeTable = new HashMap>(); + Map> edgeChangeTable = new HashMap>(); + List graphChangeList = new ArrayList(); + + for (int i=0; i values = nodeChangeTable.get(id); + if (values==null) { + values = new ArrayList(); + nodeChangeTable.put(id, values); + } + values.add(value); + + } else if (data instanceof EdgeData) { + EdgeData edgeData = (EdgeData)data; + id = edgeData.getId(); + if (graph.getEdge(id)==null) { + continue; + } + + List values = edgeChangeTable.get(id); + if (values==null) { + values = new ArrayList(); + edgeChangeTable.put(id, values); + } + values.add(value); + } else if (data instanceof GraphView) { + graphChangeList.add(value); + + } else { + throw new RuntimeException("Unrecognized graph object type"); + } + } + + Map graphAttributes = new HashMap(); + for (AttributeValue value: graphChangeList) { + graphAttributes.put(value.getColumn().getTitle(), value.getValue()); + } + if (!graphAttributes.isEmpty()) { + org.gephi.streaming.api.event.GraphEvent streamingEvent = + eventBuilder.graphEvent(ElementType.GRAPH, EventType.CHANGE, null, graphAttributes); + eventHandler.handleGraphEvent(streamingEvent); + } + + for (Map.Entry> entry: nodeChangeTable.entrySet()) { + Map attributes = new HashMap(); + for (AttributeValue value: entry.getValue()) { + if (value.getColumn().getIndex() != PropertiesColumn.NODE_ID.getIndex() + && value.getColumn().getIndex() != PropertiesColumn.EDGE_ID.getIndex()) + attributes.put(value.getColumn().getTitle(), value.getValue()); + } + + if (!attributes.isEmpty()) { + org.gephi.streaming.api.event.GraphEvent streamingEvent = + eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, entry.getKey(), attributes); + eventHandler.handleGraphEvent(streamingEvent); + } + } + + for (Map.Entry> entry: edgeChangeTable.entrySet()) { + Map attributes = new HashMap(); + for (AttributeValue value: entry.getValue()) { + if (value.getColumn().getIndex() != PropertiesColumn.NODE_ID.getIndex() + && value.getColumn().getIndex() != PropertiesColumn.EDGE_ID.getIndex()) + attributes.put(value.getColumn().getTitle(), value.getValue()); + } + + if (!attributes.isEmpty()) { + org.gephi.streaming.api.event.GraphEvent streamingEvent = + eventBuilder.graphEvent(ElementType.EDGE, EventType.CHANGE, entry.getKey(), attributes); + eventHandler.handleGraphEvent(streamingEvent); + } + } + + break; + } + } + + private Map getNodeAttributes(Node node) { + Map attributes = new HashMap(); + AttributeRow row = (AttributeRow) node.getNodeData().getAttributes(); + + if (row != null) + for (AttributeValue attributeValue: row.getValues()) { + if (attributeValue.getColumn().getIndex()!=PropertiesColumn.NODE_ID.getIndex() + && attributeValue.getValue()!=null) + attributes.put(attributeValue.getColumn().getTitle(), attributeValue.getValue()); + } + + if (sendVizData) { + attributes.put("x", node.getNodeData().x()); + attributes.put("y", node.getNodeData().y()); + attributes.put("z", node.getNodeData().z()); + + attributes.put("r", node.getNodeData().r()); + attributes.put("g", node.getNodeData().g()); + attributes.put("b", node.getNodeData().b()); + + attributes.put("size", node.getNodeData().getSize()); + } + + return attributes; + } + + private Map getEdgeAttributes(Edge edge) { + Map attributes = new HashMap(); + AttributeRow row = (AttributeRow) edge.getEdgeData().getAttributes(); + if (row != null) + for (AttributeValue attributeValue: row.getValues()) { + if (attributeValue.getColumn().getIndex()!=PropertiesColumn.EDGE_ID.getIndex() + && attributeValue.getValue()!=null) + attributes.put(attributeValue.getColumn().getTitle(), attributeValue.getValue()); + } + + if (sendVizData) { + + attributes.put("r", edge.getEdgeData().r()); + attributes.put("g", edge.getEdgeData().g()); + attributes.put("b", edge.getEdgeData().b()); + + attributes.put("weight", edge.getWeight()); + } + + return attributes; + } + +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphEventHandler.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphEventHandler.java new file mode 100644 index 0000000000..f8ed422fbe --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphEventHandler.java @@ -0,0 +1,69 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api; + +import org.gephi.streaming.api.event.GraphEvent; + +/** + * This interface defines the basic event handling operations + * of the Graph Streaming API.
+ * Implementations of this interface can be used to handle events and implement + * what should be done when an event occurred (update the workspace, + * export to a specific format, send through the network etc.). + * It could be used also to chain several + * event handlers (the handlers does something with the event and dispatches it to another + * event handler). + * + * @author Andre' Panisson + * + */ +public interface GraphEventHandler { + + /** + * This is the basic event handling operation that will be called when + * an event is received. + * + * @param event - the event to be handled + */ + public void handleGraphEvent(GraphEvent event); + +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java new file mode 100644 index 0000000000..7891e987e3 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java @@ -0,0 +1,557 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api; + +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.gephi.filters.spi.EdgeFilter; +import org.gephi.filters.spi.Filter; +import org.gephi.filters.spi.NodeFilter; + +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphFactory; +import org.gephi.graph.api.Node; +import org.gephi.streaming.api.PropertiesAssociations.EdgeProperties; +import org.gephi.streaming.api.PropertiesAssociations.NodeProperties; +import org.gephi.streaming.api.event.EdgeAddedEvent; +import org.gephi.streaming.api.event.ElementEvent; +import org.gephi.streaming.api.event.FilterEvent; +import org.gephi.streaming.api.event.GraphEvent; + +/** + * This is a GraphEventHandler implementation used to update a graph + * when an event is received. + * + * @author panisson + * + */ +public class GraphUpdaterEventHandler implements GraphEventHandler { + + private static final Logger logger = Logger.getLogger(GraphUpdaterEventHandler.class.getName()); + + private final Graph graph; + private final GraphFactory factory; + protected PropertiesAssociations properties = new PropertiesAssociations(); + protected Report report; + + /** + * This is used to create a new GraphEventHandler that will update a graph + * when an event is received. + * + * @param graph - the Graph to be updated + */ + public GraphUpdaterEventHandler(Graph graph) { + this.graph = graph; + + this.factory = graph.getModel().factory(); + + //Default node associations + properties.addNodePropertyAssociation(NodeProperties.ID, "id"); + properties.addNodePropertyAssociation(NodeProperties.LABEL, "label"); + properties.addNodePropertyAssociation(NodeProperties.X, "x"); + properties.addNodePropertyAssociation(NodeProperties.Y, "y"); + properties.addNodePropertyAssociation(NodeProperties.Z, "z"); + properties.addNodePropertyAssociation(NodeProperties.SIZE, "size"); + properties.addNodePropertyAssociation(NodeProperties.R, "r"); + properties.addNodePropertyAssociation(NodeProperties.G, "g"); + properties.addNodePropertyAssociation(NodeProperties.B, "b"); + properties.addNodePropertyAssociation(NodeProperties.COLOR, "color"); + properties.addNodePropertyAssociation(NodeProperties.FIXED, "fixed"); + + //Default edge associations + properties.addEdgePropertyAssociation(EdgeProperties.ID, "id"); + properties.addEdgePropertyAssociation(EdgeProperties.SOURCE, "source"); + properties.addEdgePropertyAssociation(EdgeProperties.TARGET, "target"); + properties.addEdgePropertyAssociation(EdgeProperties.LABEL, "label"); + properties.addEdgePropertyAssociation(EdgeProperties.WEIGHT, "weight"); + properties.addEdgePropertyAssociation(EdgeProperties.R, "r"); + properties.addEdgePropertyAssociation(EdgeProperties.G, "g"); + properties.addEdgePropertyAssociation(EdgeProperties.B, "b"); + properties.addEdgePropertyAssociation(EdgeProperties.COLOR, "color"); + properties.addEdgePropertyAssociation(EdgeProperties.SIZE, "size"); + } + + /** + * @return the report + */ + public Report getReport() { + return report; + } + + /** + * @param report the report to set + */ + public void setReport(Report report) { + this.report = report; + } + + @Override + public void handleGraphEvent(GraphEvent event) { + try { + doHandleGraphEvent(event); + } catch (RuntimeException e) { + logger.log(Level.INFO, e.getMessage(), e); + if (report!=null) { + Issue issue = new Issue(e, Issue.Level.WARNING); + report.logIssue(issue); + } + } + } + + private void doHandleGraphEvent(GraphEvent event) { + + if (event instanceof ElementEvent) { + ElementEvent elementEvent = (ElementEvent)event; + + switch (event.getElementType()) { + case GRAPH: + this.graphChanged(elementEvent.getAttributes()); + break; + case NODE: + switch (event.getEventType()) { + case ADD: + this.nodeAdded(elementEvent.getElementId(), elementEvent.getAttributes()); + break; + case CHANGE: + this.nodeChanged(elementEvent.getElementId(), elementEvent.getAttributes()); + break; + case REMOVE: + this.nodeRemoved(elementEvent.getElementId()); + break; + } + break; + case EDGE: + switch (event.getEventType()) { + case ADD: + EdgeAddedEvent eaEvent = (EdgeAddedEvent)event; + this.edgeAdded(elementEvent.getElementId(), eaEvent.getSourceId(), + eaEvent.getTargetId(), eaEvent.isDirected(), + elementEvent.getAttributes()); + break; + case CHANGE: + this.edgeChanged(elementEvent.getElementId(), + elementEvent.getAttributes()); + break; + case REMOVE: + this.edgeRemoved(elementEvent.getElementId()); + break; + } + break; + } + } else if (event instanceof FilterEvent) { + FilterEvent filterEvent = (FilterEvent)event; + applyFilter(filterEvent); + } + } + + private void log(String message) { + if (report!=null) { + Issue issue = new Issue(message, Issue.Level.INFO); + report.logIssue(issue); + } + logger.warning(message); + } + + private void graphChanged(Map attributes) { + graph.writeLock(); + + if (attributes!=null && attributes.size() > 0) { + for(Map.Entry entry: attributes.entrySet()) { + graph.setAttribute(entry.getKey(), entry.getValue()); + } + } + + graph.writeUnlock(); + } + + private void edgeAdded(String edgeId, String fromNodeId, String toNodeId, + boolean directed, Map attributes) { + + Edge edge = graph.getEdge(edgeId); + if (edge!=null) { + log("Edge added event ignored for edge "+edgeId+": Edge already exists"); + return; + } + + Node source = graph.getNode(fromNodeId); + if (source==null) { + log("Edge added event ignored for edge "+edgeId+": Source node "+fromNodeId+" not found"); + return; + } + + Node target = graph.getNode(toNodeId); + if (target==null) { + log("Edge added event ignored for edge "+edgeId+": Target node "+toNodeId+" not found"); + return; + } + + if(source!=null && target!=null) { + + edge = factory.newEdge(edgeId, source, target,0 , 1.0f, directed); + + if (attributes!=null && attributes.size() > 0) { + for(Map.Entry entry: attributes.entrySet()) { + this.addEdgeAttribute(edge, entry.getKey(), entry.getValue()); + } + } + + graph.writeLock(); + graph.addEdge(edge); + + // graph.setId(edge, edgeId); + graph.writeUnlock(); + } + } + + private void edgeChanged(String edgeId, Map attributes) { + Edge edge = graph.getEdge(edgeId); + if (edge!=null) { + + graph.writeLock(); + + if (attributes!=null && attributes.size() > 0) { + for(Map.Entry entry: attributes.entrySet()) { + this.addEdgeAttribute(edge, entry.getKey(), entry.getValue()); + } + } + + graph.writeUnlock(); + } else { + log("Edge changed event ignored for edge "+edgeId+": Edge not found"); + } + } + + private void edgeRemoved(String edgeId) { + Edge edge = graph.getEdge(edgeId); + if (edge!=null) { + graph.writeLock(); + graph.removeEdge(edge); + graph.writeUnlock(); + } else { + log("Edge removed event ignored for edge "+edgeId+": Edge not found"); + } + } + + private void nodeAdded(String nodeId, Map attributes) { + Node node = graph.getNode(nodeId); + if (node==null) { + node = factory.newNode(nodeId); + + if (attributes!=null && attributes.size() > 0) { + for(Map.Entry entry: attributes.entrySet()) { + this.addNodeAttribute(node, entry.getKey(), entry.getValue()); + } + } + + graph.writeLock(); + // graph.setId(node, nodeId); + graph.addNode(node); + graph.writeUnlock(); + } else { + log("Node added event ignored for node "+nodeId+": Node already exists"); + } + } + + private void nodeChanged(String nodeId, Map attributes) { + Node node = graph.getNode(nodeId); + if (node!=null) { + + graph.writeLock(); + + if (attributes!=null && attributes.size() > 0) { + for(Map.Entry entry: attributes.entrySet()) { + this.addNodeAttribute(node, entry.getKey(), entry.getValue()); + } + } + + graph.writeUnlock(); + } else { + log("Node changed event ignored for node "+nodeId+": Node not found"); + } + } + + private void nodeRemoved(String nodeId) { + Node node = graph.getNode(nodeId); + if (node!=null) { + graph.writeLock(); + + for (Edge edge: graph.getEdges(node).toArray()) { + graph.removeEdge(edge); + } + + graph.removeNode(node); + graph.writeUnlock(); + } else { + log("Node changed event ignored for node "+nodeId+": Node not found"); + } + } + + private void addNodeAttribute(Node node, String attributeName, Object value) { + NodeProperties p = properties.getNodeProperty(attributeName); + if (p != null) { + injectNodeProperty(p, value, node); + } + + else if (!node.getAttributeKeys().isEmpty()) { + node.setAttribute(attributeName, value); + } + } + + private void addEdgeAttribute(Edge edge, String attributeName, Object value) { + EdgeProperties p = properties.getEdgeProperty(attributeName); + if (p != null) { + injectEdgeProperty(p, value, edge); + } + else if (! edge.getAttributeKeys().isEmpty()) { + edge.setAttribute(attributeName, value); + } + } + + private void injectNodeProperty(NodeProperties p, Object value, Node node) { + switch (p) { + case ID: + String id = value.toString(); + if (id != null) { + // graph.setId(node, id); + } + break; + case LABEL: + String label = (value!=null)?value.toString():null; + node.setLabel(label); + break; + case FIXED: + boolean fixed = (value!=null)?Boolean.valueOf(value.toString()):false; + node.setFixed(fixed); + break; + case X: + float x = Float.valueOf(value.toString()); + if (x != 0) { + node.setX(x); + } + break; + case Y: + float y = Float.valueOf(value.toString()); + if (y != 0) { + node.setY(y); + } + break; + case Z: + float z = Float.valueOf(value.toString()); + if (z != 0) { + node.setZ(z); + } + break; + case R: + float r = Float.valueOf(value.toString()); + if (r < 0.0 || r > 1.0) { + throw new IllegalArgumentException("Color parameter outside of expected range: " + r); + } + node.setR(r); + break; + case G: + float g = Float.valueOf(value.toString()); + if (g < 0.0 || g > 1.0) { + throw new IllegalArgumentException("Color parameter outside of expected range: " + g); + } + node.setG(g); + break; + case B: + float b = Float.valueOf(value.toString()); + if (b < 0.0 || b > 1.0) { + throw new IllegalArgumentException("Color parameter outside of expected range: " + b); + } + node.setB(b); + break; + case COLOR: + Integer color = (value!=null)?Integer.decode(value.toString()):0; + int i = color.intValue(); + node.setR(((i >> 16) & 0xFF)/255.f); + node.setG(((i >> 8) & 0xFF)/255.f); + node.setB((i & 0xFF)/255.f); + break; + case SIZE: + float size = Float.valueOf(value.toString()); + node.setSize(size); + break; + } + } + + private void injectEdgeProperty(EdgeProperties p, Object value, + Edge edge) { + + switch (p) { + case ID: + String id = value.toString(); + if (id != null) { + // graph.setId(edge, id); + } + break; + case LABEL: + String label = value.toString(); + if (label != null) { + edge.setLabel(label); + } + break; + /* + * Unsupported set of SOURCE and TARGET + */ +// case SOURCE: +// String source = value.toString(); +// if (source != null) { +// Node sourceNode = graph.getNode(source); +// edge.setSource(sourceNode); +// } +// break; +// case TARGET: +// String target = value.toString(); +// if (target != null) { +// Node targetNode = graph.getNode(target); +// edge.setTarget(targetNode); +// } +// break; + case WEIGHT: + float weight = Float.valueOf(value.toString()); + if (weight != 0) { + edge.setWeight(weight); + } + break; + case R: + float r = Float.valueOf(value.toString()); + if (r < 0.0 || r > 1.0) { + throw new IllegalArgumentException("Color parameter outside of expected range: " + r); + } + edge.setR(r); + break; + case G: + float g = Float.valueOf(value.toString()); + if (g < 0.0 || g > 1.0) { + throw new IllegalArgumentException("Color parameter outside of expected range: " + g); + } + edge.setG(g); + break; + case B: + float b = Float.valueOf(value.toString()); + if (b < 0.0 || b > 1.0) { + throw new IllegalArgumentException("Color parameter outside of expected range: " + b); + } + edge.setB(b); + break; + case COLOR: + Integer color = (value!=null)?Integer.decode(value.toString()):0; + int i = color.intValue(); + edge.setR(((i >> 16) & 0xFF)/255.f); + edge.setG(((i >> 8) & 0xFF)/255.f); + edge.setB((i & 0xFF)/255.f); + break; + case SIZE: + float size = Float.valueOf(value.toString()); + edge.setWeight(size); + break; + } + } + + private void applyFilter(FilterEvent filterEvent) { + + Filter filter = filterEvent.getFilter(); + if (filter instanceof NodeFilter) { + NodeFilter nodeFilter = (NodeFilter)filter; + Map attributes = filterEvent.getAttributes(); + for (Node node: graph.getNodes().toArray()) { + if (nodeFilter.evaluate(graph, node)) { + + switch (filterEvent.getEventType()) { + case ADD: + log("Unsupported FilterEvent of type ADD"); + break; + case CHANGE: + graph.writeLock(); + if (attributes!=null && attributes.size() > 0) { + for(Map.Entry entry: attributes.entrySet()) { + this.addNodeAttribute(node, entry.getKey(), entry.getValue()); + } + } + graph.writeUnlock(); + break; + case REMOVE: + graph.writeLock(); + graph.removeNode(node); + graph.writeUnlock(); + break; + } + } + } + } + + if (filter instanceof EdgeFilter) { + EdgeFilter edgeFilter = (EdgeFilter)filter; + Map attributes = filterEvent.getAttributes(); + for (Edge edge: graph.getEdges().toArray()) { + if (edgeFilter.evaluate(graph, edge)) { + + switch (filterEvent.getEventType()) { + case ADD: + log("Unsupported FilterEvent of type ADD"); + break; + case CHANGE: + graph.writeLock(); + if (attributes!=null && attributes.size() > 0) { + for(Map.Entry entry: attributes.entrySet()) { + this.addEdgeAttribute(edge, entry.getKey(), entry.getValue()); + } + } + graph.writeUnlock(); + break; + case REMOVE: + graph.writeLock(); + graph.removeEdge(edge); + graph.writeUnlock(); + break; + } + } + } + } + + } + +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Issue.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Issue.java new file mode 100644 index 0000000000..7045d632e6 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Issue.java @@ -0,0 +1,101 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api; + +/** + * Issue are logged and classified by Report to describe a problem encoutered during + * import process. Fill issues as Exceptions. + * + * @author Mathieu Bastian + */ +public final class Issue { + + public enum Level { + + INFO(100), + WARNING(200), + SEVERE(500), + CRITICAL(1000); + private final int levelInt; + + Level(int levelInt) { + this.levelInt = levelInt; + } + + public int toInteger() { + return levelInt; + } + } + private final Throwable throwable; + private final String message; + private final Level level; + + public Issue(Throwable throwable, Level level) { + this.throwable = throwable; + this.level = level; + this.message = throwable.getMessage(); + } + + public Issue(String message, Level level, Throwable throwable) { + this.throwable = throwable; + this.level = level; + this.message = message; + } + + public Issue(String message, Level level) { + this.message = message; + this.level = level; + this.throwable = null; + } + + public String getMessage() { + return message; + } + + public Level getLevel() { + return level; + } + + public Throwable getThrowable() { + return throwable; + } +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/PropertiesAssociations.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/PropertiesAssociations.java new file mode 100644 index 0000000000..c255574930 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/PropertiesAssociations.java @@ -0,0 +1,159 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * + * @author Mathieu Bastian + */ +public final class PropertiesAssociations implements Serializable { + + public enum NodeProperties { + + X, Y, Z, R, G, B, COLOR, SIZE, ID, LABEL, FIXED; + } + + public enum EdgeProperties { + + R, G, B, COLOR, WEIGHT, ID, LABEL, ALPHA, SOURCE, TARGET, SIZE; + } + //PropertiesAssociations association + private List> nodePropertyAssociations = new LinkedList>(); + private List> edgePropertyAssociations = new LinkedList>(); + + public void addEdgePropertyAssociation(EdgeProperties property, String title) { + PropertyAssociation association = new PropertyAssociation(property, title); + if (edgePropertyAssociations.contains(association)) { + return; + } + //Avoid any double + for (Iterator> itr = edgePropertyAssociations.iterator(); itr.hasNext();) { + PropertyAssociation p = itr.next(); + if (p.getTitle().equalsIgnoreCase(association.getTitle())) { + itr.remove(); + } else if (p.getProperty().equals(association.getProperty())) { + itr.remove(); + } + } + edgePropertyAssociations.add(association); + } + + public void addNodePropertyAssociation(NodeProperties property, String title) { + PropertyAssociation association = new PropertyAssociation(property, title); + if (nodePropertyAssociations.contains(association)) { + return; + } + //Avoid any double + for (Iterator> itr = nodePropertyAssociations.iterator(); itr.hasNext();) { + PropertyAssociation p = itr.next(); + if (p.getTitle().equalsIgnoreCase(association.getTitle())) { + itr.remove(); + } else if (p.getProperty().equals(association.getProperty())) { + itr.remove(); + } + } + nodePropertyAssociations.add(association); + } + + PropertyAssociation[] getEdgePropertiesAssociation() { + return edgePropertyAssociations.toArray(new PropertyAssociation[0]); + } + + PropertyAssociation[] getNodePropertiesAssociation() { + return nodePropertyAssociations.toArray(new PropertyAssociation[0]); + } + + public NodeProperties getNodeProperty(String title) { + for (PropertyAssociation p : nodePropertyAssociations) { + if (p.getTitle().equalsIgnoreCase(title)) { + return p.getProperty(); + } + } + return null; + } + + public EdgeProperties getEdgeProperty(String title) { + for (PropertyAssociation p : edgePropertyAssociations) { + if (p.getTitle().equalsIgnoreCase(title)) { + return p.getProperty(); + } + } + return null; + } + + public String getNodePropertyTitle(NodeProperties property) { + for (PropertyAssociation p : nodePropertyAssociations) { + if (p.getProperty().equals(property)) { + return p.getTitle(); + } + } + return null; + } + + public String getEdgePropertyTitle(EdgeProperties property) { + for (PropertyAssociation p : edgePropertyAssociations) { + if (p.getProperty().equals(property)) { + return p.getTitle(); + } + } + return null; + } + + public String getInfos() { + String res = "***Node Properties Associations***\n"; + for (PropertyAssociation p : nodePropertyAssociations) { + res += "Property " + p.getProperty().toString() + " = " + p.getTitle() + " Column\n"; + } + res += "*********************************\n"; + res = "***Edge Properties Associations***\n"; + for (PropertyAssociation p : edgePropertyAssociations) { + res += "Property " + p.getProperty().toString() + " = " + p.getTitle() + " Column\n"; + } + res += "*********************************\n"; + return res; + } +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/PropertyAssociation.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/PropertyAssociation.java new file mode 100644 index 0000000000..5ed831d07c --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/PropertyAssociation.java @@ -0,0 +1,94 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api; + +import java.io.Serializable; + +/** + * + * @author Mathieu Bastian + */ +public final class PropertyAssociation implements Serializable { + + private final Property property; + private final String title; + private volatile int hashCode = 0; //Cache hashcode + + PropertyAssociation(Property property, String title) { + this.property = property; + this.title = title; + } + + public Property getProperty() { + return property; + } + + public String getTitle() { + return title; + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof PropertyAssociation)) { + return false; + } + if (obj == this) { + return true; + } + PropertyAssociation foreign = (PropertyAssociation) obj; + if (foreign.title.equals(title) && foreign.property.equals(property)) { + return true; + } + return false; + } + + @Override + public int hashCode() { + if (hashCode == 0) { + int res = 17; + res = 37 * res + title.hashCode(); + res = 37 * res + property.hashCode(); + hashCode = res; + } + return hashCode; + } +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Report.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Report.java new file mode 100644 index 0000000000..ee77e8854d --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Report.java @@ -0,0 +1,201 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api; + +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; +import org.gephi.streaming.api.Issue.Level; + +/** + * Report is a log and issue container. Filled with information, details, + * minor or major issues, it is stored in an issue list + * and can be retrieved to present issues to end-users. + * Behavior is the same as a simple logging library. + * + * @author Mathieu Bastian + */ +public final class Report { + + private final Queue entries = new ConcurrentLinkedQueue(); + private Issue.Level exceptionLevel = Issue.Level.CRITICAL; + private AtomicInteger eventCounter = new AtomicInteger(); + private String source; + + public int incrementEventCounter() { + return eventCounter.incrementAndGet(); + } + + public int getEventCounter() { + return eventCounter.get(); + } + + /** + * Log an information message in the report. + * @param message the message to write in the report + * @throws NullPointerException if message is null + */ + public void log(String message) { + entries.add(new ReportEntry(message)); + } + + /** + * Log an issue in the report. + * @param issue the issue to write in the report + * @throws NullPointerException if issue is null + */ + public void logIssue(Issue issue) { + entries.add(new ReportEntry(issue)); + if (issue.getLevel().toInteger() >= exceptionLevel.toInteger()) { + if (issue.getThrowable() != null) { + throw new RuntimeException(issue.getMessage(), issue.getThrowable()); + } else { + throw new RuntimeException(issue.getMessage()); + } + } + } + + /** + * Returns all issues written in the report. + * @return a collection of all issues written in the report + */ + public List getIssues() { + List res = new ArrayList(); + for (ReportEntry re : entries) { + if (re.issue != null) { + res.add(re.issue); + } + } + return res; + } + + /** + * Returns the report logs and issues, presented as HTML code. + * @return a string of HTML code where all messages and issues are written + */ + public String getHtml() { + StringBuilder builder = new StringBuilder(); + for (ReportEntry re : entries) { + if (re.issue != null) { + builder.append(re.issue.getMessage()); + builder.append("
"); + } else { + builder.append(re.message); + builder.append("
"); + } + } + builder.append(eventCounter.get()) + .append(" processed events") + .append("
"); + return builder.toString(); + } + + /** + * Returns the report logs and issues, presented as basic multi-line text. + * @return a string of all messages and issues written in the report, one per line + */ + public String getText() { + StringBuilder builder = new StringBuilder(); + for (ReportEntry re : entries) { + if (re.issue != null) { + builder.append(re.issue.getMessage()); + builder.append("\n"); + } else { + builder.append(re.message); + builder.append("\n"); + } + } + builder.append(eventCounter.get()) + .append(" processed events") + .append("\n"); + return builder.toString(); + } + + /** + * Get the current exception level for the report. Default is Level.CRITICAL. + * @return the current exception level + */ + public Level getExceptionLevel() { + return exceptionLevel; + } + + /** + * Set the level of exception for the report. If a reported issue has his level greater or equal + * as exceptionLevel, an exception is thrown. Default is Level.CRITICAL + * @param exceptionLevel the exception level where exceptions are to be thrown + */ + public void setExceptionLevel(Level exceptionLevel) { + this.exceptionLevel = exceptionLevel; + } + + /** + * @return the source + */ + public String getSource() { + return source; + } + + /** + * @param source the source to set + */ + public void setSource(String source) { + this.source = source; + } + + private class ReportEntry { + + private final Issue issue; + private final String message; + + public ReportEntry(Issue issue) { + this.issue = issue; + this.message = null; + } + + public ReportEntry(String message) { + this.message = message; + this.issue = null; + } + } +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamReader.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamReader.java new file mode 100644 index 0000000000..4aa6a71e93 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamReader.java @@ -0,0 +1,151 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api; + +import org.gephi.streaming.api.event.GraphEventBuilder; +import java.io.IOException; +import java.io.InputStream; +import java.nio.channels.ReadableByteChannel; + +/** + * Read events from a stream. + * An implementation of this class should read information + * from the InputStream, create the appropriate GraphEvent + * and send it to the GraphEventHandler handler. + * + * @author Andre' Panisson + */ +public abstract class StreamReader { + + /** + * The handler that will handle events + */ + protected final GraphEventHandler handler; + /** + * The event builder to create graph events + */ + protected final GraphEventBuilder eventBuilder; + /** + * The report to add useful information + */ + protected Report report; + /** + * A listener to receive status notifications + */ + protected StreamReaderStatusListener listener; + + /** + * Create a new StreamReader that will send to the handler + * the events created using the event builder. + * + * @param handler the GraphEventHandler to which the events will be delegated + */ + public StreamReader(GraphEventHandler handler, + GraphEventBuilder eventBuilder) { + this.handler = handler; + this.eventBuilder = eventBuilder; + } + + /** + * Used to get the Report where information is being added. + * + * @return the report + */ + public Report getReport() { + return report; + } + + /** + * Used to set the Report where information will be added. + * + * @param report the report to set + */ + public void setReport(Report report) { + this.report = report; + } + + /** + * Read from the InputStream and send the appropriate event + * to the GraphEventHandler + * + * @param inputStream the InputStream to read from. + * @throws IOException when unable to connect to the InputStream + */ + public abstract void processStream(InputStream inputStream) throws IOException; + + /** + * Read from the channel and send the appropriate event + * to the GraphEventHandler + * + * @param channel the ReadableByteChannel to read from. + * @throws IOException when unable to connect to the InputStream + */ + public abstract void processStream(ReadableByteChannel channel) throws IOException; + + /** + * Set a listener to asynchronously receive status notifications. + * @param listener the listener to be notifiedConnection + */ + public void setStatusListener(StreamReaderStatusListener listener) { + this.listener = listener; + } + + /** + * This is the listener interface to asynchronously receive status notifications. + * It should be registered using setStatusListener(). + */ + public interface StreamReaderStatusListener { + /** + * Called when the stream is closed + */ + public void onStreamClosed(); + /** + * Called when data is received + */ + public void onDataReceived(); + /** + * Called when error occurs + */ + public void onError(); + } + +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamReaderFactory.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamReaderFactory.java new file mode 100644 index 0000000000..97420c7640 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamReaderFactory.java @@ -0,0 +1,75 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api; + +import org.gephi.streaming.api.event.GraphEventBuilder; + +/** + * A factory for StreamReaders + * + * @author Andre' Panisson + * + */ +public interface StreamReaderFactory { + + /** + * Create a StreamReader based on the specified stream type. + * The read events will be sent to the specified GraphEventHandler. + * + * @param streamType + * @param handler + * @return the StreamReader able to process the specified stream type + */ + public StreamReader createStreamReader(String streamType, GraphEventHandler handler, + GraphEventBuilder eventBuilder); + + /** + * Create a StreamReader based on the specified stream type. + * The read events will be sent to the specified GraphEventHandler. + * + * @param streamType + * @param handler + * @return the StreamReader able to process the specified stream type + */ + public StreamReader createStreamReader(StreamType streamType, GraphEventHandler handler, + GraphEventBuilder eventBuilder); +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamType.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamType.java new file mode 100644 index 0000000000..df7c65abc6 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamType.java @@ -0,0 +1,67 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api; + +/** + * Defines a Stream Type + * + * @author Andre' Panisson + * + */ +public interface StreamType { + + /** + * Used to get the String representing this stream type + * @return the String representing this stream type + */ + public String getType(); + /** + * Used to get the StreamReader class implementation for this stream type + * @return the StreamReader class implementation for this stream type + */ + public Class getStreamReaderClass(); + /** + * Used to get the StreamWriter class implementation for this stream type + * @return the StreamWriter class implementation for this stream type + */ + public Class getStreamWriterClass(); +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamWriter.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamWriter.java new file mode 100644 index 0000000000..6f53796059 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamWriter.java @@ -0,0 +1,86 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api; + +import java.io.OutputStream; + +/** + * A sub-type of GraphEventHandler that writes to an OutputStream + * when an event arrives. Can be used to implement + * stream exporters or to expose the events to a socket + * in a specific format. + * + * @author Andre' Panisson + * + */ +public abstract class StreamWriter implements GraphEventHandler { + + protected final OutputStream outputStream; + + /** + * Sets the OutputStream to write to + * + * @param outputStream + */ + public StreamWriter(OutputStream outputStream) { + this.outputStream = outputStream; + } + + /** + * @return the OutputStream to write to + */ + public OutputStream getOutputStream() { + return outputStream; + } + + /** + * Called before start processing the events (to write + * headers, etc.) + */ + public abstract void startStream(); + + /** + * Called after end processing the events (to write + * footers, etc.) + */ + public abstract void endStream(); +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamWriterFactory.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamWriterFactory.java new file mode 100644 index 0000000000..091a4f8a27 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamWriterFactory.java @@ -0,0 +1,71 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api; + +import java.io.OutputStream; + +/** + * A factory for StreamWriters + * + * @author Andre' Panisson + * + */ +public interface StreamWriterFactory { + + /** + * Create a StreamWriter based on the specified stream type + * + * @param streamType + * @param outputStream + * @return the StreamWriter able to write in the specified stream type + */ + public StreamWriter createStreamWriter(String streamType, OutputStream outputStream); + + /** + * Create a StreamWriter based on the specified stream type + * + * @param streamType + * @param outputStream + * @return the StreamWriter able to write in the specified stream type + */ + public StreamWriter createStreamWriter(StreamType streamType, OutputStream outputStream); +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamingConnection.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamingConnection.java new file mode 100644 index 0000000000..3c08b012f5 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamingConnection.java @@ -0,0 +1,124 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api; + +import java.io.IOException; + +/** + * This interface represents a connection to a stream. + * + * @author panisson + */ +public interface StreamingConnection { + + /** + * This is used to get the URL where this object is connected. + * @return the URL + */ + public StreamingEndpoint getStreamingEndpoint(); + + /** + * This is used to get the Report object where information is stored. + * @return the Report object + */ + public Report getReport(); + + /** + * This is used to close the connection + * @throws IOException + */ + public void close() throws IOException; + + /** + * This is used to verify if the connection is closed + * @return true if connection closed, false otherwise + */ + public boolean isClosed(); + + /** + * This is used to connect to the URL and process asynchronously the + * events received. A new thread will be created and the process() + * will be called. + */ + public void asynchProcess(); + + /** + * This is used to connect to the URL and process the + * events received. The method returns only when the connection is closed. + */ + public void process(); + + /** + * Set a listener to asynchronously receive status notifications. + * @param listener the listener to be notifiedConnection + */ + public void addStatusListener(StatusListener listener); + + /** + * Remove the StatusListener from the listeners + * @param listener the StatusListener to remove + */ + public void removeStatusListener(StatusListener listener); + + /** + * This is the listener interface to asynchronously receive status notifications. + * It should be registered using setStatusListener(). + */ + public interface StatusListener { + /** + * Called when the connection is closed + * @param connection - the connection reference + */ + public void onConnectionClosed(StreamingConnection connection); + /** + * Called when some data is received + * @param connection - the connection reference + */ + public void onDataReceived(StreamingConnection connection); + /** + * Called when an error occurred + * @param connection - the connection reference + */ + public void onError(StreamingConnection connection); + } + +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamingController.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamingController.java new file mode 100644 index 0000000000..e3274c7151 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamingController.java @@ -0,0 +1,105 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ + +package org.gephi.streaming.api; + +import java.io.IOException; +import org.gephi.graph.api.Graph; + +/** + * Controller to connect to graph streams and update graphs accordingly. + * + * @author panisson + */ +public interface StreamingController { + + /** + *

This is used to connect to a StreamingEndpoint. The connection will be + * bound with the Graph object: any event received in the stream will cause + * an update in the graph, and updates in the graph will cause an event + * to be sent to the StreamingEndpoint. + *

Note that the connection will not start to be processed immediately after + * created. You should call connection.process() to star processing it: + *

 StreamingController controller = Lookup.getDefault().lookup(StreamingController.class);
+     * StreamingConnection connection = controller.connect(endpoint, graph);
+     * connection.process(); 
+ * + * @param endpoint - the StreamingEndpoint to connect to + * @param graph - the Graph that will be updated with the events + * @return the streaming connection + * @throws IOException + */ + public StreamingConnection connect(StreamingEndpoint endpoint, Graph graph) + throws IOException; + + /** + *

This is used to connect to a StreamingEndpoint. The connection will be + * bound with the Graph object: any event received in the stream will cause + * an update in the graph, and updates in the graph will cause an event + * to be sent to the StreamingEndpoint. + * The Report object will be updated + * with useful information. + *

Note that the connection will not start to be processed immediately after + * created. You should call connection.process() to star processing it: + *

 StreamingController controller = Lookup.getDefault().lookup(StreamingController.class);
+     * Report report = new Report();
+     * StreamingConnection connection = controller.connect(endpoint, graph, report);
+     * connection.process(); 
+ * + * @param endpoint - the StreamingEndpoint to connect to + * @param graph - the Graph that will be updated with the events + * @param report - the Report object that will be updated with information + * @return the streaming connection + * @throws IOException + */ + public StreamingConnection connect(StreamingEndpoint endpoint, Graph graph, + Report report) + throws IOException; + + /** + * Utility function to get the stream implementation for a given stream type. + * + * @param streamType - the String representation of the stream type + * @return the stream type implementation + */ + public StreamType getStreamType(String streamType); +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamingEndpoint.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamingEndpoint.java new file mode 100644 index 0000000000..f2131b73a9 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/StreamingEndpoint.java @@ -0,0 +1,143 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ + +package org.gephi.streaming.api; + +import java.io.Serializable; +import java.net.URL; + +/** + * A streaming endpoint, with the information required to connect to a stream + * and process it. + * + * @author Andre' Panisson + */ +public class StreamingEndpoint implements Serializable { + + private static final long serialVersionUID = 1L; + + private URL url; + private StreamType streamType; + private String user; + private String password; + + /** + * Create a new StreamingEndpoint with no information on it + */ + public StreamingEndpoint() {} + + /** + * Create a new StreamingEndpoint setting its URL and streamType + * @param url - the URL to set + * @param streamType - the streamType to set + */ + public StreamingEndpoint(URL url, StreamType streamType) { + this.url = url; + this.streamType = streamType; + } + + /** + * Get the URL to connect to + * @return the URL to connect to + */ + public URL getUrl() { + return url; + } + + /** + * Sets the URL to connect to + * @param url the URL to connect to + */ + public void setUrl(URL url) { + this.url = url; + } + + /** + * Return the stream type + * @return the stream type + */ + public StreamType getStreamType() { + return streamType; + } + + /** + * Sets the stream type + * + * @param streamType the stream type + */ + public void setStreamType(StreamType streamType) { + this.streamType = streamType; + } + + /** + * Return the user to be used in case of authenticated connection + * @return the user + */ + public String getUser() { + return user; + } + + /** + * Sets the user to be used in case of authenticated connection + * @param user the user to set + */ + public void setUser(String user) { + this.user = user; + } + + /** + * Return the password to be used in case of authenticated connection + * @return the password + */ + public String getPassword() { + return password; + } + + /** + * Sets the password to be used in case of authenticated connection + * @param password the password to set + */ + public void setPassword(String password) { + this.password = password; + } + +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/EdgeAddedEvent.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/EdgeAddedEvent.java new file mode 100644 index 0000000000..8ac28a0d1c --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/EdgeAddedEvent.java @@ -0,0 +1,120 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api.event; + +import java.util.Map; + +/** + * @author panisson + * + */ +public final class EdgeAddedEvent extends ElementEvent { + + private static final long serialVersionUID = 1L; + + private final String sourceId; + private final String targetId; + private final boolean directed; + + /** + * @param source + * @param elementId + * @param sourceId + * @param targetId + * @param directed + * @param attributes + */ + public EdgeAddedEvent(Object source, String elementId, + String sourceId, String targetId, boolean directed, Map attributes) { + super(source, EventType.ADD, ElementType.EDGE, elementId, attributes); + this.sourceId = sourceId; + this.targetId = targetId; + this.directed = directed; + } + + /** + * @return the sourceId + */ + public String getSourceId() { + return sourceId; + } + + /** + * @return the targetId + */ + public String getTargetId() { + return targetId; + } + + /** + * @return the directed + */ + public boolean isDirected() { + return directed; + } + + @Override + public boolean equals(Object obj) { + if ( this == obj ) return true; + if ( obj == null || obj.getClass() != this.getClass() ) return false; + + EdgeAddedEvent e = (EdgeAddedEvent)obj; + return this.elementType == e.elementType + && this.eventType == e.eventType + && this.elementId.equals(e.elementId) + && this.sourceId.equals(e.sourceId) + && this.targetId.equals(e.targetId) + && this.directed == e.directed; + } + + @Override + public int hashCode() { + int hash = 1; + hash = hash * 31 + elementType.hashCode(); + hash = hash * 31 + eventType.hashCode(); + hash = hash * 31 + elementId.hashCode(); + hash = hash * 31 + sourceId.hashCode(); + hash = hash * 31 + targetId.hashCode(); + hash = hash << 1 + (directed?1:0); + return hash; + } +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/ElementEvent.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/ElementEvent.java new file mode 100644 index 0000000000..87fa898937 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/ElementEvent.java @@ -0,0 +1,106 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api.event; + +import java.util.Collections; +import java.util.Map; + +/** + * @author panisson + * + */ +public class ElementEvent extends GraphEvent { + + private static final long serialVersionUID = 1L; + + protected final String elementId; + + /** + * @param source + * @param eventType + * @param elementType + * @param elementId + * @param attributes + */ + public ElementEvent(Object source, EventType eventType, + ElementType elementType, String elementId, Map attributes) { + super(source, eventType, elementType, attributes); + this.elementId = elementId; + } + + /** + * @return the elementId + */ + public String getElementId() { + return elementId; + } + + /* (non-Javadoc) + * @see java.util.EventObject#toString() + */ + @Override + public String toString() { + return new StringBuffer("ElementEvent[") + .append(this.eventType).append(" ") + .append(this.elementType).append(" ") + .append(this.elementId).append(": ") + .append(this.attributes).append("]").toString(); + } + + @Override + public boolean equals(Object obj) { + if ( this == obj ) return true; + if ( obj == null || obj.getClass() != this.getClass() ) return false; + + ElementEvent e = (ElementEvent)obj; + return this.elementType == e.elementType + && this.eventType == e.eventType + && this.elementId.equals(e.elementId); + } + + @Override + public int hashCode() { + return (elementType.hashCode() * 31 + eventType.hashCode()) * 31 + + ((elementId!=null)?elementId.hashCode():0); + } + +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/ElementType.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/ElementType.java new file mode 100644 index 0000000000..7ae2ba22a5 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/ElementType.java @@ -0,0 +1,61 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api.event; + +/** + * @author panisson + * + */ +public enum ElementType { + /** + * When element is a node + */ + NODE, + /** + * When element is an edge + */ + EDGE, + /** + * When element is the graph + */ + GRAPH +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/EventType.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/EventType.java new file mode 100644 index 0000000000..9bdec94f33 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/EventType.java @@ -0,0 +1,61 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api.event; + +/** + * @author panisson + * + */ +public enum EventType { + /** + * When element is added + */ + ADD, + /** + * When element is changed + */ + CHANGE, + /** + * When element is removed + */ + REMOVE +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/FilterEvent.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/FilterEvent.java new file mode 100644 index 0000000000..34241d8ff8 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/FilterEvent.java @@ -0,0 +1,92 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api.event; + +import java.util.Map; +import org.gephi.filters.spi.Filter; + +/** + * @author panisson + * + */ +public class FilterEvent extends GraphEvent { + + private static final long serialVersionUID = 1L; + + protected final Filter filter; + + /** + * @param source + * @param eventType + * @param elementType + * @param filter + */ + public FilterEvent(Object source, EventType eventType, + ElementType elementType, Filter filter, Map attributes) { + super(source, eventType, elementType, attributes); + this.filter = filter; + } + + /** + * @return the filter + */ + public Filter getFilter() { + return filter; + } + + @Override + public boolean equals(Object obj) { + if ( this == obj ) return true; + if ( obj == null || obj.getClass() != this.getClass() ) return false; + + FilterEvent e = (FilterEvent)obj; + return this.elementType == e.elementType + && this.eventType == e.eventType + && this.filter.equals(e.filter); + } + + @Override + public int hashCode() { + return elementType.hashCode() * 31 + eventType.hashCode(); + } + +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/GraphEvent.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/GraphEvent.java new file mode 100644 index 0000000000..1ca53d1ed3 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/GraphEvent.java @@ -0,0 +1,148 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api.event; + +import java.util.Collections; +import java.util.EventObject; +import java.util.Map; + +/** + * The basic streaming graph event representation. + * + * @author panisson + * + */ +public class GraphEvent extends EventObject { + + private static final long serialVersionUID = 1L; + + protected final EventType eventType; + protected final ElementType elementType; + protected String eventId; + protected Double timestamp = null; + protected final Map attributes; + + /** + * Constructs a graph Event. + * + * @param source The object on which the Event initially occurred. + * @param eventType + * @param elementType + * @exception IllegalArgumentException if source is null. + */ + public GraphEvent(Object source, EventType eventType, + ElementType elementType, Map attributes) { + super(source); + this.eventType = eventType; + this.elementType = elementType; + this.attributes = attributes; + } + + /** + * @return the eventType + */ + public EventType getEventType() { + return eventType; + } + + /** + * @return the elementType + */ + public ElementType getElementType() { + return elementType; + } + + /** + * @return the eventId + */ + public String getEventId() { + return eventId; + } + + /** + * @param eventId the eventId to set + */ + public void setEventId(String eventId) { + this.eventId = eventId; + } + + /* (non-Javadoc) + * @see java.util.EventObject#toString() + */ + @Override + public String toString() { + return new StringBuffer("GraphEvent[") + .append(this.eventType).append(" ") + .append(this.elementType).append("]").toString(); + } + + @Override + public boolean equals(Object obj) { + if ( this == obj ) return true; + if ( obj == null || obj.getClass() != this.getClass() ) return false; + + GraphEvent e = (GraphEvent)obj; + return this.elementType == e.elementType + && this.eventType == e.eventType; + } + + @Override + public int hashCode() { + return this.elementType.hashCode() * 31 + this.eventType.hashCode(); + } + + /** + * @return the node attributes + */ + public Map getAttributes() { + if (attributes==null) return null; + return Collections.unmodifiableMap(attributes); + } + + public void setTimestamp(Double timestamp) { + this.timestamp = timestamp; + } + + public Double getTimestamp() { + return this.timestamp; + } +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/GraphEventBuilder.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/GraphEventBuilder.java new file mode 100644 index 0000000000..9d4e557bb5 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/GraphEventBuilder.java @@ -0,0 +1,98 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.api.event; + +import java.util.Map; + +import org.gephi.streaming.api.event.EdgeAddedEvent; +import org.gephi.streaming.api.event.ElementEvent; +import org.gephi.streaming.api.event.ElementType; +import org.gephi.streaming.api.event.EventType; +import org.gephi.streaming.api.event.GraphEvent; + +/** + * An event builder + * + * @author Andre' Panisson + * @see GraphEventContainer + * + */ +public class GraphEventBuilder { + + private Object source; + + /** + * @param container + */ + public GraphEventBuilder(Object source) { + this.source = source; + } + + public GraphEvent graphEvent(ElementType elementType, EventType eventType, String elementId, Map attributes) { + GraphEvent event = null; + switch (elementType) { + case NODE: + event = new ElementEvent(source, eventType, ElementType.NODE, elementId, attributes); + break; + case EDGE: + if (eventType == EventType.ADD) { + String fromNodeId = (String)attributes.remove("source"); + String toNodeId = (String)attributes.remove("target"); + Boolean directed = (Boolean)attributes.remove("directed"); + event = new EdgeAddedEvent(source, elementId, fromNodeId, toNodeId, directed, attributes); + } else { + event = new ElementEvent(source, eventType, ElementType.EDGE, elementId, attributes); + } + break; + case GRAPH: + event = new ElementEvent(source, eventType, ElementType.GRAPH, elementId, attributes); + break; + } + return event; + } + + public GraphEvent edgeAddedEvent(String edgeId, String fromNodeId, String toNodeId, + boolean directed, Map attributes) { + return new EdgeAddedEvent(source, edgeId, fromNodeId, toNodeId, directed, attributes); + } + +} diff --git a/modules/StreamingAPI/src/main/nbm/manifest.mf b/modules/StreamingAPI/src/main/nbm/manifest.mf new file mode 100644 index 0000000000..82f5b054ae --- /dev/null +++ b/modules/StreamingAPI/src/main/nbm/manifest.mf @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +OpenIDE-Module-Localizing-Bundle: org/gephi/streaming/api/Bundle.properties diff --git a/modules/StreamingAPI/src/main/resources/org/gephi/streaming/api/Bundle.properties b/modules/StreamingAPI/src/main/resources/org/gephi/streaming/api/Bundle.properties new file mode 100644 index 0000000000..f8a56e82e3 --- /dev/null +++ b/modules/StreamingAPI/src/main/resources/org/gephi/streaming/api/Bundle.properties @@ -0,0 +1,5 @@ +OpenIDE-Module-Display-Category=Gephi Core +OpenIDE-Module-Long-Description=\ + API graph streaming +OpenIDE-Module-Name=Streaming API +OpenIDE-Module-Short-Description=API graph streaming diff --git a/modules/StreamingAPI/src/main/resources/org/gephi/streaming/api/layer.xml b/modules/StreamingAPI/src/main/resources/org/gephi/streaming/api/layer.xml new file mode 100644 index 0000000000..83b7d8df3b --- /dev/null +++ b/modules/StreamingAPI/src/main/resources/org/gephi/streaming/api/layer.xml @@ -0,0 +1,4 @@ + + + + diff --git a/modules/StreamingImpl/pom.xml b/modules/StreamingImpl/pom.xml new file mode 100644 index 0000000000..4ec1cecd35 --- /dev/null +++ b/modules/StreamingImpl/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + gephi-plugin-parent + org.gephi + 0.9.0 + + + org.gephi.streaming + impl + 1.0.0 + nbm + + StreamingImpl + + + + + org.gephi + filters-api + + + org.netbeans.api + org-openide-filesystems + + + org.gephi + core-library-wrapper + + + org.netbeans.api + org-openide-util-lookup + + + org.gephi + graph-api + + + org.gephi.streaming + api + 1.0.0 + + + + + + + org.codehaus.mojo + nbm-maven-plugin + + Unknown + $sourcecode_url + + + + + + + + diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/FilterFactory.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/FilterFactory.java new file mode 100644 index 0000000000..d47d317441 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/FilterFactory.java @@ -0,0 +1,204 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ + +package org.gephi.streaming.impl; + +import java.util.Map; +import org.gephi.filters.spi.EdgeFilter; +import org.gephi.filters.spi.Filter; +import org.gephi.filters.spi.FilterProperty; +import org.gephi.filters.spi.NodeFilter; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.Node; +import org.gephi.streaming.api.event.ElementType; + +/** + * A factory to create Filter objects + * + * @author panisson + */ +public class FilterFactory { + + /** + * Create a new Filter object. The filter type depends on the element type, + * if a node + * + * @param elementType + * @param filterName + * @param parameters + * @return + */ + public Filter getFilter(ElementType elementType, String filterName, Map parameters) { + Filter filter = null; + + if (elementType.equals(ElementType.NODE)) { + filter = getNodeFilter(filterName, parameters); + } else if (elementType.equals(ElementType.EDGE)) { + filter = getEdgeFilter(filterName, parameters); + } + + return filter; + } + + /** + * Create a new NodeFilter object. + * + * @param filterName the filter's name + * @param parameters the parameters to create the filter + * @return a new NodeFilter object + */ + public NodeFilter getNodeFilter(String filterName, Map parameters) { + NodeFilter filter = null; + + if (filterName.equalsIgnoreCase("ALL")) { + filter = new AbstractNodeFilter() { + @Override + public boolean evaluate(Graph graph, Node node) { + return true; + } + }; + } + + if (filterName.equalsIgnoreCase("NodeAttribute")) { + final String attributeName = (String)parameters.get("attribute"); + final String attributeValue = parameters.get("value").toString(); + filter = new AbstractNodeFilter() { + @Override + public boolean evaluate(Graph graph, Node node) { + Object attribute = node.getAttribute(attributeName); + if (attribute==null) { + if (attributeValue==null) return true; + else return false; + } + String value = attribute.toString(); + return attributeValue.equals(value); + } + }; + } + + return filter; + } + + /** + * Create a new EdgeFilter object. + * + * @param filterName the filter's name + * @param parameters the parameters to create the filter + * @return a new EdgeFilter object + */ + public EdgeFilter getEdgeFilter(String filterName, Map parameters) { + EdgeFilter filter = null; + + if (filterName.equalsIgnoreCase("ALL")) { + filter = new AbstractEdgeFilter() { + @Override + public boolean evaluate(Graph graph, Edge edge) { + return true; + } + }; + } + + if (filterName.equalsIgnoreCase("EdgeAttribute")) { + final String attributeName = (String)parameters.get("attribute"); + final String attributeValue = parameters.get("value").toString(); + filter = new AbstractEdgeFilter() { + @Override + public boolean evaluate(Graph graph, Edge edge) { + + Object attribute = edge.getAttribute(attributeName); + if (attribute==null) { + if (attributeValue==null) return true; + else return false; + } + String value = attribute.toString(); + return attributeValue.equals(value); + } + }; + } + + return filter; + } + + private abstract class AbstractNodeFilter implements NodeFilter { + @Override + public boolean init(Graph graph) { + return true; + } + + @Override + public void finish() { + } + + @Override + public String getName() { + return "NodeFilter"; + } + + @Override + public FilterProperty[] getProperties() { + return null; + } + } + + private abstract class AbstractEdgeFilter implements EdgeFilter { + + @Override + public boolean init(Graph graph) { + return true; + } + + @Override + public void finish() { + } + + @Override + public String getName() { + return "EdgeFilter"; + } + + @Override + public FilterProperty[] getProperties() { + return null; + } + } +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/GraphEventContainer.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/GraphEventContainer.java new file mode 100644 index 0000000000..69169751fa --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/GraphEventContainer.java @@ -0,0 +1,143 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; + +import org.gephi.streaming.api.GraphEventHandler; +import org.gephi.streaming.api.Report; +import org.gephi.streaming.api.event.GraphEvent; + +/** + * @author panisson + * + */ + +public class GraphEventContainer implements GraphEventHandler { + + private LinkedBlockingQueue eventQueue = new LinkedBlockingQueue(); + + private final GraphEventHandler handler; + + private Report report; + + private boolean stopped = false; + + private Thread dispatcher; + + private Object emptyQueueLock = new Object(); + + /** + * Set the source of the data put in the container. Could be the stream's URL. + * @param source the original source of data. + * @throws NullPointerException if source is null + */ + public GraphEventContainer(GraphEventHandler handler) { + this.report = new Report(); + this.handler = handler; + + dispatcher = new Thread(new EventDispatcher(), "EventDispatcher"); + dispatcher.start(); + } + + public Report getReport() { + return this.report; + } + + public void setReport(Report report) { + this.report = report; + } + + protected void fireEvent(GraphEvent event) { + eventQueue.offer(event); + } + + /** + * @return all events still in buffer + */ + public List getAllEvents() { + return new ArrayList(this.eventQueue); + } + + public void stop() { + stopped = true; + dispatcher.interrupt(); + } + + public void waitForDispatchAllEvents() { + while (eventQueue.size() > 0) { + try { + synchronized(emptyQueueLock) { + emptyQueueLock.wait(); + } + } catch (InterruptedException e) {} + } + } + + public void handleGraphEvent(GraphEvent event) { + fireEvent(event); + } + + private class EventDispatcher implements Runnable { + + @Override + public void run() { + while (!stopped) { + try { + GraphEvent event = eventQueue.take(); + handler.handleGraphEvent(event); + } catch (InterruptedException e) { + // Container was closed + //e.printStackTrace(); + } finally { + // notify threads that are waiting for empty queue + if (eventQueue.size() == 0) + synchronized(emptyQueueLock) { + emptyQueueLock.notifyAll(); + } + } + } + } + } +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamReaderFactoryImpl.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamReaderFactoryImpl.java new file mode 100644 index 0000000000..beca24eb6a --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamReaderFactoryImpl.java @@ -0,0 +1,105 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.impl; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; + +import org.gephi.streaming.api.GraphEventHandler; +import org.gephi.streaming.api.StreamReader; +import org.gephi.streaming.api.StreamReaderFactory; +import org.gephi.streaming.api.StreamType; +import org.gephi.streaming.api.event.GraphEventBuilder; +import org.openide.util.Lookup; +import org.openide.util.lookup.ServiceProvider; + +/** + * @author panisson + * + */ +@ServiceProvider(service = StreamReaderFactory.class) +public class StreamReaderFactoryImpl implements StreamReaderFactory { + + /* (non-Javadoc) + * @see org.gephi.streaming.api.StreamProcessorFactory#createStreamProcessor(java.lang.String) + */ + @Override + public StreamReader createStreamReader(String streamType, GraphEventHandler handler, + GraphEventBuilder eventBuilder) { + Collection streamTypes = Lookup.getDefault().lookupAll(StreamType.class); + for (StreamType type: streamTypes) { + if(type.getType().equalsIgnoreCase(streamType)) { + return createStreamReader(type, handler, eventBuilder); + } + } + throw new IllegalArgumentException("Type " + streamType + " not registered as a valid stream type."); + } + + /* (non-Javadoc) + * @see org.gephi.streaming.api.StreamProcessorFactory#createStreamProcessor(java.lang.String) + */ + @Override + public StreamReader createStreamReader(StreamType streamType, GraphEventHandler handler, + GraphEventBuilder eventBuilder) { + try { + Constructor constructor = + streamType.getStreamReaderClass().getConstructor( + GraphEventHandler.class, GraphEventBuilder.class); + return constructor.newInstance(handler, eventBuilder); + + } catch (InstantiationException e) { + throw new IllegalArgumentException("Error loading stream processor for type " + streamType, e); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException("Error loading stream processor for type " + streamType, e); + } catch (SecurityException e) { + throw new IllegalArgumentException("Error loading stream processor for type " + streamType, e); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("Error loading stream processor for type " + streamType, e); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Error loading stream processor for type " + streamType, e); + } catch (InvocationTargetException e) { + throw new IllegalArgumentException("Error loading stream processor for type " + streamType, e); + } + } + +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamWriterFactoryImpl.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamWriterFactoryImpl.java new file mode 100644 index 0000000000..2628c6e713 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamWriterFactoryImpl.java @@ -0,0 +1,101 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.impl; + +import java.io.OutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; + +import org.gephi.streaming.api.StreamReader; +import org.gephi.streaming.api.StreamType; +import org.gephi.streaming.api.StreamWriter; +import org.gephi.streaming.api.StreamWriterFactory; +import org.openide.util.Lookup; +import org.openide.util.lookup.ServiceProvider; + +/** + * @author panisson + * + */ +@ServiceProvider(service = StreamWriterFactory.class) +public class StreamWriterFactoryImpl implements StreamWriterFactory { + + /* (non-Javadoc) + * @see org.gephi.streaming.api.StreamProcessorFactory#createStreamProcessor(java.lang.String) + */ + @Override + public StreamWriter createStreamWriter(String streamType, OutputStream outputStream) { + Collection streamTypes = Lookup.getDefault().lookupAll(StreamType.class); + for (StreamType type: streamTypes) { + if(type.getType().equalsIgnoreCase(streamType)) { + return createStreamWriter(type, outputStream); + } + } + throw new IllegalArgumentException("Type " + streamType + " not registered as a valid stream type."); + } + + /* (non-Javadoc) + * @see org.gephi.streaming.api.StreamProcessorFactory#createStreamProcessor(java.lang.String) + */ + @Override + public StreamWriter createStreamWriter(StreamType streamType, OutputStream outputStream) { + try { + Constructor constructor = streamType.getStreamWriterClass().getConstructor(OutputStream.class); + return constructor.newInstance(outputStream); + + } catch (InstantiationException e) { + throw new IllegalArgumentException("Error loading stream processor for type " + streamType, e); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException("Error loading stream processor for type " + streamType, e); + } catch (SecurityException e) { + throw new IllegalArgumentException("Error loading stream processor for type " + streamType, e); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("Error loading stream processor for type " + streamType, e); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Error loading stream processor for type " + streamType, e); + } catch (InvocationTargetException e) { + throw new IllegalArgumentException("Error loading stream processor for type " + streamType, e); + } + } + +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamingConnectionImpl.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamingConnectionImpl.java new file mode 100644 index 0000000000..309e790a0c --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamingConnectionImpl.java @@ -0,0 +1,244 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.impl; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Authenticator; +import java.net.HttpURLConnection; +import java.net.PasswordAuthentication; +import java.net.URL; +import java.net.URLConnection; +import java.nio.channels.ReadableByteChannel; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.apache.commons.codec.binary.Base64; +import org.gephi.streaming.api.Report; +import org.gephi.streaming.api.StreamReader; +import org.gephi.streaming.api.StreamingConnection; +import org.gephi.streaming.api.StreamingEndpoint; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; + +/** + * + * @author panisson + */ +public class StreamingConnectionImpl implements StreamingConnection { + + private static final Logger logger = Logger.getLogger(StreamingConnectionImpl.class.getName()); + + private final StreamingEndpoint endpoint; + private final StreamReader streamReader; + private URLConnection connection; + private ReadableByteChannel channel; + private final List listeners = + Collections.synchronizedList(new ArrayList()); + private boolean closed = false; + private final Report report; + + public StreamingConnectionImpl(final StreamingEndpoint endpoint, + final StreamReader streamReader, Report report) throws IOException { + this.endpoint = endpoint; + this.streamReader = streamReader; + this.report = report; + + // Workaround to avoid invalid certificate problem + if (endpoint.getUrl().getProtocol().equalsIgnoreCase("https")) { + FileObject certs = FileUtil.getConfigFile("cacerts"); + if (certs == null) { + certs = FileUtil.getConfigRoot().createData("cacerts"); + OutputStream os = certs.getOutputStream(); + FileUtil.copy(this.getClass().getResourceAsStream("cacerts"), os); + os.flush(); + os.close(); + } + System.setProperty("javax.net.ssl.trustStore", FileUtil.toFile(certs).getAbsolutePath()); + System.setProperty("javax.net.ssl.trustStorePassword", "1234567890"); + } + + connection = endpoint.getUrl().openConnection(); + + if (endpoint.getUser()!=null && endpoint.getUser().length()>0) { + + // Workaround for Bug https://issues.apache.org/jira/browse/CODEC-89 + String base64Encoded = Base64.encodeBase64String((endpoint.getUser()+":"+endpoint.getPassword()).getBytes()); + base64Encoded = base64Encoded.replaceAll("\r\n?", ""); + + connection.setRequestProperty("Authorization", "Basic " + base64Encoded); + + // this option is not optimal, as it sets the same authenticator for all connections +// Authenticator.setDefault(new Authenticator() { +// @Override +// protected PasswordAuthentication getPasswordAuthentication() { +// return new PasswordAuthentication (endpoint.getUser(), endpoint.getPassword().toCharArray()); +// } +// }); + } + + + connection.connect(); + + } + + @Override + public StreamingEndpoint getStreamingEndpoint() { + return endpoint; + } + + @Override + public void asynchProcess() { + new Thread("StreamingConnection["+endpoint.getUrl().toString()+"]") { + @Override + public void run() { + process(); + } + }.start(); + } + + @Override + public boolean isClosed() { + return closed; + } + + @Override + public synchronized void close() throws IOException { + + if (connection!=null) { + if (connection instanceof HttpURLConnection) { + ((HttpURLConnection)connection).disconnect(); + } + + if (connection.getDoOutput()) { + connection.getOutputStream().close(); + } + if (connection.getDoInput()) { + connection.getInputStream().close(); + } + } + + if (channel != null) { + channel.close(); + } + + closed = true; + } + + @Override + public void addStatusListener(StatusListener listener) { + this.listeners.add(listener); + } + + @Override + public void removeStatusListener(StatusListener listener) { + this.listeners.remove(listener); + } + + @Override + public void process() { + try { + + InputStream inputStream = connection.getInputStream(); + streamReader.setStatusListener(new StreamReader.StreamReaderStatusListener() { + + @Override + public void onStreamClosed() { + synchronized (listeners) { + for (StatusListener listener: listeners) + if (listener != null) { + listener.onConnectionClosed(StreamingConnectionImpl.this); + } + } + } + + @Override + public void onDataReceived() { + synchronized (listeners) { + for (StatusListener listener: listeners) + if (listener != null) { + listener.onDataReceived(StreamingConnectionImpl.this); + } + } + } + + @Override + public void onError() { + synchronized (listeners) { + for (StatusListener listener: listeners) + if (listener != null) { + listener.onError(StreamingConnectionImpl.this); + } + } + } + }); + + streamReader.processStream(inputStream); + + } catch (IOException e) { + logger.log(Level.INFO, null, e); + } catch (RuntimeException e) { + // Exception during processing + logger.log(Level.INFO, null, e); + } finally { + closed = true; + + synchronized (listeners) { + for (StatusListener listener: listeners) + if (listener != null) { + listener.onConnectionClosed(this); + } + } + } + } + + /** + * @return the report + */ + public Report getReport() { + return report; + } + +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamingControllerImpl.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamingControllerImpl.java new file mode 100644 index 0000000000..6337f4fdeb --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamingControllerImpl.java @@ -0,0 +1,279 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ + +package org.gephi.streaming.impl; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.Authenticator; +import java.net.HttpURLConnection; +import java.net.PasswordAuthentication; +import java.net.URL; +import java.net.URLConnection; +import java.net.UnknownServiceException; +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.apache.commons.codec.binary.Base64; +import org.gephi.data.attributes.api.AttributeController; +import org.gephi.graph.api.Graph; +import org.gephi.streaming.api.Graph2EventListener; +import org.gephi.streaming.api.GraphEventHandler; +import org.gephi.streaming.api.StreamingEndpoint; +import org.gephi.streaming.api.GraphUpdaterEventHandler; +import org.gephi.streaming.api.Report; +import org.gephi.streaming.api.StreamReader; +import org.gephi.streaming.api.StreamReaderFactory; +import org.gephi.streaming.api.StreamType; +import org.gephi.streaming.api.StreamWriter; +import org.gephi.streaming.api.StreamWriterFactory; +import org.gephi.streaming.api.StreamingConnection; +import org.gephi.streaming.api.StreamingController; +import org.gephi.streaming.api.event.ElementEvent; +import org.gephi.streaming.api.event.ElementType; +import org.gephi.streaming.api.event.EventType; +import org.gephi.streaming.api.event.GraphEvent; +import org.gephi.streaming.api.event.GraphEventBuilder; +import org.openide.util.Lookup; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author panisson + */ +@ServiceProvider(service = StreamingController.class) +public class StreamingControllerImpl implements StreamingController { + + private static final Logger logger = Logger.getLogger(StreamingControllerImpl.class.getName()); + + @Override + public StreamType getStreamType(String streamType) { + Collection streamTypes = Lookup.getDefault().lookupAll(StreamType.class); + for (StreamType type: streamTypes) { + if(type.getType().equalsIgnoreCase(streamType)) { + return type; + } + } + return null; + } + + @Override + public StreamingConnection connect(StreamingEndpoint endpoint, Graph graph) throws IOException { + return connect(endpoint, graph, new Report()); + } + + @Override + public StreamingConnection connect(StreamingEndpoint endpoint, final Graph graph, + Report report) throws IOException { + logger.log(Level.FINE, "Connecting to url {0}", endpoint.getUrl().toString()); + + final Set filterededIds = new HashSet(); + + // Register listener for graph events + + ClientEventHandler handler = new ClientEventHandler(endpoint, filterededIds); + final Graph2EventListener graph2EventListener = new Graph2EventListener(graph, handler); + + graph.getModel().addGraphListener(graph2EventListener); + AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); + ac.getModel().addAttributeListener(graph2EventListener); + + if (report!=null) { + report.setSource(endpoint.getUrl().toString()); + } + + // Connect to stream + + final GraphUpdaterEventHandler graphUpdaterHandler = new GraphUpdaterEventHandler(graph); + graphUpdaterHandler.setReport(report); + GraphEventHandler filteredHandler = new GraphEventHandler() { + + @Override + public void handleGraphEvent(GraphEvent event) { + logger.log(Level.INFO, "Received event {0}", event.toString()); + if (event instanceof ElementEvent) { + ElementEvent elementEvent = (ElementEvent)event; + if (elementEvent.getElementId()!=null) { + filterededIds.add(new FilteredEventEntry(elementEvent.getElementId(), elementEvent.getElementType(), 0)); + } + } + + graphUpdaterHandler.handleGraphEvent(event); + } + }; + + final GraphEventContainer container = new GraphEventContainer(filteredHandler); + GraphEventBuilder eventBuilder = new GraphEventBuilder(endpoint.getUrl()); + StreamReaderFactory readerFactory = + Lookup.getDefault().lookup(StreamReaderFactory.class); + StreamReader reader = + readerFactory.createStreamReader(endpoint.getStreamType(), container, eventBuilder); + reader.setReport(report); + StreamingConnection connection = new StreamingConnectionImpl(endpoint, reader, report); + connection.addStatusListener( + new StreamingConnection.StatusListener() { + + @Override + public void onConnectionClosed(StreamingConnection connection) { + graph.getModel().removeGraphListener(graph2EventListener); + AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); + ac.getModel().removeAttributeListener(graph2EventListener); + + container.waitForDispatchAllEvents(); + container.stop(); + + logger.log(Level.INFO, "Connecion closed"); + } + + @Override + public void onDataReceived(StreamingConnection connection) { } + @Override + public void onError(StreamingConnection connection) { } + }); + + logger.log(Level.INFO, "Connected to url {0}", endpoint.getUrl().toString()); + + return connection; + } + + private class ClientEventHandler implements GraphEventHandler { + + private StreamingEndpoint endpoint; + private Set filteredEvents; + + public ClientEventHandler(StreamingEndpoint endpoint, Set filterededEvents) { + this.endpoint = endpoint; + this.filteredEvents = filterededEvents; + } + + @Override + public void handleGraphEvent(GraphEvent event) { + logger.log(Level.INFO, "{0};{1};Sending event {2}", + new Object[]{Thread.currentThread().getId(), System.currentTimeMillis(), event.toString()}); + + if (event instanceof ElementEvent) { + ElementEvent elementEvent = (ElementEvent)event; + FilteredEventEntry entry = new FilteredEventEntry(elementEvent.getElementId(), elementEvent.getElementType(), 0); + if (!filteredEvents.contains(entry)) { + sendEvent(endpoint, event); + } else { + filteredEvents.remove(entry); + } + } + } + } + + private void sendEvent(final StreamingEndpoint endpoint, GraphEvent event) { + logger.log(Level.FINE, "Sending event {0}", event.toString()); + try { + URL url = new URL(endpoint.getUrl(), + endpoint.getUrl().getFile()+"?operation=updateGraph&format=" + + endpoint.getStreamType().getType()); + + URLConnection connection = url.openConnection(); + + // Workaround for Bug https://issues.apache.org/jira/browse/CODEC-89 + String base64Encoded = Base64.encodeBase64String((endpoint.getUser()+":"+endpoint.getPassword()).getBytes()); + base64Encoded = base64Encoded.replaceAll("\r\n?", ""); + + connection.setRequestProperty("Authorization", "Basic " + base64Encoded); + + connection.setDoOutput(true); + connection.connect(); + + StreamWriterFactory writerFactory = + Lookup.getDefault().lookup(StreamWriterFactory.class); + + OutputStream out = null; + try { + out = connection.getOutputStream(); + } catch (UnknownServiceException e) { + // protocol doesn't support output + return; + } + StreamWriter writer = writerFactory.createStreamWriter(endpoint.getStreamType(), out); + writer.handleGraphEvent(event); + out.flush(); + out.close(); + connection.getInputStream().close(); + + } catch (IOException ex) { + logger.log(Level.FINE, null, ex); + } + } + + private class FilteredEventEntry { + + private final String elementId; + private final ElementType elementType; + private final long timestamp; + + public FilteredEventEntry(String elementId, ElementType elementType, long timestamp) { + this.elementId = elementId; + this.elementType = elementType; + if (timestamp <= 0) { + this.timestamp = System.currentTimeMillis(); + } else { + this.timestamp = timestamp; + } + } + + @Override + public boolean equals(Object obj) { + if ( this == obj ) return true; + if ( obj == null || obj.getClass() != this.getClass() ) return false; + + FilteredEventEntry e = (FilteredEventEntry)obj; + return this.elementType == e.elementType + && this.elementId.equals(e.elementId); + } + + @Override + public int hashCode() { + return this.elementType.hashCode() * 31 + this.elementId.hashCode(); + } + } + +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/BaseParser.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/BaseParser.java new file mode 100644 index 0000000000..9ee778b1b1 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/BaseParser.java @@ -0,0 +1,811 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.impl.dgs; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StreamTokenizer; + +/** + * Ths parser is based on the DGS parser implementation + * available in the GraphStream project: + * http://graphstream.sourceforge.net/ + *
+ *
+ * Copyright 2006 - 2009
+ * Julien Baudry
+ * Antoine Dutot
+ * Yoann Pigné
+ * Guilhelm Savin
+ * + */ +public abstract class BaseParser { + + protected final StreamTokenizer st; + /** + * The quote character. Can be changed in descendants. + */ + protected int QUOTE_CHAR = '"'; + + /** + * The comment character. Can be changed in descendants. + */ + protected int COMMENT_CHAR = '#'; + + /** + * Is EOL significant?. + */ + protected boolean eol_is_significant = true; + + @SuppressWarnings("deprecation") + public BaseParser(InputStream inputStream) { + // the InputStream constructor is better, as it does not block until the end of stream +// StreamTokenizer st = new StreamTokenizer( new InputStreamReader(inputStream) ); + StreamTokenizer st = new StreamTokenizer( inputStream ); + + st.commentChar(COMMENT_CHAR); + st.eolIsSignificant(eol_is_significant); + st.parseNumbers(); + st.wordChars( '_', '_' ); + + this.st = st; + } + + /** + * Push back the last read thing, so that it can be read anew. This allows + * to explore one token ahead, and if not corresponding to what is expected, + * go back. + */ + protected void pushBack() + { + st.pushBack(); + } + + /** + * Read EOF or report garbage at end of file. + */ + protected void eatEof() throws IOException + { + int tok = st.nextToken(); + + if( tok != StreamTokenizer.TT_EOF ) + parseError( "garbage at end of file, expecting EOF, " + + gotWhat( tok ) ); + } + + /** + * Read EOL. + */ + protected void eatEol() throws IOException + { + int tok = st.nextToken(); + + if( tok != StreamTokenizer.TT_EOL ) + parseError( "expecting EOL, " + gotWhat( tok ) ); + } + + /** + * Read EOL or EOF. + * @return The token read StreamTokenizer.TT_EOL or StreamTokenizer.TT_EOF. + */ + protected int eatEolOrEof() throws IOException + { + int tok = st.nextToken(); + + if( tok != StreamTokenizer.TT_EOL && tok != StreamTokenizer.TT_EOF ) + parseError( "expecting EOL or EOF, " + gotWhat( tok ) ); + + return tok; + } + + /** + * Read an expected word token or generate a parse error. + */ + protected void eatWord( String word ) throws IOException + { + int tok = st.nextToken(); + + if( tok != StreamTokenizer.TT_WORD ) + parseError( "expecting `" + word + "', " + gotWhat( tok ) ); + + if( !st.sval.equals( word ) ) + parseError( "expecting `" + word + "' got `" + st.sval + "'" ); + } + + /** + * Read an expected word among the given word list or generate a parse error. + * @param words The expected words. + */ + protected void eatWords( String...words ) + throws IOException + { + int tok = st.nextToken(); + + if( tok != StreamTokenizer.TT_WORD ) + parseError( "expecting one of `" + words + "', " + gotWhat( tok ) ); + + boolean found = false; + + for( String word: words ) + { + if( st.sval.equals( word ) ) + { + found = true; + break; + } + } + + if( ! found ) + parseError( "expecting one of `" + words + ", got `" + st.sval + "'" ); + } + + /** + * Eat either a word or another, and return the eated one. + * + * @param word1 The first word to eat. + * @param word2 The alternative word to eat. + * @return The word eaten. + */ + protected String eatOneOfTwoWords( String word1, String word2 ) + throws IOException + { + int tok = st.nextToken(); + + if( tok != StreamTokenizer.TT_WORD ) + parseError( "expecting `" + word1 + "' or `" + word2 + "', " + + gotWhat( tok ) ); + + if( st.sval.equals( word1 ) ) + return word1; + + if( st.sval.equals( word2 ) ) + return word2; + + parseError( "expecting `" + word1 + "' or `" + word2 + "' got `" + + st.sval + "'" ); + return null; + } + + /** + * Eat the expected symbol or generate a parse error. + */ + protected void eatSymbol( char symbol ) throws IOException + { + int tok = st.nextToken(); + + if( tok != symbol ) + parseError( "expecting symbol `" + symbol + "', " + gotWhat( tok ) ); + } + + /** + * Eat one of the list of expected symbols or generate a + * parse error none of symbols can be found. + */ + protected void eatSymbols( String symbols ) throws IOException + { + int tok = st.nextToken(); + int n = symbols.length(); + boolean f = false; + + for( int i = 0; i < n; ++i ) + { + if( tok == symbols.charAt( i ) ) + { + f = true; + i = n; + } + } + + if( !f ) + parseError( "expecting one of symbols `" + symbols + "', " + + gotWhat( tok ) ); + } + + /** + * Eat the expected word or push back what was read so that + * it can be read anew. + */ + protected void eatWordOrPushbak( String word ) throws IOException + { + int tok = st.nextToken(); + + if( tok != StreamTokenizer.TT_WORD ) + pushBack(); + + if( !st.sval.equals( word ) ) + pushBack(); + } + + /** + * Eat the expected symbol or push back what was read so that + * it can be read anew. + */ + protected void eatSymbolOrPushback( char symbol ) throws IOException + { + int tok = st.nextToken(); + + if( tok != symbol ) + pushBack(); + } + + /** + * Eat all until an EOL is found. The EOL is also eaten. This works only if + * EOL is significant (else it does nothing). + */ + protected void eatAllUntilEol() throws IOException + { + if( !eol_is_significant ) + return; + + int tok = st.nextToken(); + + if( tok == StreamTokenizer.TT_EOF ) + return; + + while( ( tok != StreamTokenizer.TT_EOL ) + && ( tok != StreamTokenizer.TT_EOF ) ) + { + tok = st.nextToken(); + } + } + + /** + * Eat all availables EOLs. + */ + protected void eatAllEols() throws IOException + { + if( !eol_is_significant ) + return; + + int tok = st.nextToken(); + + while( tok == StreamTokenizer.TT_EOL ) + tok = st.nextToken(); + + pushBack(); + } + + /** + * Read a word or generate a parse error. + */ + protected String getWord() throws IOException + { + int tok = st.nextToken(); + + if( tok != StreamTokenizer.TT_WORD ) + parseError( "expecting a word, " + gotWhat( tok ) ); + + return st.sval; + } + + /** + * Get a symbol. + */ + protected char getSymbol() throws IOException + { + int tok = st.nextToken(); + + if( tok > 0 && tok != StreamTokenizer.TT_WORD + && tok != StreamTokenizer.TT_NUMBER + && tok != StreamTokenizer.TT_EOL + && tok != StreamTokenizer.TT_EOF && tok != QUOTE_CHAR + && tok != COMMENT_CHAR ) + { + return (char) tok; + } + + parseError( "expecting a symbol, " + gotWhat( tok ) ); + return (char) 0; // Never reached. + } + + /** + * Get a symbol or push back what was read so that it can be read anew. If + * no symbol is found, 0 is returned. + */ + protected char getSymbolOrPushback() throws IOException + { + int tok = st.nextToken(); + + if( tok > 0 && tok != StreamTokenizer.TT_WORD + && tok != StreamTokenizer.TT_NUMBER + && tok != StreamTokenizer.TT_EOL + && tok != StreamTokenizer.TT_EOF && tok != QUOTE_CHAR + && tok != COMMENT_CHAR ) + { + return (char) tok; + } + + pushBack(); + + return (char) 0; + } + + /** + * Read a string constant (between quotes) or generate a parse error. Return + * the content of the string without the quotes. + */ + protected String getString() throws IOException + { + int tok = st.nextToken(); + + if( tok != QUOTE_CHAR ) + parseError( "expecting a string constant, " + gotWhat( tok ) ); + + return st.sval; + } + + /** + * Read a word or number or generate a parse error. If it is a number it is + * converted to a string before being returned. + */ + protected String getWordOrNumber() throws IOException + { + int tok = st.nextToken(); + + if( tok != StreamTokenizer.TT_WORD && tok != StreamTokenizer.TT_NUMBER ) + parseError( "expecting a word or number, " + gotWhat( tok ) ); + + if( tok == StreamTokenizer.TT_NUMBER ) + { + // If st.nval is an integer, as it is stored into a double, + // toString() will transform it by automatically adding ".0", we + // prevent this. The tokenizer does not allow to read integers. + + if( ( st.nval - ( (int) st.nval ) ) == 0 ) + return Integer.toString( (int) st.nval ); + else + return Double.toString( st.nval ); + } + else + { + return st.sval; + } + } + + /** + * Read a string or number or generate a parse error. If it is a number it + * is converted to a string before being returned. + */ + protected String getStringOrNumber() throws IOException + { + int tok = st.nextToken(); + + if( tok != QUOTE_CHAR && tok != StreamTokenizer.TT_NUMBER ) + parseError( "expecting a string constant or a number, " + + gotWhat( tok ) ); + + if( tok == StreamTokenizer.TT_NUMBER ) + { + if( ( st.nval - ( (int) st.nval ) ) == 0 ) + return Integer.toString( (int) st.nval ); + else + return Double.toString( st.nval ); + } + else + { + return st.sval; + } + } + + /** + * Read a string or number or generate a parse error. If it is a number it + * is converted to a string before being returned. + */ + protected String getStringOrWordOrNumber() throws IOException + { + int tok = st.nextToken(); + + if( tok == StreamTokenizer.TT_EOL || tok == StreamTokenizer.TT_EOF ) + parseError( "expecting word, string or number, " + gotWhat( tok ) ); + + if( tok == StreamTokenizer.TT_NUMBER ) + { + if( ( st.nval - ( (int) st.nval ) ) == 0 ) + return Integer.toString( (int) st.nval ); + else + return Double.toString( st.nval ); + } + else + { + return st.sval; + } + } + + /** + * Read a string or number or generate a parse error. The returned value + * is converted to a Number of a String depending on its type. + */ + protected Object getStringOrWordOrNumberO() throws IOException + { + int tok = st.nextToken(); + + if( tok == StreamTokenizer.TT_EOL || tok == StreamTokenizer.TT_EOF ) + parseError( "expecting word, string or number, " + gotWhat( tok ) ); + + if( tok == StreamTokenizer.TT_NUMBER ) + { + return st.nval; + } + else + { + return st.sval; + } + } + + /** + * Read a string or number or generate a parse error. The returned value + * is converted to a Number of a String depending on its type. + */ + protected Object getStringOrWordOrSymbolOrNumberO() throws IOException + { + int tok = st.nextToken(); + + if( tok == StreamTokenizer.TT_EOL || tok == StreamTokenizer.TT_EOF ) + parseError( "expecting word, string or number, " + gotWhat( tok ) ); + + if( tok == StreamTokenizer.TT_NUMBER ) + { + return st.nval; + } + else if( tok == StreamTokenizer.TT_WORD ) + { + return st.sval; + } + else return Character.toString( (char) tok ); + } + + /** + * Read a word or string or generate a parse error. + */ + protected String getWordOrString() throws IOException + { + int tok = st.nextToken(); + + if( tok == StreamTokenizer.TT_WORD || tok == QUOTE_CHAR ) + return st.sval; + + parseError( "expecting a word or string, " + gotWhat( tok ) ); + return null; + } + + /** + * Read a word or symbol or generate a parse error. + */ + protected String getWordOrSymbol() throws IOException + { + int tok = st.nextToken(); + + if( tok == StreamTokenizer.TT_NUMBER || tok == QUOTE_CHAR + || tok == StreamTokenizer.TT_EOF ) + parseError( "expecting a word or symbol, " + gotWhat( tok ) ); + + if( tok == StreamTokenizer.TT_WORD ) + return st.sval; + else + return Character.toString( (char) tok ); + } + + /** + * Read a word or symbol or push back the read thing so that it is readable + * anew. In the second case, null is returned. + */ + protected String getWordOrSymbolOrPushback() throws IOException + { + int tok = st.nextToken(); + + if( tok == StreamTokenizer.TT_NUMBER || tok == QUOTE_CHAR + || tok == StreamTokenizer.TT_EOF ) + { + pushBack(); + return null; + } + + if( tok == StreamTokenizer.TT_WORD ) + return st.sval; + else + return Character.toString( (char) tok ); + } + + /** + * Read a word or symbol or string or generate a parse error. + */ + protected String getWordOrSymbolOrString() throws IOException + { + int tok = st.nextToken(); + + if( tok == StreamTokenizer.TT_NUMBER || tok == StreamTokenizer.TT_EOF ) + parseError( "expecting a word, symbol or string, " + gotWhat( tok ) ); + + if( tok == QUOTE_CHAR ) + return st.sval; + + if( tok == StreamTokenizer.TT_WORD ) + return st.sval; + else + return Character.toString( (char) tok ); + } + + /** + * Read a word or symbol or string or number or generate a parse error. + */ + protected String getAllExceptedEof() throws IOException + { + int tok = st.nextToken(); + + if( tok == StreamTokenizer.TT_EOF ) + parseError( "expecting all excepted EOF, " + gotWhat( tok ) ); + + if( tok == StreamTokenizer.TT_NUMBER || tok == StreamTokenizer.TT_EOF ) + { + if( ( st.nval - ( (int) st.nval ) ) == 0 ) + return Integer.toString( (int) st.nval ); + else + return Double.toString( st.nval ); + } + + if( tok == QUOTE_CHAR ) + return st.sval; + + if( tok == StreamTokenizer.TT_WORD ) + return st.sval; + else + return Character.toString( (char) tok ); + } + + /** + * Read a word, a symbol or EOF, or generate a parse error. If this is EOF, + * the string "EOF" is returned. + */ + protected String getWordOrSymbolOrEof() throws IOException + { + int tok = st.nextToken(); + + if( tok == StreamTokenizer.TT_NUMBER || tok == QUOTE_CHAR ) + parseError( "expecting a word or symbol, " + gotWhat( tok ) ); + + if( tok == StreamTokenizer.TT_WORD ) + return st.sval; + else if( tok == StreamTokenizer.TT_EOF ) + return "EOF"; + else + return Character.toString( (char) tok ); + } + + /** + * Read a word or symbol or string or EOL/EOF or generate a parse error. + * If EOL is read the "EOL" string is returned. If EOF is read the "EOF" + * string is returned. + * @return A string. + */ + protected String getWordOrSymbolOrStringOrEolOrEof() throws IOException + { + int tok = st.nextToken(); + + if( tok == StreamTokenizer.TT_NUMBER ) + parseError( "expecting a word, symbol or string, " + gotWhat( tok ) ); + + if( tok == QUOTE_CHAR ) + return st.sval; + + if( tok == StreamTokenizer.TT_WORD ) + return st.sval; + + if( tok == StreamTokenizer.TT_EOF ) + return "EOF"; + + if( tok == StreamTokenizer.TT_EOL ) + return "EOL"; + + return Character.toString( (char) tok ); + } + + /** + * Read a word or number or string or EOL/EOF or generate a parse error. + * If EOL is read the "EOL" string is returned. If EOF is read the "EOF" + * string is returned. If a number is returned, it is converted to a string + * as follows: if it is an integer, only the integer part is converted to a + * string without dot or comma and no leading zeros. If it is a float the + * fractional part is also converted and the dot is used as separator. + * @return A string. + */ + protected String getWordOrNumberOrStringOrEolOrEof() throws IOException + { + int tok = st.nextToken(); + + if( tok == StreamTokenizer.TT_NUMBER ) + { + if( st.nval - ((int)st.nval) != 0 ) + return Double.toString( st.nval ); + + return Integer.toString( (int) st.nval ); + } + + if( tok == QUOTE_CHAR ) + return st.sval; + + if( tok == StreamTokenizer.TT_WORD ) + return st.sval; + + if( tok == StreamTokenizer.TT_EOF ) + return "EOF"; + + if( tok == StreamTokenizer.TT_EOL ) + return "EOL"; + + parseError( "expecting a word, a number, a string, EOL or EOF, " + gotWhat( tok ) ); + return null; // Never happen, parseError throws unconditionally an exception. + } + + /** + * Read a word or string or EOL/EOF or generate a parse error. + * If EOL is read the "EOL" string is returned. If EOF is read the "EOF" + * string is returned. + * @return A string. + */ + protected String getWordOrStringOrEolOrEof() throws IOException + { + int tok = st.nextToken(); + + if( tok == StreamTokenizer.TT_WORD ) + return st.sval; + + if( tok == QUOTE_CHAR ) + return st.sval; + + if( tok == StreamTokenizer.TT_EOL ) + return "EOL"; + + if( tok == StreamTokenizer.TT_EOF ) + return "EOF"; + + parseError( "expecting a word, a string, EOL or EOF, " + gotWhat( tok ) ); + return null; // Never happen, parseError throws unconditionally an exception. + } + + // Ordre: Word | String | Symbol | Number | Eol | Eof + + /** + * Read a word or number or string or EOL/EOF or generate a parse error. + * If EOL is read the "EOL" string is returned. If EOF is read the "EOF" + * string is returned. If a number is returned, it is converted to a string + * as follows: if it is an integer, only the integer part is converted to a + * string without dot or comma and no leading zeros. If it is a float the + * fractional part is also converted and the dot is used as separator. + * @return A string. + */ + protected String getWordOrSymbolOrNumberOrStringOrEolOrEof() throws IOException + { + int tok = st.nextToken(); + + if( tok == StreamTokenizer.TT_NUMBER ) + { + if( st.nval - ((int)st.nval) != 0 ) + return Double.toString( st.nval ); + + return Integer.toString( (int) st.nval ); + } + + if( tok == QUOTE_CHAR ) + return st.sval; + + if( tok == StreamTokenizer.TT_WORD ) + return st.sval; + + if( tok == StreamTokenizer.TT_EOF ) + return "EOF"; + + if( tok == StreamTokenizer.TT_EOL ) + return "EOL"; + + return Character.toString( (char) tok ); + } + + /** + * Read a number or generate a parse error. + */ + protected double getNumber() throws IOException + { + int tok = st.nextToken(); + + if( tok != StreamTokenizer.TT_NUMBER ) + parseError( "expecting a number, " + gotWhat( tok ) ); + + return st.nval; + } + + /** + * Read a number (possibly with an exponent) or generate a parse error. + */ + protected double getNumberExp() throws IOException + { + int tok = st.nextToken(); + + if( tok != StreamTokenizer.TT_NUMBER ) + parseError( "expecting a number, " + gotWhat( tok ) ); + + double nb = st.nval; + + tok = st.nextToken(); + + if( tok == StreamTokenizer.TT_WORD + && ( st.sval.startsWith( "e-" ) || st.sval.startsWith( "e+" ) ) ) + { + double exp = Double.parseDouble( st.sval.substring( 2 ) ); + return Math.pow( nb, exp ); + } + else + { + st.pushBack(); + } + + return nb; + } + + /** + * Return a string containing "got " then the content of the current + * token. + */ + protected String gotWhat( int token ) + { + switch( token ) + { + case StreamTokenizer.TT_NUMBER : + return "got number `" + st.nval + "'"; + case StreamTokenizer.TT_WORD : + return "got word `" + st.sval + "'"; + case StreamTokenizer.TT_EOF : + return "got EOF"; + default: + if( token == QUOTE_CHAR ) + return "got string constant `" + st.sval + "'"; + else + return "unknown symbol `" + token + "' (" + ( (char) token ) + + ")"; + } + } + + /** + * Generate a parse error. + */ + protected abstract void parseError( String message ) throws IOException; + +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSParser.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSParser.java new file mode 100644 index 0000000000..b630bb1131 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSParser.java @@ -0,0 +1,471 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.impl.dgs; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StreamTokenizer; +import java.util.ArrayList; +import java.util.HashMap; +import org.gephi.streaming.api.Issue; +import org.gephi.streaming.api.Report; +import org.gephi.streaming.api.StreamReader.StreamReaderStatusListener; + + +/** + * Ths parser is based on the DGS parser implementation + * available in the GraphStream project: + * http://graphstream.sourceforge.net/ + *
+ *
+ * Copyright 2006 - 2009
+ * Julien Baudry
+ * Antoine Dutot
+ * Yoann Pigné
+ * Guilhelm Savin
+ * + */ +public class DGSParser extends BaseParser { + + /** + * Format version. + */ + protected int version; + + /** + * Name of the graph. + */ + protected String graphName; + + /** + * Number of step given in the header. + */ + protected int stepCountAnnounced; + + /** + * Number of events given in the header. + */ + protected int eventCountAnnounced; + + /** + * An attribute set used everywhere. + */ + protected HashMap attributes = new HashMap(); + + /** + * True as soon as the end of file is reached. + */ + protected boolean finished; + + private DGSParserListener listener; + private StreamReaderStatusListener statusListener; + private Report report; + + public DGSParser(InputStream inputStream, DGSParserListener listener, Report report, StreamReaderStatusListener statusListener) { + super(inputStream); + this.listener = listener; + this.statusListener = statusListener; + this.report = report; + } + + public void parse() throws IOException { + this.begin(); + while(this.next(false, false)) {} + } + + protected void begin() throws IOException + { + st.parseNumbers(); + eatWords( "DGS003", "DGS004" ); + + version = 3; + + eatEol(); + graphName = getWordOrString(); + stepCountAnnounced = (int)getNumber();//Integer.parseInt( getWord() ); + eventCountAnnounced = (int)getNumber();//Integer.parseInt( getWord() ); + eatEol(); + + if( graphName != null ) { + HashMap attributes = new HashMap(); + listener.onGraphChanged( attributes ); + } + + else graphName = "DGS_"; + + graphName = String.format( "%s_%d", graphName, System.currentTimeMillis()+((long)Math.random()*10) ); + } + + /** + * Read either one event or several. + * @param readSteps If true, read several events (usually starting with a + * step event, but it may be preceded by other events), until + * another step is encountered. + * @param stop If true stop at the next step encountered (and push it back + * so that is is readable at the next call to this method). + * @return True if it remains things to read. + */ + protected boolean next( boolean readSteps, boolean stop ) throws IOException + { + String key = null; + boolean loop = readSteps; + + // Sorted in probability of appearance ... + + do + { + key = getWordOrSymbolOrStringOrEolOrEof(); + + if( key.equals( "ce" ) ) + { + readCE(); + } + else if( key.equals( "cn" ) ) + { + readCN(); + } + else if( key.equals( "ae" ) ) + { + readAE(); + } + else if( key.equals( "an" ) ) + { + readAN(); + } + else if( key.equals( "de" ) ) + { + readDE(); + } + else if( key.equals( "dn" ) ) + { + readDN(); + } + else if( key.equals( "cg" ) ) + { + readCG(); + } + else if( key.equals( "st" ) ) + { + if( readSteps ) + { + if( stop ) + { + loop = false; + pushBack(); + } + else + { + stop = true; + readST(); + } + } + else + { + readST(); + } + } + else if( key.equals( "#" ) ) + { + eatAllUntilEol(); + return next( readSteps, stop ); + } + else if( key.equals( "EOL" ) ) + { + // Probably an empty line. + // NOP + return next( readSteps, stop ); + } + else if( key.equals( "EOF" ) ) + { + finished = true; + return false; + } + else + { + parseError( "unknown token '"+key+"'" ); + } + + if(statusListener!=null) statusListener.onDataReceived(); + } + while( loop ); + + return true; + } + + protected void readCE() throws IOException + { + String tag = getStringOrWordOrNumber(); + + readAttributes( attributes ); + + listener.onEdgeChanged( graphName, tag, new HashMap(attributes) ); + + if( eatEolOrEof() == StreamTokenizer.TT_EOF ) + pushBack(); + } + + protected void readCN() throws IOException + { + String tag = getStringOrWordOrNumber(); + + readAttributes( attributes ); + + listener.onNodeChanged( graphName, tag, new HashMap(attributes) ); + + if( eatEolOrEof() == StreamTokenizer.TT_EOF ) + pushBack(); + } + + protected void readCG() throws IOException + { + readAttributes( attributes ); + + listener.onGraphChanged(new HashMap(attributes)); + + if( eatEolOrEof() == StreamTokenizer.TT_EOF ) + pushBack(); + } + + protected void readAE() throws IOException + { + int dir = 0; + boolean directed = false; + String dirc = null; + String tag = null; + String fromTag = null; + String toTag = null; + + tag = getStringOrWordOrNumber(); + fromTag = getStringOrWordOrNumber(); + dirc = getWordOrSymbolOrNumberOrStringOrEolOrEof(); + + if( dirc.equals( ">" ) ) + { + directed = true; + dir = 1; + } + else if( dirc.equals( "<" ) ) + { + directed = true; + dir = 2; + } + else + { + pushBack(); + } + + toTag = getStringOrWordOrNumber(); + + if( dir == 2 ) + { + String tmp = toTag; + toTag = fromTag; + fromTag = tmp; + } + + readAttributes( attributes ); + listener.onEdgeAdded( graphName, tag, fromTag, toTag, directed, new HashMap(attributes) ); + + if( eatEolOrEof() == StreamTokenizer.TT_EOF ) + pushBack(); + } + + protected void readAN() throws IOException + { + String tag = getStringOrWordOrNumber(); + + readAttributes( attributes ); + + listener.onNodeAdded( graphName, tag, new HashMap(attributes) ); + + if( eatEolOrEof() == StreamTokenizer.TT_EOF ) + pushBack(); + } + + protected void readDE() throws IOException + { + String tag = getStringOrWordOrNumber(); + + listener.onEdgeRemoved( graphName, tag ); + + if( eatEolOrEof() == StreamTokenizer.TT_EOF ) + pushBack(); + } + + protected void readDN() throws IOException + { + String tag = getStringOrWordOrNumber(); + + listener.onNodeRemoved( graphName, tag ); + + if( eatEolOrEof() == StreamTokenizer.TT_EOF ) + pushBack(); + } + + protected void readST() throws IOException + { + String w = getWordOrNumber(); + + try + { + double time = Double.parseDouble( w ); + + listener.onStepBegins( graphName, time ); + } + catch( NumberFormatException e ) + { + parseError( "expecting a number after `st', got `" + w + "'" ); + } + + if( eatEolOrEof() == StreamTokenizer.TT_EOF ) + pushBack(); + } + + protected void readAttributes( HashMap attributes ) throws IOException + { + boolean del = false; + String key = getWordOrSymbolOrStringOrEolOrEof(); + + attributes.clear(); + + if( key.equals( "-" ) ) + { + key = getWordOrSymbolOrStringOrEolOrEof(); + del = true; + } + + if( key.equals( "+" ) ) + key = getWordOrSymbolOrStringOrEolOrEof(); + + while( ! key.equals( "EOF" ) && ! key.equals( "EOL" ) && ! key.equals( "]" ) ) + { + if( del ) + attributes.put( key, null ); + else attributes.put( key, readAttributeValue( key ) ); + + key = getWordOrSymbolOrStringOrEolOrEof(); + + if( key.equals( "-" ) ) + { + key = getWordOrStringOrEolOrEof(); + del = true; + } + + if( key.equals( "+" ) ) + { + key = getWordOrStringOrEolOrEof(); + del = false; + } + } + + pushBack(); + } + + /** + * Read an attribute. The "key" (attribute name) is already read. + * @param key The attribute name, already read. + */ + protected Object readAttributeValue( String key ) throws IOException + { + ArrayList vector = null; + Object value = null; + Object value2 = null; + String next = null; + + eatSymbols( ":=" ); + + value = getStringOrWordOrSymbolOrNumberO(); + + if( value.equals( "[" ) ) + { + HashMap map = new HashMap(); + + readAttributes( map ); + eatSymbol( ']' ); + + value = map; + } + else + { + pushBack(); + + value = getStringOrWordOrNumberO(); + next = getWordOrSymbolOrNumberOrStringOrEolOrEof(); + + while( next.equals( "," ) ) + { + if( vector == null ) + { + vector = new ArrayList(); + vector.add( value ); + } + + value2 = getStringOrWordOrNumberO(); + next = getWordOrSymbolOrNumberOrStringOrEolOrEof(); + + vector.add( value2 ); + } + + pushBack(); + } + + if( vector != null ) + return vector.toArray(); + else return value; + } + + /** + * Generate a parse error. + */ + protected void parseError( String message ) throws IOException + { + if(statusListener!=null) statusListener.onError(); + if (report!=null) { + Issue issue = new Issue("parse error: " + + st.lineno() + ": " + message, Issue.Level.SEVERE); + report.logIssue(issue); + } + throw new IOException( "parse error: " + + st.lineno() + ": " + message ); + } + +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSParserListener.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSParserListener.java new file mode 100644 index 0000000000..68afec7733 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSParserListener.java @@ -0,0 +1,71 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.impl.dgs; + +import java.util.Map; + +/** + * A parser listener for the GraphStream DSG file format. + * + * @author panisson + * + */ +public interface DGSParserListener { + + void onNodeAdded( String sourceId, String nodeId, Map attributes ); + + void onNodeChanged(String sourceId, String nodeId, Map attributes); + + void onNodeRemoved( String sourceId, String nodeId ); + + void onEdgeAdded( String sourceId, String edgeId, String fromNodeId, String toNodeId, + boolean directed, Map attributes ); + + void onEdgeChanged(String graphName, String tag, Map attributes); + + void onEdgeRemoved( String sourceId, String edgeId ); + + void onGraphChanged(Map attributes); + + void onStepBegins(String graphName, double time); + +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSStreamReader.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSStreamReader.java new file mode 100644 index 0000000000..b564fe0eb3 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSStreamReader.java @@ -0,0 +1,157 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.impl.dgs; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.util.Date; +import java.util.Map; + +import org.gephi.streaming.api.GraphEventHandler; +import org.gephi.streaming.api.StreamReader; +import org.gephi.streaming.api.event.ElementType; +import org.gephi.streaming.api.event.EventType; +import org.gephi.streaming.api.event.GraphEventBuilder; + +/** + * A stream reader for the GraphStream DSG file format. + * + * @author panisson + * + */ +public class DGSStreamReader extends StreamReader implements DGSParserListener { + + /** + * @param handler the GraphEventHandler to which the events will be delegated + */ + public DGSStreamReader(GraphEventHandler handler, + GraphEventBuilder eventBuilder) { + super(handler, eventBuilder); + } + + @Override + public void processStream(InputStream inputStream) { + + DGSParser parser = new DGSParser(inputStream, this, report, listener); + try { + parser.parse(); + } catch (IOException e) { + } finally { + if (report!=null) + report.log("Stream closed at "+new Date()); + } + + if (listener!=null) { + listener.onStreamClosed(); + } + } + + @Override + public void onEdgeAdded(String graphName, String edgeId, String fromTag, + String toTag, boolean directed, Map attributes) { + handler.handleGraphEvent(eventBuilder.edgeAddedEvent(edgeId, fromTag, toTag, directed, attributes)); + if (report!=null) + report.incrementEventCounter(); + } + + @Override + public void onEdgeChanged(String sourceId, String edgeId, Map attributes) { + handler.handleGraphEvent(eventBuilder.graphEvent(ElementType.EDGE, EventType.CHANGE, edgeId, attributes)); + if (report!=null) + report.incrementEventCounter(); + } + + @Override + public void onEdgeRemoved(String sourceId, String edgeId) { + handler.handleGraphEvent(eventBuilder.graphEvent(ElementType.EDGE, EventType.REMOVE, edgeId, null)); + if (report!=null) + report.incrementEventCounter(); + } + + @Override + public void onGraphChanged(Map attributes) { + handler.handleGraphEvent(eventBuilder.graphEvent(ElementType.GRAPH, EventType.CHANGE, null, attributes)); + if (report!=null) + report.incrementEventCounter(); + } + + @Override + public void onNodeAdded(String sourceId, String nodeId, Map attributes) { + handler.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, nodeId, attributes)); + if (report!=null) + report.incrementEventCounter(); + } + + @Override + public void onNodeChanged(String sourceId, String nodeId, Map attributes) { + handler.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, nodeId, attributes)); + if (report!=null) + report.incrementEventCounter(); + } + + @Override + public void onNodeRemoved(String sourceId, String nodeId) { + handler.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.REMOVE, nodeId, null)); + if (report!=null) + report.incrementEventCounter(); + } + + @Override + public void onStepBegins(String graphName, double time) { + //TODO + System.out.println("onStepBegins: Not implemented"); + if (report!=null) + report.incrementEventCounter(); + } + + @Override + public String toString() { + return "DGSStreamProcessor"; + } + + @Override + public void processStream(ReadableByteChannel channel) throws IOException { + this.processStream(Channels.newInputStream(channel)); + } +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSStreamType.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSStreamType.java new file mode 100644 index 0000000000..9f481c9b84 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSStreamType.java @@ -0,0 +1,75 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.impl.dgs; + +import org.gephi.streaming.api.StreamReader; +import org.gephi.streaming.api.StreamType; +import org.gephi.streaming.api.StreamWriter; +import org.openide.util.lookup.ServiceProvider; + +/** + * The GraphStream DSG file format. + * + * @author panisson + * + */ +@ServiceProvider(service = StreamType.class) +public class DGSStreamType implements StreamType { + + public String getType() { + return "DGS"; + } + + public Class getStreamReaderClass() { + return DGSStreamReader.class; + } + + public Class getStreamWriterClass() { + return DGSStreamWriter.class; + } + + @Override + public String toString() { + return getType(); + } + +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSStreamWriter.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSStreamWriter.java new file mode 100644 index 0000000000..f8534a67e5 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/dgs/DGSStreamWriter.java @@ -0,0 +1,311 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.impl.dgs; + +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import org.gephi.streaming.api.StreamWriter; +import org.gephi.streaming.api.event.EdgeAddedEvent; +import org.gephi.streaming.api.event.ElementEvent; +import org.gephi.streaming.api.event.GraphEvent; + +/** + * @author panisson + * + */ +public class DGSStreamWriter extends StreamWriter { + + /** + * A shortcut to the output. + */ + protected PrintStream out; + + protected String graphName = ""; + + public DGSStreamWriter(OutputStream outputStream) { + super(outputStream); + out = new PrintStream(outputStream, true); + } + + @Override + public void startStream() { + outputHeader(); + } + + @Override + public void endStream() { + outputEndOfFile(); + } + + protected void outputHeader() { + + out.printf( "DGS004%n" ); + + if( graphName.length() <= 0 ) + out.printf( "null 0 0%n" ); + else out.printf( "\"%s\" 0 0%n", graphName ); + } + + protected void outputEndOfFile() { + // NOP + } + + @Override + public synchronized void handleGraphEvent(GraphEvent event) { + + if (event instanceof ElementEvent) { + ElementEvent elementEvent = (ElementEvent)event; + + switch (event.getElementType()) { + case NODE: + switch (event.getEventType()) { + case ADD: + this.nodeAdded(elementEvent.getElementId(), elementEvent.getAttributes()); + break; + case CHANGE: + this.nodeChanged(elementEvent.getElementId(), elementEvent.getAttributes()); + break; + case REMOVE: + this.nodeRemoved(elementEvent.getElementId()); + break; + } + break; + case EDGE: + switch (event.getEventType()) { + case ADD: + EdgeAddedEvent eaEvent = (EdgeAddedEvent)event; + this.edgeAdded(elementEvent.getElementId(), eaEvent.getSourceId(), + eaEvent.getTargetId(), eaEvent.isDirected(), + elementEvent.getAttributes()); + break; + case CHANGE: + this.edgeChanged(elementEvent.getElementId(), + elementEvent.getAttributes()); + break; + case REMOVE: + this.edgeRemoved(elementEvent.getElementId()); + break; + } + break; + } + } + } + + private void graphAttributeAdded(String attribute, Object value) { + graphAttributeChanged(attribute, null, value); + } + + public void graphAttributeChanged(String attribute, Object oldValue, + Object newValue) { + out.printf("cg %s%n", attributeString(attribute, newValue, false)); + } + + private void graphAttributeRemoved(String attribute) { + out.printf("cg %s%n", attributeString(attribute, null, true)); + } + + private void edgeAdded(String edgeId, String fromNodeId, String toNodeId, + boolean directed, Map attributes) { + out.printf("ae \"%s\" \"%s\" %s \"%s\"", edgeId, fromNodeId, directed ? ">" : "", toNodeId); + if (attributes != null && !attributes.isEmpty()) { + for (Map.Entry entry : attributes.entrySet()) { + out.printf(" %s", attributeString(entry.getKey(), entry.getValue(), false)); + } + } + out.printf("%n"); + } + + private void edgeChanged(String edgeId, Map attributes) { + if (attributes != null && attributes.size() > 0) { + out.printf("ce \"%s\"", edgeId); + for (Map.Entry entry : attributes.entrySet()) { + out.printf(" \"%s\":\"%s\"", entry.getKey(), entry.getValue()); + } + out.printf("%n"); + } + } + + private void edgeRemoved(String edgeId) { + out.printf("de \"%s\"%n", edgeId); + } + + private void graphCleared() { + out.printf("clear%n"); + } + + private void nodeAdded(String nodeId, Map attributes) { + out.printf("an \"%s\"", nodeId); + if (attributes != null && !attributes.isEmpty()) { + for (Map.Entry entry : attributes.entrySet()) { + out.printf(" %s", attributeString(entry.getKey(), entry.getValue(), false)); + } + } + out.printf("%n"); + } + + private void nodeChanged(String nodeId, Map attributes) { + if (attributes != null && attributes.size() > 0) { + out.printf("cn \"%s\"", nodeId); + for (Map.Entry entry : attributes.entrySet()) { + out.printf(" \"%s\":\"%s\"", entry.getKey(), entry.getValue()); + } + out.printf("%n"); + } + } + + private void nodeRemoved(String nodeId) { + out.printf("dn \"%s\"%n", nodeId); + } + + public void stepBegins(double step) { + out.printf(Locale.US, "st %f%n", step); + } + + // Utility methods + + protected String attributeString(String key, Object value, boolean remove) { + if (key == null || key.length() == 0) { + return null; + } + + if (remove) { + return String.format(" -\"%s\"", key); + } else { + if (value != null && value.getClass().isArray()) { + Object[] values = (Object[]) value; + StringBuilder sb = new StringBuilder(); + + sb.append(String.format(" \"%s\":", key)); + + if (values.length > 0) { + sb.append(valueString(values[0])); + } else { + sb.append("\"\""); + } + + for (int i = 1; i < values.length; ++i) { + sb.append(String.format(",%s", valueString(values[i]))); + } + + return sb.toString(); + } else { + return String.format(" \"%s\":%s", key, valueString(value)); + } + } + } + + protected String valueString(Object value) { + + if (value instanceof CharSequence) + { + return String.format("\"%s\"", (CharSequence) value); + } + else if (value instanceof Number) + { + if (value instanceof Integer || value instanceof Short || value instanceof Byte || value instanceof Long) + { + return String.format(Locale.US, "%d", ((Number) value).longValue()); + } + else if (value instanceof Float || value instanceof Double) + { + return String.format(Locale.US, "%f", ((Number) value).doubleValue()); + } + else if (value instanceof Character) + { + return String.format("\"%c\"", ((Character) value).charValue()); + } + else if (value instanceof Boolean) + { + return String.format(Locale.US, "\"%b\"", ((Boolean) value)); + } + else + { + return String.format(Locale.US, " %f", ((Number) value).doubleValue()); + } + } + else if (value == null) + { + return "\"\""; + } + else if (value instanceof Object[]) + { + Object array[] = (Object[]) value; + int n = array.length; + StringBuilder sb = new StringBuilder(); + + if (array.length > 0) { + sb.append(valueString(array[0])); + } + + for (int i = 1; i < n; i++) + { + sb.append(","); + sb.append(valueString(array[i])); + } + + return sb.toString(); + } + else + { + return String.format("\"%s\"", value.toString()); + } + } + + protected String hashToString(HashMap hash) { + StringBuilder sb = new StringBuilder(); + + sb.append("[ "); + + for (Object key : hash.keySet()) { + sb.append(attributeString(key.toString(), hash.get(key), false)); + sb.append(" "); + } + + sb.append(']'); + + return sb.toString(); + } +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/JSONStreamReader.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/JSONStreamReader.java new file mode 100644 index 0000000000..b1984056cc --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/JSONStreamReader.java @@ -0,0 +1,365 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.impl.json; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.gephi.filters.spi.EdgeFilter; +import org.gephi.filters.spi.Filter; +import org.gephi.filters.spi.FilterProperty; +import org.gephi.filters.spi.NodeFilter; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.Node; + +import org.gephi.streaming.api.GraphEventHandler; +import org.gephi.streaming.api.Issue; +import org.gephi.streaming.api.StreamReader; +import org.gephi.streaming.api.event.ElementType; +import org.gephi.streaming.api.event.EventType; +import org.gephi.streaming.api.event.FilterEvent; +import org.gephi.streaming.api.event.GraphEvent; +import org.gephi.streaming.api.event.GraphEventBuilder; +import org.gephi.streaming.impl.FilterFactory; +import org.gephi.streaming.impl.json.parser.JSONException; +import org.gephi.streaming.impl.json.parser.JSONObject; +import org.gephi.streaming.impl.json.parser.JSONConstants.Fields; +import org.gephi.streaming.impl.json.parser.JSONConstants.Types; + +/** + * A stream reader for the GraphStream JSON file format. + * + * @author panisson + * + */ +public class JSONStreamReader extends StreamReader { + + private static final Logger logger = Logger.getLogger(JSONStreamReader.class.getName()); + private FilterFactory filterFactory = new FilterFactory(); + + /** + * @param handler the GraphEventHandler to which the events will be delegated + * @param eventBuilder + */ + public JSONStreamReader(GraphEventHandler handler, + GraphEventBuilder eventBuilder) { + super(handler, eventBuilder); + } + + @Override + public void processStream(InputStream inputStream) throws IOException { + +// this.processStream(Channels.newChannel(inputStream), listener); + + StringBuilder content = new StringBuilder(1024); + + InputStreamReader reader = new InputStreamReader(inputStream, Charset.forName("UTF-8")); + + try { + int read; + while ((read = reader.read())!=-1) { + char readChar = (char)read; + if (readChar == '\r') { + if (listener!=null) { + listener.onDataReceived(); + } + parse(content.toString()); + content.setLength(0); + } else { + content.append(readChar); + } + } + } catch (IOException e) { + } finally { + if (report!=null) + report.log("Stream closed at "+new Date()); + } + + + + + if (content.length() > 0) { + parse(content.toString()); + } + + if (listener!=null) { + listener.onStreamClosed(); + } + + } + + @Override + public void processStream(ReadableByteChannel channel) throws IOException { + + this.processStream(Channels.newInputStream(channel)); + +// StringBuilder content = new StringBuilder(); + +// ByteBuffer buffer = ByteBuffer.allocate(4096); +// CharsetDecoder decoder = Charset.defaultCharset().newDecoder(); +// +// try { +// int read; +// while ((read = channel.read(buffer))!=-1) { +// +// buffer.flip(); +// +// if (listener!=null) { +// listener.onDataReceived(); +// } +// CharBuffer charbuffer = decoder.decode(buffer); +// content.append(charbuffer.toString()); +// +// int pos; +// while ((pos=content.indexOf("\r")) > 0) { +// parse(content.substring(0, pos+1)); +// content.delete(0, pos+1); +// } +// +// buffer.clear(); +// } +// } catch (IOException e) { +// } finally { +// if (report!=null) +// report.log("Stream closed at "+new Date()); +// } +// +// if (content.length() > 0) { +// parse(content.toString()); +// } +// +// if (listener!=null) { +// listener.onStreamClosed(); +// } + } + + @Override + public String toString() { + return "JSONStreamProcessor"; + } + + private void parse(String content) { + content = content.trim(); + if (content.length() == 0) return; + + try { + JSONObject jo = new JSONObject(content); + + String id = null; + if (jo.has(Fields.ID.value())) { + id = jo.getString(Fields.ID.value()); + } + + Double t = null; + if (jo.has(Fields.T.value())) { + String tstr = jo.getString(Fields.T.value()); + t = Double.valueOf(tstr); + } + + Iterator keys = jo.keys(); + + while (keys.hasNext()) { + String key = keys.next(); + if (Fields.ID.value().equals(key)) continue; + if (Fields.T.value().equals(key)) continue; + + Object gObjs = jo.get(key); + if (gObjs instanceof JSONObject) { + parse(key, (JSONObject)gObjs, id, t); + } else { + throw new IllegalArgumentException("Invalid attribute: "+key); + //logger.log(Level.WARNING, "JSON attribute ignored: \"{0}\"", new String[]{key}); + } + } + + if (report!=null) { + report.incrementEventCounter(); + } + } catch (JSONException e) { + + if (listener!=null) { + listener.onError(); + } + + if (report!=null) { + StringBuilder message = new StringBuilder("JSON object "); + message.append(report.getEventCounter()+1) + .append(" ignored: \"") + .append(content) + .append("\": ") + .append(e.getMessage()); + + Issue issue = new Issue(message.toString(), Issue.Level.WARNING, e); + report.logIssue(issue); + } + logger.log(Level.WARNING, "JSON object ignored: \"{0}\": {1}", new String[]{content, e.getMessage()}); + } + } + + private void parse(String type, JSONObject gObjs, String eventId, Double t) throws JSONException { + + Types eventType = Types.fromString(type); + + if (gObjs.has("filter")) { + + Map attributes = null; + if (gObjs.has("attributes")) { + JSONObject attrObj = gObjs.getJSONObject("attributes"); + attributes = readAttributes(attrObj); + } + + handler.handleGraphEvent( + + new FilterEvent(this, eventType.getEventType(), + eventType.getElementType(), getFilter(eventType.getElementType(), gObjs), attributes)); + return; + } + + if (eventType.equals(Types.CG)) { + Map attributes = readAttributes(gObjs); + handler.handleGraphEvent(eventBuilder.graphEvent(ElementType.GRAPH, EventType.CHANGE, + null, attributes)); + return; + } + + Iterator it = gObjs.keys(); + while (it.hasNext()) { + GraphEvent event = null; + String id = it.next(); + + if (eventType.equals(Types.AN)) { + JSONObject gObj = (JSONObject)gObjs.get(id); + Map attributes = readAttributes(gObj); + event = eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, id, attributes); + + } else if (eventType.equals(Types.CN)) { + JSONObject gObj = (JSONObject)gObjs.get(id); + Map attributes = readAttributes(gObj); + event = eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, id, attributes); + + } else if (eventType.equals(Types.DN)) { + event = eventBuilder.graphEvent(ElementType.NODE, EventType.REMOVE, id, null); + + } else if (eventType.equals(Types.AE)) { + Map attributes = new HashMap(); + JSONObject gObj = (JSONObject)gObjs.get(id); + Iterator i2 = gObj.keys(); + while (i2.hasNext()) { + String key = i2.next(); + if (!key.equals(Fields.SOURCE.value()) + && !key.equals(Fields.TARGET.value()) + && !key.equals(Fields.DIRECTED.value())) { + Object value = gObj.get(key); + attributes.put(key, value); + } + } + + boolean directed = true; + if (gObj.has(Fields.DIRECTED.value())) { + directed = Boolean.valueOf(gObj.getString(Fields.DIRECTED.value())); + } + + event = eventBuilder.edgeAddedEvent(id, + gObj.getString(Fields.SOURCE.value()), + gObj.getString(Fields.TARGET.value()), + directed, attributes); + + } else if (eventType.equals(Types.CE)) { + JSONObject gObj = (JSONObject)gObjs.get(id); + Map attributes = readAttributes(gObj); + event = eventBuilder.graphEvent(ElementType.EDGE, EventType.CHANGE, id, attributes); + + } else if (eventType.equals(Types.DE)) { + event = eventBuilder.graphEvent(ElementType.EDGE, EventType.REMOVE, id, null); + + } + + if (event != null) { + if (eventId!=null) { + event.setEventId(eventId); + } + if (t!=null) { + event.setTimestamp(t); + } + handler.handleGraphEvent(event); + } + } + } + + private Map readAttributes(JSONObject gObj) throws JSONException { + Map attributes = new HashMap(); + Iterator it = gObj.keys(); + while (it.hasNext()) { + String key = it.next(); + Object value = gObj.get(key); + attributes.put(key, value); + } + return attributes; + } + + private Filter getFilter(ElementType elementType, JSONObject gObjs) throws JSONException{ + Object filter = gObjs.get("filter"); + + if (filter instanceof String) { + String filterName = (String)filter; + return filterFactory.getFilter(elementType, filterName, null); + } else { + JSONObject filterObj = (JSONObject)filter; + String filterName = filterObj.keys().next(); + JSONObject filterAttr = filterObj.getJSONObject(filterName); + return filterFactory.getFilter(elementType, filterName, readAttributes(filterAttr)); + } + } +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/JSONStreamType.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/JSONStreamType.java new file mode 100644 index 0000000000..9adc5a6392 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/JSONStreamType.java @@ -0,0 +1,75 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.impl.json; + +import org.gephi.streaming.api.StreamReader; +import org.gephi.streaming.api.StreamType; +import org.gephi.streaming.api.StreamWriter; +import org.openide.util.lookup.ServiceProvider; + +/** + * The GraphStream JSON file format. + * + * @author panisson + * + */ +@ServiceProvider(service = StreamType.class) +public class JSONStreamType implements StreamType { + + public String getType() { + return "JSON"; + } + + public Class getStreamReaderClass() { + return JSONStreamReader.class; + } + + public Class getStreamWriterClass() { + return JSONStreamWriter.class; + } + + @Override + public String toString() { + return getType(); + } + +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/JSONStreamWriter.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/JSONStreamWriter.java new file mode 100644 index 0000000000..e0471da9ae --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/JSONStreamWriter.java @@ -0,0 +1,332 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.impl.json; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.gephi.streaming.api.StreamWriter; +import org.gephi.streaming.api.event.EdgeAddedEvent; +import org.gephi.streaming.api.event.ElementEvent; +import org.gephi.streaming.api.event.GraphEvent; +import org.gephi.streaming.impl.json.parser.JSONConstants; +import org.gephi.streaming.impl.json.parser.JSONException; +import org.gephi.streaming.impl.json.parser.JSONObject; +import org.gephi.streaming.impl.json.parser.JSONConstants.Fields; +import org.gephi.streaming.impl.json.parser.JSONConstants.Types; + +/** + * StreamWriter implementation to output graph data in JSON format. + * + * @author panisson + * + */ +public class JSONStreamWriter extends StreamWriter { + + private static final Logger logger = + Logger.getLogger(JSONStreamWriter.class.getName()); + + private static String EOL = "\r\n"; + + /** + * @param outputStream - the OutputStream to send formatted data. + */ + public JSONStreamWriter(OutputStream outputStream) { + super(outputStream); + out = new PrintStream(outputStream, true); + } + + @Override + public void startStream() { + outputHeader(); + } + + @Override + public void endStream() { + outputEndOfFile(); + } + + public void handleGraphEvent(GraphEvent event) { + + if (event instanceof ElementEvent) { + ElementEvent elementEvent = (ElementEvent)event; + JSONObject o = null; + + switch (event.getElementType()) { + case GRAPH: + o = graphAttributeChanged(elementEvent.getAttributes()); + break; + case NODE: + + switch (event.getEventType()) { + case ADD: + o = nodeAdded(elementEvent.getElementId(), elementEvent.getAttributes()); + break; + case CHANGE: + o = nodeChanged(elementEvent.getElementId(), elementEvent.getAttributes()); + break; + case REMOVE: + o = nodeRemoved(elementEvent.getElementId()); + break; + } + break; + case EDGE: + switch (event.getEventType()) { + case ADD: + EdgeAddedEvent eaEvent = (EdgeAddedEvent)event; + o = edgeAdded(elementEvent.getElementId(), eaEvent.getSourceId(), + eaEvent.getTargetId(), eaEvent.isDirected(), + elementEvent.getAttributes()); + break; + case CHANGE: + o = edgeChanged(elementEvent.getElementId(), + elementEvent.getAttributes()); + break; + case REMOVE: + o = edgeRemoved(elementEvent.getElementId()); + break; + } + break; + } + + if (o != null) { + + if (event.getTimestamp() != null) { + try { + o.put(JSONConstants.Fields.T.value(), event.getTimestamp()); + } catch (JSONException e) { + logger.log(Level.WARNING, "Unable to write timestamp " + + "into JSON object '{0}': {1}", + new Object[]{o.toString(), e.getMessage()}); + } + } + + out.print(o.toString() + EOL); + } + + } + try { + outputStream.flush(); + } catch (IOException ex) { + throw new RuntimeException(ex); +// Logger.getLogger(JSONStreamWriter.class.getName()).log(Level.SEVERE, null, ex); + } + } + + /** + * A shortcut to the output. + */ + protected PrintStream out; + + protected String graphName = ""; + + protected void outputHeader() { + } + + protected void outputEndOfFile() { + } + + private JSONObject graphAttributeChanged(Map attributes) { + try { + JSONObject graphData = createGraphData(attributes); + return new JSONObject() + .put(Types.CG.value(), graphData); + } catch (JSONException e) { + logger.log(Level.WARNING, "Unable to write JSONObject for " + + "graphAttributeChanged event: {0}", + new Object[]{e.getMessage()}); + return null; + } + } + + private JSONObject edgeAdded(String edgeId, String fromNodeId, String toNodeId, + boolean directed, Map attributes) { + try { + + JSONObject edgeData = createEdgeData(edgeId, fromNodeId, toNodeId, directed, attributes); + + return new JSONObject() + .put(Types.AE.value(), edgeData); + } catch (JSONException e) { + logger.log(Level.WARNING, "Unable to write JSONObject for " + + "edgeAdded event, edge {0}: {1}", + new Object[]{edgeId, e.getMessage()}); + return null; + } + } + + private JSONObject edgeChanged(String edgeId, Map attributes) { + try { + JSONObject edgeData = createEdgeChangedData(edgeId, attributes); + return new JSONObject() + .put(Types.CE.value(), edgeData); + } catch (JSONException e) { + logger.log(Level.WARNING, "Unable to write JSONObject for " + + "edgeChanged event, edge {0}: {1}", + new Object[]{edgeId, e.getMessage()}); + return null; + } + } + + private JSONObject edgeRemoved(String edgeId) { + try { + return new JSONObject() + .put(Types.DE.value(), new JSONObject() + .put(edgeId, new JSONObject()) + ); + } catch (JSONException e) { + logger.log(Level.WARNING, "Unable to write JSONObject for " + + "edgeRemoved event, edge {0}: {1}", + new Object[]{edgeId, e.getMessage()}); + return null; + } + } + + private JSONObject nodeAdded(String nodeId, Map attributes) { + try { + JSONObject nodeData = createNodeData(nodeId, attributes); + return new JSONObject() + .put(Types.AN.value(), nodeData); + } catch (JSONException e) { + logger.log(Level.WARNING, "Unable to write JSONObject for " + + "nodeAdded event, node {0}: {1}", + new Object[]{nodeId, e.getMessage()}); + return null; + } + } + + private JSONObject nodeChanged(String nodeId, Map attributes) { + try { + JSONObject nodeData = createNodeData(nodeId, attributes); + return new JSONObject() + .put(Types.CN.value(), nodeData); + } catch (JSONException e) { + logger.log(Level.WARNING, "Unable to write JSONObject for " + + "nodeChanged event, node {0}: {1}", + new Object[]{nodeId, e.getMessage()}); + return null; + } + } + + private JSONObject nodeRemoved(String nodeId) { + try { + return new JSONObject() + .put(Types.DN.value(), new JSONObject() + .put(nodeId, new JSONObject()) + ); + } catch (JSONException e) { + logger.log(Level.WARNING, "Unable to write JSONObject for " + + "nodeRemoved event, node {0}: {1}", + new Object[]{nodeId, e.getMessage()}); + return null; + } + } + + private JSONObject createGraphData(Map attributes) + throws JSONException { + + JSONObject attributesJObject = new JSONObject(); + if (attributes != null && attributes.size() > 0) { + for (Map.Entry entry : attributes.entrySet()) { + attributesJObject.put(entry.getKey(), entry.getValue()); + } + } + + return attributesJObject; + } + + private JSONObject createNodeData(String nodeId, + Map attributes) throws JSONException { + + JSONObject attributesJObject = new JSONObject(); + if (attributes != null && attributes.size() > 0) { + for (Map.Entry entry : attributes.entrySet()) { + attributesJObject.put(entry.getKey(), entry.getValue()); + } + } + + JSONObject nodeData = new JSONObject(); + nodeData.put(nodeId, attributesJObject); + + return nodeData; + } + + private JSONObject createEdgeData(String edgeId, String fromNodeId, String toNodeId, + boolean directed, Map attributes) throws JSONException { + + JSONObject attributesJObject = new JSONObject() + .put(Fields.SOURCE.value(), fromNodeId) + .put(Fields.TARGET.value(), toNodeId) + .put(Fields.DIRECTED.value(), directed); + + if (attributes != null && attributes.size() > 0) { + for (Map.Entry entry : attributes.entrySet()) { + attributesJObject.put(entry.getKey(), entry.getValue()); + } + } + + JSONObject nodeData = new JSONObject(); + nodeData.put(edgeId, attributesJObject); + + return nodeData; + } + + private JSONObject createEdgeChangedData(String edgeId, Map attributes) throws JSONException { + + JSONObject attributesJObject = new JSONObject(); + + if (attributes != null && attributes.size() > 0) { + for (Map.Entry entry : attributes.entrySet()) { + attributesJObject.put(entry.getKey(), entry.getValue()); + } + } + + JSONObject nodeData = new JSONObject(); + nodeData.put(edgeId, attributesJObject); + + return nodeData; + } +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/CDL.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/CDL.java new file mode 100644 index 0000000000..caa9e7e431 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/CDL.java @@ -0,0 +1,279 @@ +package org.gephi.streaming.impl.json.parser; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * This provides static methods to convert comma delimited text into a + * JSONArray, and to covert a JSONArray into comma delimited text. Comma + * delimited text is a very popular format for data interchange. It is + * understood by most database, spreadsheet, and organizer programs. + *

+ * Each row of text represents a row in a table or a data record. Each row + * ends with a NEWLINE character. Each row contains one or more values. + * Values are separated by commas. A value can contain any character except + * for comma, unless is is wrapped in single quotes or double quotes. + *

+ * The first row usually contains the names of the columns. + *

+ * A comma delimited list can be converted into a JSONArray of JSONObjects. + * The names for the elements in the JSONObjects can be taken from the names + * in the first row. + * @author JSON.org + * @version 2009-09-11 + */ +public class CDL { + + /** + * Get the next value. The value can be wrapped in quotes. The value can + * be empty. + * @param x A JSONTokener of the source text. + * @return The value string, or null if empty. + * @throws JSONException if the quoted string is badly formed. + */ + private static String getValue(JSONTokener x) throws JSONException { + char c; + char q; + StringBuffer sb; + do { + c = x.next(); + } while (c == ' ' || c == '\t'); + switch (c) { + case 0: + return null; + case '"': + case '\'': + q = c; + sb = new StringBuffer(); + for (;;) { + c = x.next(); + if (c == q) { + break; + } + if (c == 0 || c == '\n' || c == '\r') { + throw x.syntaxError("Missing close quote '" + q + "'."); + } + sb.append(c); + } + return sb.toString(); + case ',': + x.back(); + return ""; + default: + x.back(); + return x.nextTo(','); + } + } + + /** + * Produce a JSONArray of strings from a row of comma delimited values. + * @param x A JSONTokener of the source text. + * @return A JSONArray of strings. + * @throws JSONException + */ + public static JSONArray rowToJSONArray(JSONTokener x) throws JSONException { + JSONArray ja = new JSONArray(); + for (;;) { + String value = getValue(x); + char c = x.next(); + if (value == null || + (ja.length() == 0 && value.length() == 0 && c != ',')) { + return null; + } + ja.put(value); + for (;;) { + if (c == ',') { + break; + } + if (c != ' ') { + if (c == '\n' || c == '\r' || c == 0) { + return ja; + } + throw x.syntaxError("Bad character '" + c + "' (" + + (int)c + ")."); + } + c = x.next(); + } + } + } + + /** + * Produce a JSONObject from a row of comma delimited text, using a + * parallel JSONArray of strings to provides the names of the elements. + * @param names A JSONArray of names. This is commonly obtained from the + * first row of a comma delimited text file using the rowToJSONArray + * method. + * @param x A JSONTokener of the source text. + * @return A JSONObject combining the names and values. + * @throws JSONException + */ + public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x) + throws JSONException { + JSONArray ja = rowToJSONArray(x); + return ja != null ? ja.toJSONObject(names) : null; + } + + /** + * Produce a JSONArray of JSONObjects from a comma delimited text string, + * using the first row as a source of names. + * @param string The comma delimited text. + * @return A JSONArray of JSONObjects. + * @throws JSONException + */ + public static JSONArray toJSONArray(String string) throws JSONException { + return toJSONArray(new JSONTokener(string)); + } + + /** + * Produce a JSONArray of JSONObjects from a comma delimited text string, + * using the first row as a source of names. + * @param x The JSONTokener containing the comma delimited text. + * @return A JSONArray of JSONObjects. + * @throws JSONException + */ + public static JSONArray toJSONArray(JSONTokener x) throws JSONException { + return toJSONArray(rowToJSONArray(x), x); + } + + /** + * Produce a JSONArray of JSONObjects from a comma delimited text string + * using a supplied JSONArray as the source of element names. + * @param names A JSONArray of strings. + * @param string The comma delimited text. + * @return A JSONArray of JSONObjects. + * @throws JSONException + */ + public static JSONArray toJSONArray(JSONArray names, String string) + throws JSONException { + return toJSONArray(names, new JSONTokener(string)); + } + + /** + * Produce a JSONArray of JSONObjects from a comma delimited text string + * using a supplied JSONArray as the source of element names. + * @param names A JSONArray of strings. + * @param x A JSONTokener of the source text. + * @return A JSONArray of JSONObjects. + * @throws JSONException + */ + public static JSONArray toJSONArray(JSONArray names, JSONTokener x) + throws JSONException { + if (names == null || names.length() == 0) { + return null; + } + JSONArray ja = new JSONArray(); + for (;;) { + JSONObject jo = rowToJSONObject(names, x); + if (jo == null) { + break; + } + ja.put(jo); + } + if (ja.length() == 0) { + return null; + } + return ja; + } + + + /** + * Produce a comma delimited text row from a JSONArray. Values containing + * the comma character will be quoted. Troublesome characters may be + * removed. + * @param ja A JSONArray of strings. + * @return A string ending in NEWLINE. + */ + public static String rowToString(JSONArray ja) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < ja.length(); i += 1) { + if (i > 0) { + sb.append(','); + } + Object o = ja.opt(i); + if (o != null) { + String s = o.toString(); + if (s.length() > 0 && (s.indexOf(',') >= 0 || s.indexOf('\n') >= 0 || + s.indexOf('\r') >= 0 || s.indexOf(0) >= 0 || + s.charAt(0) == '"')) { + sb.append('"'); + int length = s.length(); + for (int j = 0; j < length; j += 1) { + char c = s.charAt(j); + if (c >= ' ' && c != '"') { + sb.append(c); + } + } + sb.append('"'); + } else { + sb.append(s); + } + } + } + sb.append('\n'); + return sb.toString(); + } + + /** + * Produce a comma delimited text from a JSONArray of JSONObjects. The + * first row will be a list of names obtained by inspecting the first + * JSONObject. + * @param ja A JSONArray of JSONObjects. + * @return A comma delimited text. + * @throws JSONException + */ + public static String toString(JSONArray ja) throws JSONException { + JSONObject jo = ja.optJSONObject(0); + if (jo != null) { + JSONArray names = jo.names(); + if (names != null) { + return rowToString(names) + toString(names, ja); + } + } + return null; + } + + /** + * Produce a comma delimited text from a JSONArray of JSONObjects using + * a provided list of names. The list of names is not included in the + * output. + * @param names A JSONArray of strings. + * @param ja A JSONArray of JSONObjects. + * @return A comma delimited text. + * @throws JSONException + */ + public static String toString(JSONArray names, JSONArray ja) + throws JSONException { + if (names == null || names.length() == 0) { + return null; + } + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < ja.length(); i += 1) { + JSONObject jo = ja.optJSONObject(i); + if (jo != null) { + sb.append(rowToString(jo.toJSONArray(names))); + } + } + return sb.toString(); + } +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/Cookie.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/Cookie.java new file mode 100644 index 0000000000..705dbdb2a7 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/Cookie.java @@ -0,0 +1,169 @@ +package org.gephi.streaming.impl.json.parser; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * Convert a web browser cookie specification to a JSONObject and back. + * JSON and Cookies are both notations for name/value pairs. + * @author JSON.org + * @version 2008-09-18 + */ +public class Cookie { + + /** + * Produce a copy of a string in which the characters '+', '%', '=', ';' + * and control characters are replaced with "%hh". This is a gentle form + * of URL encoding, attempting to cause as little distortion to the + * string as possible. The characters '=' and ';' are meta characters in + * cookies. By convention, they are escaped using the URL-encoding. This is + * only a convention, not a standard. Often, cookies are expected to have + * encoded values. We encode '=' and ';' because we must. We encode '%' and + * '+' because they are meta characters in URL encoding. + * @param string The source string. + * @return The escaped result. + */ + public static String escape(String string) { + char c; + String s = string.trim(); + StringBuffer sb = new StringBuffer(); + int len = s.length(); + for (int i = 0; i < len; i += 1) { + c = s.charAt(i); + if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') { + sb.append('%'); + sb.append(Character.forDigit((char)((c >>> 4) & 0x0f), 16)); + sb.append(Character.forDigit((char)(c & 0x0f), 16)); + } else { + sb.append(c); + } + } + return sb.toString(); + } + + + /** + * Convert a cookie specification string into a JSONObject. The string + * will contain a name value pair separated by '='. The name and the value + * will be unescaped, possibly converting '+' and '%' sequences. The + * cookie properties may follow, separated by ';', also represented as + * name=value (except the secure property, which does not have a value). + * The name will be stored under the key "name", and the value will be + * stored under the key "value". This method does not do checking or + * validation of the parameters. It only converts the cookie string into + * a JSONObject. + * @param string The cookie specification string. + * @return A JSONObject containing "name", "value", and possibly other + * members. + * @throws JSONException + */ + public static JSONObject toJSONObject(String string) throws JSONException { + String n; + JSONObject o = new JSONObject(); + Object v; + JSONTokener x = new JSONTokener(string); + o.put("name", x.nextTo('=')); + x.next('='); + o.put("value", x.nextTo(';')); + x.next(); + while (x.more()) { + n = unescape(x.nextTo("=;")); + if (x.next() != '=') { + if (n.equals("secure")) { + v = Boolean.TRUE; + } else { + throw x.syntaxError("Missing '=' in cookie parameter."); + } + } else { + v = unescape(x.nextTo(';')); + x.next(); + } + o.put(n, v); + } + return o; + } + + + /** + * Convert a JSONObject into a cookie specification string. The JSONObject + * must contain "name" and "value" members. + * If the JSONObject contains "expires", "domain", "path", or "secure" + * members, they will be appended to the cookie specification string. + * All other members are ignored. + * @param o A JSONObject + * @return A cookie specification string + * @throws JSONException + */ + public static String toString(JSONObject o) throws JSONException { + StringBuffer sb = new StringBuffer(); + + sb.append(escape(o.getString("name"))); + sb.append("="); + sb.append(escape(o.getString("value"))); + if (o.has("expires")) { + sb.append(";expires="); + sb.append(o.getString("expires")); + } + if (o.has("domain")) { + sb.append(";domain="); + sb.append(escape(o.getString("domain"))); + } + if (o.has("path")) { + sb.append(";path="); + sb.append(escape(o.getString("path"))); + } + if (o.optBoolean("secure")) { + sb.append(";secure"); + } + return sb.toString(); + } + + /** + * Convert %hh sequences to single characters, and + * convert plus to space. + * @param s A string that may contain + * + (plus) and + * %hh sequences. + * @return The unescaped string. + */ + public static String unescape(String s) { + int len = s.length(); + StringBuffer b = new StringBuffer(); + for (int i = 0; i < len; ++i) { + char c = s.charAt(i); + if (c == '+') { + c = ' '; + } else if (c == '%' && i + 2 < len) { + int d = JSONTokener.dehexchar(s.charAt(i + 1)); + int e = JSONTokener.dehexchar(s.charAt(i + 2)); + if (d >= 0 && e >= 0) { + c = (char)(d * 16 + e); + i += 2; + } + } + b.append(c); + } + return b.toString(); + } +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/CookieList.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/CookieList.java new file mode 100644 index 0000000000..2786bf2798 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/CookieList.java @@ -0,0 +1,90 @@ +package org.gephi.streaming.impl.json.parser; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +import java.util.Iterator; + +/** + * Convert a web browser cookie list string to a JSONObject and back. + * @author JSON.org + * @version 2008-09-18 + */ +public class CookieList { + + /** + * Convert a cookie list into a JSONObject. A cookie list is a sequence + * of name/value pairs. The names are separated from the values by '='. + * The pairs are separated by ';'. The names and the values + * will be unescaped, possibly converting '+' and '%' sequences. + * + * To add a cookie to a cooklist, + * cookielistJSONObject.put(cookieJSONObject.getString("name"), + * cookieJSONObject.getString("value")); + * @param string A cookie list string + * @return A JSONObject + * @throws JSONException + */ + public static JSONObject toJSONObject(String string) throws JSONException { + JSONObject o = new JSONObject(); + JSONTokener x = new JSONTokener(string); + while (x.more()) { + String name = Cookie.unescape(x.nextTo('=')); + x.next('='); + o.put(name, Cookie.unescape(x.nextTo(';'))); + x.next(); + } + return o; + } + + + /** + * Convert a JSONObject into a cookie list. A cookie list is a sequence + * of name/value pairs. The names are separated from the values by '='. + * The pairs are separated by ';'. The characters '%', '+', '=', and ';' + * in the names and values are replaced by "%hh". + * @param o A JSONObject + * @return A cookie list string + * @throws JSONException + */ + public static String toString(JSONObject o) throws JSONException { + boolean b = false; + Iterator keys = o.keys(); + String s; + StringBuffer sb = new StringBuffer(); + while (keys.hasNext()) { + s = keys.next().toString(); + if (!o.isNull(s)) { + if (b) { + sb.append(';'); + } + sb.append(Cookie.escape(s)); + sb.append("="); + sb.append(Cookie.escape(o.getString(s))); + b = true; + } + } + return sb.toString(); + } +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/HTTP.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/HTTP.java new file mode 100644 index 0000000000..e5b3226a28 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/HTTP.java @@ -0,0 +1,163 @@ +package org.gephi.streaming.impl.json.parser; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +import java.util.Iterator; + +/** + * Convert an HTTP header to a JSONObject and back. + * @author JSON.org + * @version 2008-09-18 + */ +public class HTTP { + + /** Carriage return/line feed. */ + public static final String CRLF = "\r\n"; + + /** + * Convert an HTTP header string into a JSONObject. It can be a request + * header or a response header. A request header will contain + *

{
+     *    Method: "POST" (for example),
+     *    "Request-URI": "/" (for example),
+     *    "HTTP-Version": "HTTP/1.1" (for example)
+     * }
+ * A response header will contain + *
{
+     *    "HTTP-Version": "HTTP/1.1" (for example),
+     *    "Status-Code": "200" (for example),
+     *    "Reason-Phrase": "OK" (for example)
+     * }
+ * In addition, the other parameters in the header will be captured, using + * the HTTP field names as JSON names, so that
+     *    Date: Sun, 26 May 2002 18:06:04 GMT
+     *    Cookie: Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s
+     *    Cache-Control: no-cache
+ * become + *
{...
+     *    Date: "Sun, 26 May 2002 18:06:04 GMT",
+     *    Cookie: "Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s",
+     *    "Cache-Control": "no-cache",
+     * ...}
+ * It does no further checking or conversion. It does not parse dates. + * It does not do '%' transforms on URLs. + * @param string An HTTP header string. + * @return A JSONObject containing the elements and attributes + * of the XML string. + * @throws JSONException + */ + public static JSONObject toJSONObject(String string) throws JSONException { + JSONObject o = new JSONObject(); + HTTPTokener x = new HTTPTokener(string); + String t; + + t = x.nextToken(); + if (t.toUpperCase().startsWith("HTTP")) { + +// Response + + o.put("HTTP-Version", t); + o.put("Status-Code", x.nextToken()); + o.put("Reason-Phrase", x.nextTo('\0')); + x.next(); + + } else { + +// Request + + o.put("Method", t); + o.put("Request-URI", x.nextToken()); + o.put("HTTP-Version", x.nextToken()); + } + +// Fields + + while (x.more()) { + String name = x.nextTo(':'); + x.next(':'); + o.put(name, x.nextTo('\0')); + x.next(); + } + return o; + } + + + /** + * Convert a JSONObject into an HTTP header. A request header must contain + *
{
+     *    Method: "POST" (for example),
+     *    "Request-URI": "/" (for example),
+     *    "HTTP-Version": "HTTP/1.1" (for example)
+     * }
+ * A response header must contain + *
{
+     *    "HTTP-Version": "HTTP/1.1" (for example),
+     *    "Status-Code": "200" (for example),
+     *    "Reason-Phrase": "OK" (for example)
+     * }
+ * Any other members of the JSONObject will be output as HTTP fields. + * The result will end with two CRLF pairs. + * @param o A JSONObject + * @return An HTTP header string. + * @throws JSONException if the object does not contain enough + * information. + */ + public static String toString(JSONObject o) throws JSONException { + Iterator keys = o.keys(); + String s; + StringBuffer sb = new StringBuffer(); + if (o.has("Status-Code") && o.has("Reason-Phrase")) { + sb.append(o.getString("HTTP-Version")); + sb.append(' '); + sb.append(o.getString("Status-Code")); + sb.append(' '); + sb.append(o.getString("Reason-Phrase")); + } else if (o.has("Method") && o.has("Request-URI")) { + sb.append(o.getString("Method")); + sb.append(' '); + sb.append('"'); + sb.append(o.getString("Request-URI")); + sb.append('"'); + sb.append(' '); + sb.append(o.getString("HTTP-Version")); + } else { + throw new JSONException("Not enough material for an HTTP header."); + } + sb.append(CRLF); + while (keys.hasNext()) { + s = keys.next().toString(); + if (!s.equals("HTTP-Version") && !s.equals("Status-Code") && + !s.equals("Reason-Phrase") && !s.equals("Method") && + !s.equals("Request-URI") && !o.isNull(s)) { + sb.append(s); + sb.append(": "); + sb.append(o.getString(s)); + sb.append(CRLF); + } + } + sb.append(CRLF); + return sb.toString(); + } +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/HTTPTokener.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/HTTPTokener.java new file mode 100644 index 0000000000..c576462a9c --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/HTTPTokener.java @@ -0,0 +1,77 @@ +package org.gephi.streaming.impl.json.parser; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * The HTTPTokener extends the JSONTokener to provide additional methods + * for the parsing of HTTP headers. + * @author JSON.org + * @version 2008-09-18 + */ +public class HTTPTokener extends JSONTokener { + + /** + * Construct an HTTPTokener from a string. + * @param s A source string. + */ + public HTTPTokener(String s) { + super(s); + } + + + /** + * Get the next token or string. This is used in parsing HTTP headers. + * @throws JSONException + * @return A String. + */ + public String nextToken() throws JSONException { + char c; + char q; + StringBuffer sb = new StringBuffer(); + do { + c = next(); + } while (Character.isWhitespace(c)); + if (c == '"' || c == '\'') { + q = c; + for (;;) { + c = next(); + if (c < ' ') { + throw syntaxError("Unterminated string."); + } + if (c == q) { + return sb.toString(); + } + sb.append(c); + } + } + for (;;) { + if (c == 0 || Character.isWhitespace(c)) { + return sb.toString(); + } + sb.append(c); + c = next(); + } + } +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONArray.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONArray.java new file mode 100644 index 0000000000..e8d1700198 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONArray.java @@ -0,0 +1,918 @@ +package org.gephi.streaming.impl.json.parser; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +import java.io.IOException; +import java.io.Writer; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +/** + * A JSONArray is an ordered sequence of values. Its external text form is a + * string wrapped in square brackets with commas separating the values. The + * internal form is an object having get and opt + * methods for accessing the values by index, and put methods for + * adding or replacing values. The values can be any of these types: + * Boolean, JSONArray, JSONObject, + * Number, String, or the + * JSONObject.NULL object. + *

+ * The constructor can convert a JSON text into a Java object. The + * toString method converts to JSON text. + *

+ * A get method returns a value if one can be found, and throws an + * exception if one cannot be found. An opt method returns a + * default value instead of throwing an exception, and so is useful for + * obtaining optional values. + *

+ * The generic get() and opt() methods return an + * object which you can cast or query for type. There are also typed + * get and opt methods that do type checking and type + * coercion for you. + *

+ * The texts produced by the toString methods strictly conform to + * JSON syntax rules. The constructors are more forgiving in the texts they will + * accept: + *

    + *
  • An extra , (comma) may appear just + * before the closing bracket.
  • + *
  • The null value will be inserted when there + * is , (comma) elision.
  • + *
  • Strings may be quoted with ' (single + * quote).
  • + *
  • Strings do not need to be quoted at all if they do not begin with a quote + * or single quote, and if they do not contain leading or trailing spaces, + * and if they do not contain any of these characters: + * { } [ ] / \ : , = ; # and if they do not look like numbers + * and if they are not the reserved words true, + * false, or null.
  • + *
  • Values can be separated by ; (semicolon) as + * well as by , (comma).
  • + *
  • Numbers may have the + * 0x- (hex) prefix.
  • + *
+ + * @author JSON.org + * @version 2009-04-14 + */ +public class JSONArray { + + + /** + * The arrayList where the JSONArray's properties are kept. + */ + private ArrayList myArrayList; + + + /** + * Construct an empty JSONArray. + */ + public JSONArray() { + this.myArrayList = new ArrayList(); + } + + /** + * Construct a JSONArray from a JSONTokener. + * @param x A JSONTokener + * @throws JSONException If there is a syntax error. + */ + public JSONArray(JSONTokener x) throws JSONException { + this(); + char c = x.nextClean(); + char q; + if (c == '[') { + q = ']'; + } else if (c == '(') { + q = ')'; + } else { + throw x.syntaxError("A JSONArray text must start with '['"); + } + if (x.nextClean() == ']') { + return; + } + x.back(); + for (;;) { + if (x.nextClean() == ',') { + x.back(); + this.myArrayList.add(null); + } else { + x.back(); + this.myArrayList.add(x.nextValue()); + } + c = x.nextClean(); + switch (c) { + case ';': + case ',': + if (x.nextClean() == ']') { + return; + } + x.back(); + break; + case ']': + case ')': + if (q != c) { + throw x.syntaxError("Expected a '" + new Character(q) + "'"); + } + return; + default: + throw x.syntaxError("Expected a ',' or ']'"); + } + } + } + + + /** + * Construct a JSONArray from a source JSON text. + * @param source A string that begins with + * [ (left bracket) + * and ends with ] (right bracket). + * @throws JSONException If there is a syntax error. + */ + public JSONArray(String source) throws JSONException { + this(new JSONTokener(source)); + } + + + /** + * Construct a JSONArray from a Collection. + * @param collection A Collection. + */ + public JSONArray(Collection collection) { + this.myArrayList = new ArrayList(); + if (collection != null) { + Iterator iter = collection.iterator(); + while (iter.hasNext()) { + Object o = iter.next(); + this.myArrayList.add(JSONObject.wrap(o)); + } + } + } + + + /** + * Construct a JSONArray from an array + * @throws JSONException If not an array. + */ + public JSONArray(Object array) throws JSONException { + this(); + if (array.getClass().isArray()) { + int length = Array.getLength(array); + for (int i = 0; i < length; i += 1) { + this.put(JSONObject.wrap(Array.get(array, i))); + } + } else { + throw new JSONException( +"JSONArray initial value should be a string or collection or array."); + } + } + + + /** + * Get the object value associated with an index. + * @param index + * The index must be between 0 and length() - 1. + * @return An object value. + * @throws JSONException If there is no value for the index. + */ + public Object get(int index) throws JSONException { + Object o = opt(index); + if (o == null) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + return o; + } + + + /** + * Get the boolean value associated with an index. + * The string values "true" and "false" are converted to boolean. + * + * @param index The index must be between 0 and length() - 1. + * @return The truth. + * @throws JSONException If there is no value for the index or if the + * value is not convertable to boolean. + */ + public boolean getBoolean(int index) throws JSONException { + Object o = get(index); + if (o.equals(Boolean.FALSE) || + (o instanceof String && + ((String)o).equalsIgnoreCase("false"))) { + return false; + } else if (o.equals(Boolean.TRUE) || + (o instanceof String && + ((String)o).equalsIgnoreCase("true"))) { + return true; + } + throw new JSONException("JSONArray[" + index + "] is not a Boolean."); + } + + + /** + * Get the double value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException If the key is not found or if the value cannot + * be converted to a number. + */ + public double getDouble(int index) throws JSONException { + Object o = get(index); + try { + return o instanceof Number ? + ((Number)o).doubleValue() : + Double.valueOf((String)o).doubleValue(); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + + "] is not a number."); + } + } + + + /** + * Get the int value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException If the key is not found or if the value cannot + * be converted to a number. + * if the value cannot be converted to a number. + */ + public int getInt(int index) throws JSONException { + Object o = get(index); + return o instanceof Number ? + ((Number)o).intValue() : (int)getDouble(index); + } + + + /** + * Get the JSONArray associated with an index. + * @param index The index must be between 0 and length() - 1. + * @return A JSONArray value. + * @throws JSONException If there is no value for the index. or if the + * value is not a JSONArray + */ + public JSONArray getJSONArray(int index) throws JSONException { + Object o = get(index); + if (o instanceof JSONArray) { + return (JSONArray)o; + } + throw new JSONException("JSONArray[" + index + + "] is not a JSONArray."); + } + + + /** + * Get the JSONObject associated with an index. + * @param index subscript + * @return A JSONObject value. + * @throws JSONException If there is no value for the index or if the + * value is not a JSONObject + */ + public JSONObject getJSONObject(int index) throws JSONException { + Object o = get(index); + if (o instanceof JSONObject) { + return (JSONObject)o; + } + throw new JSONException("JSONArray[" + index + + "] is not a JSONObject."); + } + + + /** + * Get the long value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException If the key is not found or if the value cannot + * be converted to a number. + */ + public long getLong(int index) throws JSONException { + Object o = get(index); + return o instanceof Number ? + ((Number)o).longValue() : (long)getDouble(index); + } + + + /** + * Get the string associated with an index. + * @param index The index must be between 0 and length() - 1. + * @return A string value. + * @throws JSONException If there is no value for the index. + */ + public String getString(int index) throws JSONException { + return get(index).toString(); + } + + + /** + * Determine if the value is null. + * @param index The index must be between 0 and length() - 1. + * @return true if the value at the index is null, or if there is no value. + */ + public boolean isNull(int index) { + return JSONObject.NULL.equals(opt(index)); + } + + + /** + * Make a string from the contents of this JSONArray. The + * separator string is inserted between each element. + * Warning: This method assumes that the data structure is acyclical. + * @param separator A string that will be inserted between the elements. + * @return a string. + * @throws JSONException If the array contains an invalid number. + */ + public String join(String separator) throws JSONException { + int len = length(); + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < len; i += 1) { + if (i > 0) { + sb.append(separator); + } + sb.append(JSONObject.valueToString(this.myArrayList.get(i))); + } + return sb.toString(); + } + + + /** + * Get the number of elements in the JSONArray, included nulls. + * + * @return The length (or size). + */ + public int length() { + return this.myArrayList.size(); + } + + + /** + * Get the optional object value associated with an index. + * @param index The index must be between 0 and length() - 1. + * @return An object value, or null if there is no + * object at that index. + */ + public Object opt(int index) { + return (index < 0 || index >= length()) ? + null : this.myArrayList.get(index); + } + + + /** + * Get the optional boolean value associated with an index. + * It returns false if there is no value at that index, + * or if the value is not Boolean.TRUE or the String "true". + * + * @param index The index must be between 0 and length() - 1. + * @return The truth. + */ + public boolean optBoolean(int index) { + return optBoolean(index, false); + } + + + /** + * Get the optional boolean value associated with an index. + * It returns the defaultValue if there is no value at that index or if + * it is not a Boolean or the String "true" or "false" (case insensitive). + * + * @param index The index must be between 0 and length() - 1. + * @param defaultValue A boolean default. + * @return The truth. + */ + public boolean optBoolean(int index, boolean defaultValue) { + try { + return getBoolean(index); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get the optional double value associated with an index. + * NaN is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + */ + public double optDouble(int index) { + return optDouble(index, Double.NaN); + } + + + /** + * Get the optional double value associated with an index. + * The defaultValue is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * + * @param index subscript + * @param defaultValue The default value. + * @return The value. + */ + public double optDouble(int index, double defaultValue) { + try { + return getDouble(index); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get the optional int value associated with an index. + * Zero is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + */ + public int optInt(int index) { + return optInt(index, 0); + } + + + /** + * Get the optional int value associated with an index. + * The defaultValue is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return The value. + */ + public int optInt(int index, int defaultValue) { + try { + return getInt(index); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get the optional JSONArray associated with an index. + * @param index subscript + * @return A JSONArray value, or null if the index has no value, + * or if the value is not a JSONArray. + */ + public JSONArray optJSONArray(int index) { + Object o = opt(index); + return o instanceof JSONArray ? (JSONArray)o : null; + } + + + /** + * Get the optional JSONObject associated with an index. + * Null is returned if the key is not found, or null if the index has + * no value, or if the value is not a JSONObject. + * + * @param index The index must be between 0 and length() - 1. + * @return A JSONObject value. + */ + public JSONObject optJSONObject(int index) { + Object o = opt(index); + return o instanceof JSONObject ? (JSONObject)o : null; + } + + + /** + * Get the optional long value associated with an index. + * Zero is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + */ + public long optLong(int index) { + return optLong(index, 0); + } + + + /** + * Get the optional long value associated with an index. + * The defaultValue is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return The value. + */ + public long optLong(int index, long defaultValue) { + try { + return getLong(index); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get the optional string value associated with an index. It returns an + * empty string if there is no value at that index. If the value + * is not a string and is not null, then it is coverted to a string. + * + * @param index The index must be between 0 and length() - 1. + * @return A String value. + */ + public String optString(int index) { + return optString(index, ""); + } + + + /** + * Get the optional string associated with an index. + * The defaultValue is returned if the key is not found. + * + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return A String value. + */ + public String optString(int index, String defaultValue) { + Object o = opt(index); + return o != null ? o.toString() : defaultValue; + } + + + /** + * Append a boolean value. This increases the array's length by one. + * + * @param value A boolean value. + * @return this. + */ + public JSONArray put(boolean value) { + put(value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + + /** + * Put a value in the JSONArray, where the value will be a + * JSONArray which is produced from a Collection. + * @param value A Collection value. + * @return this. + */ + public JSONArray put(Collection value) { + put(new JSONArray(value)); + return this; + } + + + /** + * Append a double value. This increases the array's length by one. + * + * @param value A double value. + * @throws JSONException if the value is not finite. + * @return this. + */ + public JSONArray put(double value) throws JSONException { + Double d = new Double(value); + JSONObject.testValidity(d); + put(d); + return this; + } + + + /** + * Append an int value. This increases the array's length by one. + * + * @param value An int value. + * @return this. + */ + public JSONArray put(int value) { + put(new Integer(value)); + return this; + } + + + /** + * Append an long value. This increases the array's length by one. + * + * @param value A long value. + * @return this. + */ + public JSONArray put(long value) { + put(new Long(value)); + return this; + } + + + /** + * Put a value in the JSONArray, where the value will be a + * JSONObject which is produced from a Map. + * @param value A Map value. + * @return this. + */ + public JSONArray put(Map value) { + put(new JSONObject(value)); + return this; + } + + + /** + * Append an object value. This increases the array's length by one. + * @param value An object value. The value should be a + * Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the + * JSONObject.NULL object. + * @return this. + */ + public JSONArray put(Object value) { + this.myArrayList.add(value); + return this; + } + + + /** + * Put or replace a boolean value in the JSONArray. If the index is greater + * than the length of the JSONArray, then null elements will be added as + * necessary to pad it out. + * @param index The subscript. + * @param value A boolean value. + * @return this. + * @throws JSONException If the index is negative. + */ + public JSONArray put(int index, boolean value) throws JSONException { + put(index, value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + + /** + * Put a value in the JSONArray, where the value will be a + * JSONArray which is produced from a Collection. + * @param index The subscript. + * @param value A Collection value. + * @return this. + * @throws JSONException If the index is negative or if the value is + * not finite. + */ + public JSONArray put(int index, Collection value) throws JSONException { + put(index, new JSONArray(value)); + return this; + } + + + /** + * Put or replace a double value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad + * it out. + * @param index The subscript. + * @param value A double value. + * @return this. + * @throws JSONException If the index is negative or if the value is + * not finite. + */ + public JSONArray put(int index, double value) throws JSONException { + put(index, new Double(value)); + return this; + } + + + /** + * Put or replace an int value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad + * it out. + * @param index The subscript. + * @param value An int value. + * @return this. + * @throws JSONException If the index is negative. + */ + public JSONArray put(int index, int value) throws JSONException { + put(index, new Integer(value)); + return this; + } + + + /** + * Put or replace a long value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad + * it out. + * @param index The subscript. + * @param value A long value. + * @return this. + * @throws JSONException If the index is negative. + */ + public JSONArray put(int index, long value) throws JSONException { + put(index, new Long(value)); + return this; + } + + + /** + * Put a value in the JSONArray, where the value will be a + * JSONObject which is produced from a Map. + * @param index The subscript. + * @param value The Map value. + * @return this. + * @throws JSONException If the index is negative or if the the value is + * an invalid number. + */ + public JSONArray put(int index, Map value) throws JSONException { + put(index, new JSONObject(value)); + return this; + } + + + /** + * Put or replace an object value in the JSONArray. If the index is greater + * than the length of the JSONArray, then null elements will be added as + * necessary to pad it out. + * @param index The subscript. + * @param value The value to put into the array. The value should be a + * Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the + * JSONObject.NULL object. + * @return this. + * @throws JSONException If the index is negative or if the the value is + * an invalid number. + */ + public JSONArray put(int index, Object value) throws JSONException { + JSONObject.testValidity(value); + if (index < 0) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + if (index < length()) { + this.myArrayList.set(index, value); + } else { + while (index != length()) { + put(JSONObject.NULL); + } + put(value); + } + return this; + } + + + /** + * Remove an index and close the hole. + * @param index The index of the element to be removed. + * @return The value that was associated with the index, + * or null if there was no value. + */ + public Object remove(int index) { + Object o = opt(index); + this.myArrayList.remove(index); + return o; + } + + + /** + * Produce a JSONObject by combining a JSONArray of names with the values + * of this JSONArray. + * @param names A JSONArray containing a list of key strings. These will be + * paired with the values. + * @return A JSONObject, or null if there are no names or if this JSONArray + * has no values. + * @throws JSONException If any of the names are null. + */ + public JSONObject toJSONObject(JSONArray names) throws JSONException { + if (names == null || names.length() == 0 || length() == 0) { + return null; + } + JSONObject jo = new JSONObject(); + for (int i = 0; i < names.length(); i += 1) { + jo.put(names.getString(i), this.opt(i)); + } + return jo; + } + + + /** + * Make a JSON text of this JSONArray. For compactness, no + * unnecessary whitespace is added. If it is not possible to produce a + * syntactically correct JSON text then null will be returned instead. This + * could occur if the array contains an invalid number. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, transmittable + * representation of the array. + */ + public String toString() { + try { + return '[' + join(",") + ']'; + } catch (Exception e) { + return null; + } + } + + + /** + * Make a prettyprinted JSON text of this JSONArray. + * Warning: This method assumes that the data structure is acyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with [ (left bracket) and ending + * with ] (right bracket). + * @throws JSONException + */ + public String toString(int indentFactor) throws JSONException { + return toString(indentFactor, 0); + } + + + /** + * Make a prettyprinted JSON text of this JSONArray. + * Warning: This method assumes that the data structure is acyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @param indent The indention of the top level. + * @return a printable, displayable, transmittable + * representation of the array. + * @throws JSONException + */ + String toString(int indentFactor, int indent) throws JSONException { + int len = length(); + if (len == 0) { + return "[]"; + } + int i; + StringBuffer sb = new StringBuffer("["); + if (len == 1) { + sb.append(JSONObject.valueToString(this.myArrayList.get(0), + indentFactor, indent)); + } else { + int newindent = indent + indentFactor; + sb.append('\n'); + for (i = 0; i < len; i += 1) { + if (i > 0) { + sb.append(",\n"); + } + for (int j = 0; j < newindent; j += 1) { + sb.append(' '); + } + sb.append(JSONObject.valueToString(this.myArrayList.get(i), + indentFactor, newindent)); + } + sb.append('\n'); + for (i = 0; i < indent; i += 1) { + sb.append(' '); + } + } + sb.append(']'); + return sb.toString(); + } + + + /** + * Write the contents of the JSONArray as JSON text to a writer. + * For compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return The writer. + * @throws JSONException + */ + public Writer write(Writer writer) throws JSONException { + try { + boolean b = false; + int len = length(); + + writer.write('['); + + for (int i = 0; i < len; i += 1) { + if (b) { + writer.write(','); + } + Object v = this.myArrayList.get(i); + if (v instanceof JSONObject) { + ((JSONObject)v).write(writer); + } else if (v instanceof JSONArray) { + ((JSONArray)v).write(writer); + } else { + writer.write(JSONObject.valueToString(v)); + } + b = true; + } + writer.write(']'); + return writer; + } catch (IOException e) { + throw new JSONException(e); + } + } +} \ No newline at end of file diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONConstants.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONConstants.java new file mode 100644 index 0000000000..7e669f7068 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONConstants.java @@ -0,0 +1,65 @@ +package org.gephi.streaming.impl.json.parser; + +import org.gephi.streaming.api.event.ElementType; +import org.gephi.streaming.api.event.EventType; + +public class JSONConstants { + + public enum Types { + AN("an", EventType.ADD, ElementType.NODE), + CN("cn", EventType.CHANGE, ElementType.NODE), + DN("dn", EventType.REMOVE, ElementType.NODE), + AE("ae", EventType.ADD, ElementType.EDGE), + CE("ce", EventType.CHANGE, ElementType.EDGE), + DE("de", EventType.REMOVE, ElementType.EDGE), + CG("cg", EventType.CHANGE, ElementType.GRAPH); + + private String value; + private EventType eventType; + private ElementType elementType; + private Types(String value, EventType eventType, ElementType elementType) { + this.value = value; + this.eventType = eventType; + this.elementType = elementType; + } + + public String value() { + return value; + } + + public EventType getEventType() { + return eventType; + } + + public ElementType getElementType() { + return elementType; + } + + public static Types fromString(String strtype) { + for (Types type: Types.values()) { + if (type.value.equalsIgnoreCase(strtype)) { + return type; + } + } + throw new IllegalArgumentException("Invalid type"); + } + } + + public enum Fields { + + ID("id"), + T("t"), //timestamp + SOURCE("source"), + TARGET("target"), + DIRECTED("directed"); + private String value; + + private Fields(String value) { + this.value = value; + } + + public String value() { + return value; + } + }; +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONException.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONException.java new file mode 100644 index 0000000000..16fbc9131c --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONException.java @@ -0,0 +1,31 @@ +package org.gephi.streaming.impl.json.parser; + +/** + * The JSONException is thrown by the JSON.org classes when things are amiss. + * @author JSON.org + * @version 2008-09-18 + */ +public class JSONException extends Exception { + /** + * + */ + private static final long serialVersionUID = 0; + private Throwable cause; + + /** + * Constructs a JSONException with an explanatory message. + * @param message Detail about the reason for the exception. + */ + public JSONException(String message) { + super(message); + } + + public JSONException(Throwable t) { + super(t.getMessage()); + this.cause = t; + } + + public Throwable getCause() { + return this.cause; + } +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONML.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONML.java new file mode 100644 index 0000000000..8aa0f0c46b --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONML.java @@ -0,0 +1,455 @@ +package org.gephi.streaming.impl.json.parser; + +/* +Copyright (c) 2008 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +import java.util.Iterator; + + +/** + * This provides static methods to convert an XML text into a JSONArray or + * JSONObject, and to covert a JSONArray or JSONObject into an XML text using + * the JsonML transform. + * @author JSON.org + * @version 2010-02-12 + */ +public class JSONML { + + /** + * Parse XML values and store them in a JSONArray. + * @param x The XMLTokener containing the source string. + * @param arrayForm true if array form, false if object form. + * @param ja The JSONArray that is containing the current tag or null + * if we are at the outermost level. + * @return A JSONArray if the value is the outermost tag, otherwise null. + * @throws JSONException + */ + private static Object parse(XMLTokener x, boolean arrayForm, + JSONArray ja) throws JSONException { + String attribute; + char c; + String closeTag = null; + int i; + JSONArray newja = null; + JSONObject newjo = null; + Object token; + String tagName = null; + +// Test for and skip past these forms: +// +// +// +// + + while (true) { + token = x.nextContent(); + if (token == XML.LT) { + token = x.nextToken(); + if (token instanceof Character) { + if (token == XML.SLASH) { + +// Close tag "); + } + x.back(); + } else if (c == '[') { + token = x.nextToken(); + if (token.equals("CDATA") && x.next() == '[') { + if (ja != null) { + ja.put(x.nextCDATA()); + } + } else { + throw x.syntaxError("Expected 'CDATA['"); + } + } else { + i = 1; + do { + token = x.nextMeta(); + if (token == null) { + throw x.syntaxError("Missing '>' after ' 0); + } + } else if (token == XML.QUEST) { + +// "); + } else { + throw x.syntaxError("Misshaped tag"); + } + +// Open tag < + + } else { + if (!(token instanceof String)) { + throw x.syntaxError("Bad tagName '" + token + "'."); + } + tagName = (String)token; + newja = new JSONArray(); + newjo = new JSONObject(); + if (arrayForm) { + newja.put(tagName); + if (ja != null) { + ja.put(newja); + } + } else { + newjo.put("tagName", tagName); + if (ja != null) { + ja.put(newjo); + } + } + token = null; + for (;;) { + if (token == null) { + token = x.nextToken(); + } + if (token == null) { + throw x.syntaxError("Misshaped tag"); + } + if (!(token instanceof String)) { + break; + } + +// attribute = value + + attribute = (String)token; + if (!arrayForm && (attribute == "tagName" || attribute == "childNode")) { + throw x.syntaxError("Reserved attribute."); + } + token = x.nextToken(); + if (token == XML.EQ) { + token = x.nextToken(); + if (!(token instanceof String)) { + throw x.syntaxError("Missing value"); + } + newjo.accumulate(attribute, JSONObject.stringToValue((String)token)); + token = null; + } else { + newjo.accumulate(attribute, ""); + } + } + if (arrayForm && newjo.length() > 0) { + newja.put(newjo); + } + +// Empty tag <.../> + + if (token == XML.SLASH) { + if (x.nextToken() != XML.GT) { + throw x.syntaxError("Misshaped tag"); + } + if (ja == null) { + if (arrayForm) { + return newja; + } else { + return newjo; + } + } + +// Content, between <...> and + + } else { + if (token != XML.GT) { + throw x.syntaxError("Misshaped tag"); + } + closeTag = (String)parse(x, arrayForm, newja); + if (closeTag != null) { + if (!closeTag.equals(tagName)) { + throw x.syntaxError("Mismatched '" + tagName + + "' and '" + closeTag + "'"); + } + tagName = null; + if (!arrayForm && newja.length() > 0) { + newjo.put("childNodes", newja); + } + if (ja == null) { + if (arrayForm) { + return newja; + } else { + return newjo; + } + } + } + } + } + } else { + if (ja != null) { + ja.put(token instanceof String ? + JSONObject.stringToValue((String)token) : token); + } + } + } + } + + + /** + * Convert a well-formed (but not necessarily valid) XML string into a + * JSONArray using the JsonML transform. Each XML tag is represented as + * a JSONArray in which the first element is the tag name. If the tag has + * attributes, then the second element will be JSONObject containing the + * name/value pairs. If the tag contains children, then strings and + * JSONArrays will represent the child tags. + * Comments, prologs, DTDs, and <[ [ ]]> are ignored. + * @param string The source string. + * @return A JSONArray containing the structured data from the XML string. + * @throws JSONException + */ + public static JSONArray toJSONArray(String string) throws JSONException { + return toJSONArray(new XMLTokener(string)); + } + + + /** + * Convert a well-formed (but not necessarily valid) XML string into a + * JSONArray using the JsonML transform. Each XML tag is represented as + * a JSONArray in which the first element is the tag name. If the tag has + * attributes, then the second element will be JSONObject containing the + * name/value pairs. If the tag contains children, then strings and + * JSONArrays will represent the child content and tags. + * Comments, prologs, DTDs, and <[ [ ]]> are ignored. + * @param x An XMLTokener. + * @return A JSONArray containing the structured data from the XML string. + * @throws JSONException + */ + public static JSONArray toJSONArray(XMLTokener x) throws JSONException { + return (JSONArray)parse(x, true, null); + } + + + + /** + * Convert a well-formed (but not necessarily valid) XML string into a + * JSONObject using the JsonML transform. Each XML tag is represented as + * a JSONObject with a "tagName" property. If the tag has attributes, then + * the attributes will be in the JSONObject as properties. If the tag + * contains children, the object will have a "childNodes" property which + * will be an array of strings and JsonML JSONObjects. + + * Comments, prologs, DTDs, and <[ [ ]]> are ignored. + * @param x An XMLTokener of the XML source text. + * @return A JSONObject containing the structured data from the XML string. + * @throws JSONException + */ + public static JSONObject toJSONObject(XMLTokener x) throws JSONException { + return (JSONObject)parse(x, false, null); + } + /** + * Convert a well-formed (but not necessarily valid) XML string into a + * JSONObject using the JsonML transform. Each XML tag is represented as + * a JSONObject with a "tagName" property. If the tag has attributes, then + * the attributes will be in the JSONObject as properties. If the tag + * contains children, the object will have a "childNodes" property which + * will be an array of strings and JsonML JSONObjects. + + * Comments, prologs, DTDs, and <[ [ ]]> are ignored. + * @param string The XML source text. + * @return A JSONObject containing the structured data from the XML string. + * @throws JSONException + */ + public static JSONObject toJSONObject(String string) throws JSONException { + return toJSONObject(new XMLTokener(string)); + } + + + /** + * Reverse the JSONML transformation, making an XML text from a JSONArray. + * @param ja A JSONArray. + * @return An XML string. + * @throws JSONException + */ + public static String toString(JSONArray ja) throws JSONException { + Object e; + int i; + JSONObject jo; + String k; + Iterator keys; + int length; + StringBuffer sb = new StringBuffer(); + String tagName; + String v; + +// Emit = length) { + sb.append('/'); + sb.append('>'); + } else { + sb.append('>'); + do { + e = ja.get(i); + i += 1; + if (e != null) { + if (e instanceof String) { + sb.append(XML.escape(e.toString())); + } else if (e instanceof JSONObject) { + sb.append(toString((JSONObject)e)); + } else if (e instanceof JSONArray) { + sb.append(toString((JSONArray)e)); + } + } + } while (i < length); + sb.append('<'); + sb.append('/'); + sb.append(tagName); + sb.append('>'); + } + return sb.toString(); + } + + /** + * Reverse the JSONML transformation, making an XML text from a JSONObject. + * The JSONObject must contain a "tagName" property. If it has children, + * then it must have a "childNodes" property containing an array of objects. + * The other properties are attributes with string values. + * @param jo A JSONObject. + * @return An XML string. + * @throws JSONException + */ + public static String toString(JSONObject jo) throws JSONException { + StringBuffer sb = new StringBuffer(); + Object e; + int i; + JSONArray ja; + String k; + Iterator keys; + int len; + String tagName; + String v; + +//Emit '); + } else { + sb.append('>'); + len = ja.length(); + for (i = 0; i < len; i += 1) { + e = ja.get(i); + if (e != null) { + if (e instanceof String) { + sb.append(XML.escape(e.toString())); + } else if (e instanceof JSONObject) { + sb.append(toString((JSONObject)e)); + } else if (e instanceof JSONArray) { + sb.append(toString((JSONArray)e)); + } + } + } + sb.append('<'); + sb.append('/'); + sb.append(tagName); + sb.append('>'); + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONObject.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONObject.java new file mode 100644 index 0000000000..461fd0622a --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONObject.java @@ -0,0 +1,1584 @@ +package org.gephi.streaming.impl.json.parser; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +import java.io.IOException; +import java.io.Writer; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeSet; + +/** + * A JSONObject is an unordered collection of name/value pairs. Its + * external form is a string wrapped in curly braces with colons between the + * names and values, and commas between the values and names. The internal form + * is an object having get and opt methods for + * accessing the values by name, and put methods for adding or + * replacing values by name. The values can be any of these types: + * Boolean, JSONArray, JSONObject, + * Number, String, or the JSONObject.NULL + * object. A JSONObject constructor can be used to convert an external form + * JSON text into an internal form whose values can be retrieved with the + * get and opt methods, or to convert values into a + * JSON text using the put and toString methods. + * A get method returns a value if one can be found, and throws an + * exception if one cannot be found. An opt method returns a + * default value instead of throwing an exception, and so is useful for + * obtaining optional values. + *

+ * The generic get() and opt() methods return an + * object, which you can cast or query for type. There are also typed + * get and opt methods that do type checking and type + * coercion for you. + *

+ * The put methods adds values to an object. For example,

+ *     myString = new JSONObject().put("JSON", "Hello, World!").toString();
+ * produces the string {"JSON": "Hello, World"}. + *

+ * The texts produced by the toString methods strictly conform to + * the JSON syntax rules. + * The constructors are more forgiving in the texts they will accept: + *

    + *
  • An extra , (comma) may appear just + * before the closing brace.
  • + *
  • Strings may be quoted with ' (single + * quote).
  • + *
  • Strings do not need to be quoted at all if they do not begin with a quote + * or single quote, and if they do not contain leading or trailing spaces, + * and if they do not contain any of these characters: + * { } [ ] / \ : , = ; # and if they do not look like numbers + * and if they are not the reserved words true, + * false, or null.
  • + *
  • Keys can be followed by = or => as well as + * by :.
  • + *
  • Values can be followed by ; (semicolon) as + * well as by , (comma).
  • + *
  • Numbers may have the 0x- (hex) prefix.
  • + *
+ * @author JSON.org + * @version 2010-05-17 + */ +public class JSONObject { + + /** + * JSONObject.NULL is equivalent to the value that JavaScript calls null, + * whilst Java's null is equivalent to the value that JavaScript calls + * undefined. + */ + private static final class Null { + + /** + * There is only intended to be a single instance of the NULL object, + * so the clone method returns itself. + * @return NULL. + */ + protected final Object clone() { + return this; + } + + + /** + * A Null object is equal to the null value and to itself. + * @param object An object to test for nullness. + * @return true if the object parameter is the JSONObject.NULL object + * or null. + */ + public boolean equals(Object object) { + return object == null || object == this; + } + + + /** + * Get the "null" string value. + * @return The string "null". + */ + public String toString() { + return "null"; + } + } + + + /** + * The map where the JSONObject's properties are kept. + */ + private Map map; + + + /** + * It is sometimes more convenient and less ambiguous to have a + * NULL object than to use Java's null value. + * JSONObject.NULL.equals(null) returns true. + * JSONObject.NULL.toString() returns "null". + */ + public static final Object NULL = new Null(); + + + /** + * Construct an empty JSONObject. + */ + public JSONObject() { + this.map = new HashMap(); + } + + + /** + * Construct a JSONObject from a subset of another JSONObject. + * An array of strings is used to identify the keys that should be copied. + * Missing keys are ignored. + * @param jo A JSONObject. + * @param names An array of strings. + * @throws JSONException + * @exception JSONException If a value is a non-finite number or if a name is duplicated. + */ + public JSONObject(JSONObject jo, String[] names) { + this(); + for (int i = 0; i < names.length; i += 1) { + try { + putOnce(names[i], jo.opt(names[i])); + } catch (Exception ignore) { + } + } + } + + + /** + * Construct a JSONObject from a JSONTokener. + * @param x A JSONTokener object containing the source string. + * @throws JSONException If there is a syntax error in the source string + * or a duplicated key. + */ + public JSONObject(JSONTokener x) throws JSONException { + this(); + char c; + String key; + + if (x.nextClean() != '{') { + throw x.syntaxError("A JSONObject text must begin with '{'"); + } + for (;;) { + c = x.nextClean(); + switch (c) { + case 0: + throw x.syntaxError("A JSONObject text must end with '}'"); + case '}': + return; + default: + x.back(); + key = x.nextValue().toString(); + } + + /* + * The key is followed by ':'. We will also tolerate '=' or '=>'. + */ + + c = x.nextClean(); + if (c == '=') { + if (x.next() != '>') { + x.back(); + } + } else if (c != ':') { + throw x.syntaxError("Expected a ':' after a key"); + } + putOnce(key, x.nextValue()); + + /* + * Pairs are separated by ','. We will also tolerate ';'. + */ + + switch (x.nextClean()) { + case ';': + case ',': + if (x.nextClean() == '}') { + return; + } + x.back(); + break; + case '}': + return; + default: + throw x.syntaxError("Expected a ',' or '}'"); + } + } + } + + + /** + * Construct a JSONObject from a Map. + * + * @param map A map object that can be used to initialize the contents of + * the JSONObject. + * @throws JSONException + */ + public JSONObject(Map map) { + this.map = new HashMap(); + if (map != null) { + Iterator> i = map.entrySet().iterator(); + while (i.hasNext()) { + Map.Entry e = i.next(); + this.map.put(e.getKey(), wrap(e.getValue())); + } + } + } + + + /** + * Construct a JSONObject from an Object using bean getters. + * It reflects on all of the public methods of the object. + * For each of the methods with no parameters and a name starting + * with "get" or "is" followed by an uppercase letter, + * the method is invoked, and a key and the value returned from the getter method + * are put into the new JSONObject. + * + * The key is formed by removing the "get" or "is" prefix. + * If the second remaining character is not upper case, then the first + * character is converted to lower case. + * + * For example, if an object has a method named "getName", and + * if the result of calling object.getName() is "Larry Fine", + * then the JSONObject will contain "name": "Larry Fine". + * + * @param bean An object that has getter methods that should be used + * to make a JSONObject. + */ + public JSONObject(Object bean) { + this(); + populateMap(bean); + } + + + /** + * Construct a JSONObject from an Object, using reflection to find the + * public members. The resulting JSONObject's keys will be the strings + * from the names array, and the values will be the field values associated + * with those keys in the object. If a key is not found or not visible, + * then it will not be copied into the new JSONObject. + * @param object An object that has fields that should be used to make a + * JSONObject. + * @param names An array of strings, the names of the fields to be obtained + * from the object. + */ + public JSONObject(Object object, String names[]) { + this(); + Class c = object.getClass(); + for (int i = 0; i < names.length; i += 1) { + String name = names[i]; + try { + putOpt(name, c.getField(name).get(object)); + } catch (Exception ignore) { + } + } + } + + + /** + * Construct a JSONObject from a source JSON text string. + * This is the most commonly used JSONObject constructor. + * @param source A string beginning + * with { (left brace) and ending + * with } (right brace). + * @exception JSONException If there is a syntax error in the source + * string or a duplicated key. + */ + public JSONObject(String source) throws JSONException { + this(new JSONTokener(source)); + } + + + /** + * Accumulate values under a key. It is similar to the put method except + * that if there is already an object stored under the key then a + * JSONArray is stored under the key to hold all of the accumulated values. + * If there is already a JSONArray, then the new value is appended to it. + * In contrast, the put method replaces the previous value. + * @param key A key string. + * @param value An object to be accumulated under the key. + * @return this. + * @throws JSONException If the value is an invalid number + * or if the key is null. + */ + public JSONObject accumulate(String key, Object value) + throws JSONException { + testValidity(value); + Object o = opt(key); + if (o == null) { + put(key, value instanceof JSONArray ? + new JSONArray().put(value) : + value); + } else if (o instanceof JSONArray) { + ((JSONArray)o).put(value); + } else { + put(key, new JSONArray().put(o).put(value)); + } + return this; + } + + + /** + * Append values to the array under a key. If the key does not exist in the + * JSONObject, then the key is put in the JSONObject with its value being a + * JSONArray containing the value parameter. If the key was already + * associated with a JSONArray, then the value parameter is appended to it. + * @param key A key string. + * @param value An object to be accumulated under the key. + * @return this. + * @throws JSONException If the key is null or if the current value + * associated with the key is not a JSONArray. + */ + public JSONObject append(String key, Object value) throws JSONException { + testValidity(value); + Object o = opt(key); + if (o == null) { + put(key, new JSONArray().put(value)); + } else if (o instanceof JSONArray) { + put(key, ((JSONArray)o).put(value)); + } else { + throw new JSONException("JSONObject[" + key + + "] is not a JSONArray."); + } + return this; + } + + + /** + * Produce a string from a double. The string "null" will be returned if + * the number is not finite. + * @param d A double. + * @return A String. + */ + static public String doubleToString(double d) { + if (Double.isInfinite(d) || Double.isNaN(d)) { + return "null"; + } + +// Shave off trailing zeros and decimal point, if possible. + + String s = Double.toString(d); + if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) { + while (s.endsWith("0")) { + s = s.substring(0, s.length() - 1); + } + if (s.endsWith(".")) { + s = s.substring(0, s.length() - 1); + } + } + return s; + } + + + /** + * Get the value object associated with a key. + * + * @param key A key string. + * @return The object associated with the key. + * @throws JSONException if the key is not found. + */ + public Object get(String key) throws JSONException { + Object o = opt(key); + if (o == null) { + throw new JSONException("JSONObject[" + quote(key) + + "] not found."); + } + return o; + } + + + /** + * Get the boolean value associated with a key. + * + * @param key A key string. + * @return The truth. + * @throws JSONException + * if the value is not a Boolean or the String "true" or "false". + */ + public boolean getBoolean(String key) throws JSONException { + Object o = get(key); + if (o.equals(Boolean.FALSE) || + (o instanceof String && + ((String)o).equalsIgnoreCase("false"))) { + return false; + } else if (o.equals(Boolean.TRUE) || + (o instanceof String && + ((String)o).equalsIgnoreCase("true"))) { + return true; + } + throw new JSONException("JSONObject[" + quote(key) + + "] is not a Boolean."); + } + + + /** + * Get the double value associated with a key. + * @param key A key string. + * @return The numeric value. + * @throws JSONException if the key is not found or + * if the value is not a Number object and cannot be converted to a number. + */ + public double getDouble(String key) throws JSONException { + Object o = get(key); + try { + return o instanceof Number ? + ((Number)o).doubleValue() : + Double.valueOf((String)o).doubleValue(); + } catch (Exception e) { + throw new JSONException("JSONObject[" + quote(key) + + "] is not a number."); + } + } + + + /** + * Get the int value associated with a key. + * + * @param key A key string. + * @return The integer value. + * @throws JSONException if the key is not found or if the value cannot + * be converted to an integer. + */ + public int getInt(String key) throws JSONException { + Object o = get(key); + try { + return o instanceof Number ? + ((Number)o).intValue() : + Integer.parseInt((String)o); + } catch (Exception e) { + throw new JSONException("JSONObject[" + quote(key) + + "] is not an int."); + } + } + + + /** + * Get the JSONArray value associated with a key. + * + * @param key A key string. + * @return A JSONArray which is the value. + * @throws JSONException if the key is not found or + * if the value is not a JSONArray. + */ + public JSONArray getJSONArray(String key) throws JSONException { + Object o = get(key); + if (o instanceof JSONArray) { + return (JSONArray)o; + } + throw new JSONException("JSONObject[" + quote(key) + + "] is not a JSONArray."); + } + + + /** + * Get the JSONObject value associated with a key. + * + * @param key A key string. + * @return A JSONObject which is the value. + * @throws JSONException if the key is not found or + * if the value is not a JSONObject. + */ + public JSONObject getJSONObject(String key) throws JSONException { + Object o = get(key); + if (o instanceof JSONObject) { + return (JSONObject)o; + } + throw new JSONException("JSONObject[" + quote(key) + + "] is not a JSONObject."); + } + + + /** + * Get the long value associated with a key. + * + * @param key A key string. + * @return The long value. + * @throws JSONException if the key is not found or if the value cannot + * be converted to a long. + */ + public long getLong(String key) throws JSONException { + Object o = get(key); + try { + return o instanceof Number ? + ((Number)o).longValue() : + Long.parseLong((String)o); + } catch (Exception e) { + throw new JSONException("JSONObject[" + quote(key) + + "] is not a long."); + } + } + + + /** + * Get an array of field names from a JSONObject. + * + * @return An array of field names, or null if there are no names. + */ + public static String[] getNames(JSONObject jo) { + int length = jo.length(); + if (length == 0) { + return null; + } + Iterator i = jo.keys(); + String[] names = new String[length]; + int j = 0; + while (i.hasNext()) { + names[j] = (String)i.next(); + j += 1; + } + return names; + } + + + /** + * Get an array of field names from an Object. + * + * @return An array of field names, or null if there are no names. + */ + public static String[] getNames(Object object) { + if (object == null) { + return null; + } + Class klass = object.getClass(); + Field[] fields = klass.getFields(); + int length = fields.length; + if (length == 0) { + return null; + } + String[] names = new String[length]; + for (int i = 0; i < length; i += 1) { + names[i] = fields[i].getName(); + } + return names; + } + + + /** + * Get the string associated with a key. + * + * @param key A key string. + * @return A string which is the value. + * @throws JSONException if the key is not found. + */ + public String getString(String key) throws JSONException { + return get(key).toString(); + } + + + /** + * Determine if the JSONObject contains a specific key. + * @param key A key string. + * @return true if the key exists in the JSONObject. + */ + public boolean has(String key) { + return this.map.containsKey(key); + } + + + /** + * Increment a property of a JSONObject. If there is no such property, + * create one with a value of 1. If there is such a property, and if + * it is an Integer, Long, Double, or Float, then add one to it. + * @param key A key string. + * @return this. + * @throws JSONException If there is already a property with this name + * that is not an Integer, Long, Double, or Float. + */ + public JSONObject increment(String key) throws JSONException { + Object value = opt(key); + if (value == null) { + put(key, 1); + } else { + if (value instanceof Integer) { + put(key, ((Integer)value).intValue() + 1); + } else if (value instanceof Long) { + put(key, ((Long)value).longValue() + 1); + } else if (value instanceof Double) { + put(key, ((Double)value).doubleValue() + 1); + } else if (value instanceof Float) { + put(key, ((Float)value).floatValue() + 1); + } else { + throw new JSONException("Unable to increment [" + key + "]."); + } + } + return this; + } + + + /** + * Determine if the value associated with the key is null or if there is + * no value. + * @param key A key string. + * @return true if there is no value associated with the key or if + * the value is the JSONObject.NULL object. + */ + public boolean isNull(String key) { + return JSONObject.NULL.equals(opt(key)); + } + + + /** + * Get an enumeration of the keys of the JSONObject. + * + * @return An iterator of the keys. + */ + public Iterator keys() { + return this.map.keySet().iterator(); + } + + + /** + * Get the number of keys stored in the JSONObject. + * + * @return The number of keys in the JSONObject. + */ + public int length() { + return this.map.size(); + } + + + /** + * Produce a JSONArray containing the names of the elements of this + * JSONObject. + * @return A JSONArray containing the key strings, or null if the JSONObject + * is empty. + */ + public JSONArray names() { + JSONArray ja = new JSONArray(); + Iterator keys = keys(); + while (keys.hasNext()) { + ja.put(keys.next()); + } + return ja.length() == 0 ? null : ja; + } + + /** + * Produce a string from a Number. + * @param n A Number + * @return A String. + * @throws JSONException If n is a non-finite number. + */ + static public String numberToString(Number n) + throws JSONException { + if (n == null) { + throw new JSONException("Null pointer"); + } + testValidity(n); + +// Shave off trailing zeros and decimal point, if possible. + + String s = n.toString(); + if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) { + while (s.endsWith("0")) { + s = s.substring(0, s.length() - 1); + } + if (s.endsWith(".")) { + s = s.substring(0, s.length() - 1); + } + } + return s; + } + + + /** + * Get an optional value associated with a key. + * @param key A key string. + * @return An object which is the value, or null if there is no value. + */ + public Object opt(String key) { + return key == null ? null : this.map.get(key); + } + + + /** + * Get an optional boolean associated with a key. + * It returns false if there is no such key, or if the value is not + * Boolean.TRUE or the String "true". + * + * @param key A key string. + * @return The truth. + */ + public boolean optBoolean(String key) { + return optBoolean(key, false); + } + + + /** + * Get an optional boolean associated with a key. + * It returns the defaultValue if there is no such key, or if it is not + * a Boolean or the String "true" or "false" (case insensitive). + * + * @param key A key string. + * @param defaultValue The default. + * @return The truth. + */ + public boolean optBoolean(String key, boolean defaultValue) { + try { + return getBoolean(key); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get an optional double associated with a key, + * or NaN if there is no such key or if its value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A string which is the key. + * @return An object which is the value. + */ + public double optDouble(String key) { + return optDouble(key, Double.NaN); + } + + + /** + * Get an optional double associated with a key, or the + * defaultValue if there is no such key or if its value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public double optDouble(String key, double defaultValue) { + try { + Object o = opt(key); + return o instanceof Number ? ((Number)o).doubleValue() : + new Double((String)o).doubleValue(); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get an optional int value associated with a key, + * or zero if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @return An object which is the value. + */ + public int optInt(String key) { + return optInt(key, 0); + } + + + /** + * Get an optional int value associated with a key, + * or the default if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public int optInt(String key, int defaultValue) { + try { + return getInt(key); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get an optional JSONArray associated with a key. + * It returns null if there is no such key, or if its value is not a + * JSONArray. + * + * @param key A key string. + * @return A JSONArray which is the value. + */ + public JSONArray optJSONArray(String key) { + Object o = opt(key); + return o instanceof JSONArray ? (JSONArray)o : null; + } + + + /** + * Get an optional JSONObject associated with a key. + * It returns null if there is no such key, or if its value is not a + * JSONObject. + * + * @param key A key string. + * @return A JSONObject which is the value. + */ + public JSONObject optJSONObject(String key) { + Object o = opt(key); + return o instanceof JSONObject ? (JSONObject)o : null; + } + + + /** + * Get an optional long value associated with a key, + * or zero if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @return An object which is the value. + */ + public long optLong(String key) { + return optLong(key, 0); + } + + + /** + * Get an optional long value associated with a key, + * or the default if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public long optLong(String key, long defaultValue) { + try { + return getLong(key); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get an optional string associated with a key. + * It returns an empty string if there is no such key. If the value is not + * a string and is not null, then it is coverted to a string. + * + * @param key A key string. + * @return A string which is the value. + */ + public String optString(String key) { + return optString(key, ""); + } + + + /** + * Get an optional string associated with a key. + * It returns the defaultValue if there is no such key. + * + * @param key A key string. + * @param defaultValue The default. + * @return A string which is the value. + */ + public String optString(String key, String defaultValue) { + Object o = opt(key); + return o != null ? o.toString() : defaultValue; + } + + + private void populateMap(Object bean) { + Class klass = bean.getClass(); + +// If klass is a System class then set includeSuperClass to false. + + boolean includeSuperClass = klass.getClassLoader() != null; + + Method[] methods = (includeSuperClass) ? + klass.getMethods() : klass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i += 1) { + try { + Method method = methods[i]; + if (Modifier.isPublic(method.getModifiers())) { + String name = method.getName(); + String key = ""; + if (name.startsWith("get")) { + if (name.equals("getClass") || + name.equals("getDeclaringClass")) { + key = ""; + } else { + key = name.substring(3); + } + } else if (name.startsWith("is")) { + key = name.substring(2); + } + if (key.length() > 0 && + Character.isUpperCase(key.charAt(0)) && + method.getParameterTypes().length == 0) { + if (key.length() == 1) { + key = key.toLowerCase(); + } else if (!Character.isUpperCase(key.charAt(1))) { + key = key.substring(0, 1).toLowerCase() + + key.substring(1); + } + + Object result = method.invoke(bean, (Object[])null); + + map.put(key, wrap(result)); + } + } + } catch (Exception ignore) { + } + } + } + + + /** + * Put a key/boolean pair in the JSONObject. + * + * @param key A key string. + * @param value A boolean which is the value. + * @return this. + * @throws JSONException If the key is null. + */ + public JSONObject put(String key, boolean value) throws JSONException { + put(key, value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + + /** + * Put a key/value pair in the JSONObject, where the value will be a + * JSONArray which is produced from a Collection. + * @param key A key string. + * @param value A Collection value. + * @return this. + * @throws JSONException + */ + public JSONObject put(String key, Collection value) throws JSONException { + put(key, new JSONArray(value)); + return this; + } + + + /** + * Put a key/double pair in the JSONObject. + * + * @param key A key string. + * @param value A double which is the value. + * @return this. + * @throws JSONException If the key is null or if the number is invalid. + */ + public JSONObject put(String key, double value) throws JSONException { + put(key, new Double(value)); + return this; + } + + + /** + * Put a key/int pair in the JSONObject. + * + * @param key A key string. + * @param value An int which is the value. + * @return this. + * @throws JSONException If the key is null. + */ + public JSONObject put(String key, int value) throws JSONException { + put(key, new Integer(value)); + return this; + } + + + /** + * Put a key/long pair in the JSONObject. + * + * @param key A key string. + * @param value A long which is the value. + * @return this. + * @throws JSONException If the key is null. + */ + public JSONObject put(String key, long value) throws JSONException { + put(key, new Long(value)); + return this; + } + + + /** + * Put a key/value pair in the JSONObject, where the value will be a + * JSONObject which is produced from a Map. + * @param key A key string. + * @param value A Map value. + * @return this. + * @throws JSONException + */ + public JSONObject put(String key, Map value) throws JSONException { + put(key, new JSONObject(value)); + return this; + } + + + /** + * Put a key/value pair in the JSONObject. If the value is null, + * then the key will be removed from the JSONObject if it is present. + * @param key A key string. + * @param value An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, + * or the JSONObject.NULL object. + * @return this. + * @throws JSONException If the value is non-finite number + * or if the key is null. + */ + public JSONObject put(String key, Object value) throws JSONException { + if (key == null) { + throw new JSONException("Null key."); + } + if (value != null) { + testValidity(value); + this.map.put(key, value); + } else { + remove(key); + } + return this; + } + + + /** + * Put a key/value pair in the JSONObject, but only if the key and the + * value are both non-null, and only if there is not already a member + * with that name. + * @param key + * @param value + * @return his. + * @throws JSONException if the key is a duplicate + */ + public JSONObject putOnce(String key, Object value) throws JSONException { + if (key != null && value != null) { + if (opt(key) != null) { + throw new JSONException("Duplicate key \"" + key + "\""); + } + put(key, value); + } + return this; + } + + + /** + * Put a key/value pair in the JSONObject, but only if the + * key and the value are both non-null. + * @param key A key string. + * @param value An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, + * or the JSONObject.NULL object. + * @return this. + * @throws JSONException If the value is a non-finite number. + */ + public JSONObject putOpt(String key, Object value) throws JSONException { + if (key != null && value != null) { + put(key, value); + } + return this; + } + + + /** + * Produce a string in double quotes with backslash sequences in all the + * right places. A backslash will be inserted within = '\u0080' && c < '\u00a0') || + (c >= '\u2000' && c < '\u2100')) { + t = "000" + Integer.toHexString(c); + sb.append("\\u" + t.substring(t.length() - 4)); + } else { + sb.append(c); + } + } + } + sb.append('"'); + return sb.toString(); + } + + /** + * Remove a name and its value, if present. + * @param key The name to be removed. + * @return The value that was associated with the name, + * or null if there was no value. + */ + public Object remove(String key) { + return this.map.remove(key); + } + + /** + * Get an enumeration of the keys of the JSONObject. + * The keys will be sorted alphabetically. + * + * @return An iterator of the keys. + */ + public Iterator sortedKeys() { + return new TreeSet(this.map.keySet()).iterator(); + } + + /** + * Try to convert a string into a number, boolean, or null. If the string + * can't be converted, return the string. + * @param s A String. + * @return A simple JSON value. + */ + static public Object stringToValue(String s) { + if (s.equals("")) { + return s; + } + if (s.equalsIgnoreCase("true")) { + return Boolean.TRUE; + } + if (s.equalsIgnoreCase("false")) { + return Boolean.FALSE; + } + if (s.equalsIgnoreCase("null")) { + return JSONObject.NULL; + } + + /* + * If it might be a number, try converting it. + * We support the non-standard 0x- convention. + * If a number cannot be produced, then the value will just + * be a string. Note that the 0x-, plus, and implied string + * conventions are non-standard. A JSON parser may accept + * non-JSON forms as long as it accepts all correct JSON forms. + */ + + char b = s.charAt(0); + if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') { + if (b == '0' && s.length() > 2 && + (s.charAt(1) == 'x' || s.charAt(1) == 'X')) { + try { + return new Integer(Integer.parseInt(s.substring(2), 16)); + } catch (Exception ignore) { + } + } + try { + if (s.indexOf('.') > -1 || + s.indexOf('e') > -1 || s.indexOf('E') > -1) { + return Double.valueOf(s); + } else { + Long myLong = new Long(s); + if (myLong.longValue() == myLong.intValue()) { + return new Integer(myLong.intValue()); + } else { + return myLong; + } + } + } catch (Exception ignore) { + } + } + return s; + } + + + /** + * Throw an exception if the object is an NaN or infinite number. + * @param o The object to test. + * @throws JSONException If o is a non-finite number. + */ + static void testValidity(Object o) throws JSONException { + if (o != null) { + if (o instanceof Double) { + if (((Double)o).isInfinite() || ((Double)o).isNaN()) { + throw new JSONException( + "JSON does not allow non-finite numbers."); + } + } else if (o instanceof Float) { + if (((Float)o).isInfinite() || ((Float)o).isNaN()) { + throw new JSONException( + "JSON does not allow non-finite numbers."); + } + } + } + } + + + /** + * Produce a JSONArray containing the values of the members of this + * JSONObject. + * @param names A JSONArray containing a list of key strings. This + * determines the sequence of the values in the result. + * @return A JSONArray of values. + * @throws JSONException If any of the values are non-finite numbers. + */ + public JSONArray toJSONArray(JSONArray names) throws JSONException { + if (names == null || names.length() == 0) { + return null; + } + JSONArray ja = new JSONArray(); + for (int i = 0; i < names.length(); i += 1) { + ja.put(this.opt(names.getString(i))); + } + return ja; + } + + /** + * Make a JSON text of this JSONObject. For compactness, no whitespace + * is added. If this would not result in a syntactically correct JSON text, + * then null will be returned instead. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, portable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + */ + public String toString() { + try { + Iterator keys = keys(); + StringBuffer sb = new StringBuffer("{"); + + while (keys.hasNext()) { + if (sb.length() > 1) { + sb.append(','); + } + Object o = keys.next(); + sb.append(quote(o.toString())); + sb.append(':'); + sb.append(valueToString(this.map.get(o))); + } + sb.append('}'); + return sb.toString(); + } catch (Exception e) { + return null; + } + } + + + /** + * Make a prettyprinted JSON text of this JSONObject. + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @return a printable, displayable, portable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + * @throws JSONException If the object contains an invalid number. + */ + public String toString(int indentFactor) throws JSONException { + return toString(indentFactor, 0); + } + + + /** + * Make a prettyprinted JSON text of this JSONObject. + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @param indent The indentation of the top level. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + * @throws JSONException If the object contains an invalid number. + */ + String toString(int indentFactor, int indent) throws JSONException { + int j; + int n = length(); + if (n == 0) { + return "{}"; + } + Iterator keys = sortedKeys(); + StringBuffer sb = new StringBuffer("{"); + int newindent = indent + indentFactor; + Object o; + if (n == 1) { + o = keys.next(); + sb.append(quote(o.toString())); + sb.append(": "); + sb.append(valueToString(this.map.get(o), indentFactor, + indent)); + } else { + while (keys.hasNext()) { + o = keys.next(); + if (sb.length() > 1) { + sb.append(",\n"); + } else { + sb.append('\n'); + } + for (j = 0; j < newindent; j += 1) { + sb.append(' '); + } + sb.append(quote(o.toString())); + sb.append(": "); + sb.append(valueToString(this.map.get(o), indentFactor, + newindent)); + } + if (sb.length() > 1) { + sb.append('\n'); + for (j = 0; j < indent; j += 1) { + sb.append(' '); + } + } + } + sb.append('}'); + return sb.toString(); + } + + + /** + * Make a JSON text of an Object value. If the object has an + * value.toJSONString() method, then that method will be used to produce + * the JSON text. The method is required to produce a strictly + * conforming text. If the object does not contain a toJSONString + * method (which is the most common case), then a text will be + * produced by other means. If the value is an array or Collection, + * then a JSONArray will be made from it and its toJSONString method + * will be called. If the value is a MAP, then a JSONObject will be made + * from it and its toJSONString method will be called. Otherwise, the + * value's toString method will be called, and the result will be quoted. + * + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param value The value to be serialized. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + * @throws JSONException If the value is or contains an invalid number. + */ + static String valueToString(Object value) throws JSONException { + if (value == null || value.equals(null)) { + return "null"; + } + if (value instanceof JSONString) { + Object o; + try { + o = ((JSONString)value).toJSONString(); + } catch (Exception e) { + throw new JSONException(e); + } + if (o instanceof String) { + return (String)o; + } + throw new JSONException("Bad value from toJSONString: " + o); + } + if (value instanceof Number) { + return numberToString((Number) value); + } + if (value instanceof Boolean || value instanceof JSONObject || + value instanceof JSONArray) { + return value.toString(); + } + if (value instanceof Map) { + return new JSONObject((Map)value).toString(); + } + if (value instanceof Collection) { + return new JSONArray((Collection)value).toString(); + } + if (value.getClass().isArray()) { + return new JSONArray(value).toString(); + } + return quote(value.toString()); + } + + + /** + * Make a prettyprinted JSON text of an object value. + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param value The value to be serialized. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @param indent The indentation of the top level. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + * @throws JSONException If the object contains an invalid number. + */ + static String valueToString(Object value, int indentFactor, int indent) + throws JSONException { + if (value == null || value.equals(null)) { + return "null"; + } + try { + if (value instanceof JSONString) { + Object o = ((JSONString)value).toJSONString(); + if (o instanceof String) { + return (String)o; + } + } + } catch (Exception ignore) { + } + if (value instanceof Number) { + return numberToString((Number) value); + } + if (value instanceof Boolean) { + return value.toString(); + } + if (value instanceof JSONObject) { + return ((JSONObject)value).toString(indentFactor, indent); + } + if (value instanceof JSONArray) { + return ((JSONArray)value).toString(indentFactor, indent); + } + if (value instanceof Map) { + return new JSONObject((Map)value).toString(indentFactor, indent); + } + if (value instanceof Collection) { + return new JSONArray((Collection)value).toString(indentFactor, indent); + } + if (value.getClass().isArray()) { + return new JSONArray(value).toString(indentFactor, indent); + } + return quote(value.toString()); + } + + + /** + * Wrap an object, if necessary. If the object is null, return the NULL + * object. If it is an array or collection, wrap it in a JSONArray. If + * it is a map, wrap it in a JSONObject. If it is a standard property + * (Double, String, et al) then it is already wrapped. Otherwise, if it + * comes from one of the java packages, turn it into a string. And if + * it doesn't, try to wrap it in a JSONObject. If the wrapping fails, + * then null is returned. + * + * @param object The object to wrap + * @return The wrapped value + */ + static Object wrap(Object object) { + try { + if (object == null) { + return NULL; + } + if (object instanceof JSONObject || object instanceof JSONArray || + NULL.equals(object) || object instanceof JSONString || + object instanceof Byte || object instanceof Character || + object instanceof Short || object instanceof Integer || + object instanceof Long || object instanceof Boolean || + object instanceof Float || object instanceof Double || + object instanceof String) { + return object; + } + + if (object instanceof Collection) { + return new JSONArray((Collection)object); + } + if (object.getClass().isArray()) { + return new JSONArray(object); + } + if (object instanceof Map) { + return new JSONObject((Map)object); + } + Package objectPackage = object.getClass().getPackage(); + String objectPackageName = ( objectPackage != null ? objectPackage.getName() : "" ); + if (objectPackageName.startsWith("java.") || + objectPackageName.startsWith("javax.") || + object.getClass().getClassLoader() == null) { + return object.toString(); + } + return new JSONObject(object); + } catch(Exception exception) { + return null; + } + } + + + /** + * Write the contents of the JSONObject as JSON text to a writer. + * For compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return The writer. + * @throws JSONException + */ + public Writer write(Writer writer) throws JSONException { + try { + boolean b = false; + Iterator keys = keys(); + writer.write('{'); + + while (keys.hasNext()) { + if (b) { + writer.write(','); + } + Object k = keys.next(); + writer.write(quote(k.toString())); + writer.write(':'); + Object v = this.map.get(k); + if (v instanceof JSONObject) { + ((JSONObject)v).write(writer); + } else if (v instanceof JSONArray) { + ((JSONArray)v).write(writer); + } else { + writer.write(valueToString(v)); + } + b = true; + } + writer.write('}'); + return writer; + } catch (IOException exception) { + throw new JSONException(exception); + } + } +} \ No newline at end of file diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONString.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONString.java new file mode 100644 index 0000000000..169eb0f455 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONString.java @@ -0,0 +1,18 @@ +package org.gephi.streaming.impl.json.parser; +/** + * The JSONString interface allows a toJSONString() + * method so that a class can change the behavior of + * JSONObject.toString(), JSONArray.toString(), + * and JSONWriter.value(Object). The + * toJSONString method will be used instead of the default behavior + * of using the Object's toString() method and quoting the result. + */ +public interface JSONString { + /** + * The toJSONString method allows a class to produce its own JSON + * serialization. + * + * @return A strictly syntactically correct JSON text. + */ + public String toJSONString(); +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONStringer.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONStringer.java new file mode 100644 index 0000000000..203ff32443 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONStringer.java @@ -0,0 +1,78 @@ +package org.gephi.streaming.impl.json.parser; + +/* +Copyright (c) 2006 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +import java.io.StringWriter; + +/** + * JSONStringer provides a quick and convenient way of producing JSON text. + * The texts produced strictly conform to JSON syntax rules. No whitespace is + * added, so the results are ready for transmission or storage. Each instance of + * JSONStringer can produce one JSON text. + *

+ * A JSONStringer instance provides a value method for appending + * values to the + * text, and a key + * method for adding keys before values in objects. There are array + * and endArray methods that make and bound array values, and + * object and endObject methods which make and bound + * object values. All of these methods return the JSONWriter instance, + * permitting cascade style. For example,

+ * myString = new JSONStringer()
+ *     .object()
+ *         .key("JSON")
+ *         .value("Hello, World!")
+ *     .endObject()
+ *     .toString();
which produces the string
+ * {"JSON":"Hello, World!"}
+ *

+ * The first method called must be array or object. + * There are no methods for adding commas or colons. JSONStringer adds them for + * you. Objects and arrays can be nested up to 20 levels deep. + *

+ * This can sometimes be easier than using a JSONObject to build a string. + * @author JSON.org + * @version 2008-09-18 + */ +public class JSONStringer extends JSONWriter { + /** + * Make a fresh JSONStringer. It can be used to build one JSON text. + */ + public JSONStringer() { + super(new StringWriter()); + } + + /** + * Return the JSON text. This method is used to obtain the product of the + * JSONStringer instance. It will return null if there was a + * problem in the construction of the JSON text (such as the calls to + * array were not properly balanced with calls to + * endArray). + * @return The JSON text. + */ + public String toString() { + return this.mode == 'd' ? this.writer.toString() : null; + } +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONTokener.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONTokener.java new file mode 100644 index 0000000000..7ac23547b4 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONTokener.java @@ -0,0 +1,435 @@ +package org.gephi.streaming.impl.json.parser; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * A JSONTokener takes a source string and extracts characters and tokens from + * it. It is used by the JSONObject and JSONArray constructors to parse + * JSON source strings. + * @author JSON.org + * @version 2010-02-02 + */ +public class JSONTokener { + + private int character; + private boolean eof; + private int index; + private int line; + private char previous; + private Reader reader; + private boolean usePrevious; + + + /** + * Construct a JSONTokener from a reader. + * + * @param reader A reader. + */ + public JSONTokener(Reader reader) { + this.reader = reader.markSupported() ? + reader : new BufferedReader(reader); + this.eof = false; + this.usePrevious = false; + this.previous = 0; + this.index = 0; + this.character = 1; + this.line = 1; + } + + + /** + * Construct a JSONTokener from a string. + * + * @param s A source string. + */ + public JSONTokener(String s) { + this(new StringReader(s)); + } + + + /** + * Back up one character. This provides a sort of lookahead capability, + * so that you can test for a digit or letter before attempting to parse + * the next number or identifier. + */ + public void back() throws JSONException { + if (usePrevious || index <= 0) { + throw new JSONException("Stepping back two steps is not supported"); + } + this.index -= 1; + this.character -= 1; + this.usePrevious = true; + this.eof = false; + } + + + /** + * Get the hex value of a character (base16). + * @param c A character between '0' and '9' or between 'A' and 'F' or + * between 'a' and 'f'. + * @return An int between 0 and 15, or -1 if c was not a hex digit. + */ + public static int dehexchar(char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'A' && c <= 'F') { + return c - ('A' - 10); + } + if (c >= 'a' && c <= 'f') { + return c - ('a' - 10); + } + return -1; + } + + public boolean end() { + return eof && !usePrevious; + } + + + /** + * Determine if the source string still contains characters that next() + * can consume. + * @return true if not yet at the end of the source. + */ + public boolean more() throws JSONException { + next(); + if (end()) { + return false; + } + back(); + return true; + } + + + /** + * Get the next character in the source string. + * + * @return The next character, or 0 if past the end of the source string. + */ + public char next() throws JSONException { + int c; + if (this.usePrevious) { + this.usePrevious = false; + c = this.previous; + } else { + try { + c = this.reader.read(); + } catch (IOException exception) { + throw new JSONException(exception); + } + + if (c <= 0) { // End of stream + this.eof = true; + c = 0; + } + } + this.index += 1; + if (this.previous == '\r') { + this.line += 1; + this.character = c == '\n' ? 0 : 1; + } else if (c == '\n') { + this.line += 1; + this.character = 0; + } else { + this.character += 1; + } + this.previous = (char) c; + return this.previous; + } + + + /** + * Consume the next character, and check that it matches a specified + * character. + * @param c The character to match. + * @return The character. + * @throws JSONException if the character does not match. + */ + public char next(char c) throws JSONException { + char n = next(); + if (n != c) { + throw syntaxError("Expected '" + c + "' and instead saw '" + + n + "'"); + } + return n; + } + + + /** + * Get the next n characters. + * + * @param n The number of characters to take. + * @return A string of n characters. + * @throws JSONException + * Substring bounds error if there are not + * n characters remaining in the source string. + */ + public String next(int n) throws JSONException { + if (n == 0) { + return ""; + } + + char[] buffer = new char[n]; + int pos = 0; + + while (pos < n) { + buffer[pos] = next(); + if (end()) { + throw syntaxError("Substring bounds error"); + } + pos += 1; + } + return new String(buffer); + } + + + /** + * Get the next char in the string, skipping whitespace. + * @throws JSONException + * @return A character, or 0 if there are no more characters. + */ + public char nextClean() throws JSONException { + for (;;) { + char c = next(); + if (c == 0 || c > ' ') { + return c; + } + } + } + + + /** + * Return the characters up to the next close quote character. + * Backslash processing is done. The formal JSON format does not + * allow strings in single quotes, but an implementation is allowed to + * accept them. + * @param quote The quoting character, either + * " (double quote) or + * ' (single quote). + * @return A String. + * @throws JSONException Unterminated string. + */ + public String nextString(char quote) throws JSONException { + char c; + StringBuffer sb = new StringBuffer(); + for (;;) { + c = next(); + switch (c) { + case 0: + case '\n': + case '\r': + throw syntaxError("Unterminated string"); + case '\\': + c = next(); + switch (c) { + case 'b': + sb.append('\b'); + break; + case 't': + sb.append('\t'); + break; + case 'n': + sb.append('\n'); + break; + case 'f': + sb.append('\f'); + break; + case 'r': + sb.append('\r'); + break; + case 'u': + sb.append((char)Integer.parseInt(next(4), 16)); + break; + case '"': + case '\'': + case '\\': + case '/': + sb.append(c); + break; + default: + throw syntaxError("Illegal escape."); + } + break; + default: + if (c == quote) { + return sb.toString(); + } + sb.append(c); + } + } + } + + + /** + * Get the text up but not including the specified character or the + * end of line, whichever comes first. + * @param d A delimiter character. + * @return A string. + */ + public String nextTo(char d) throws JSONException { + StringBuffer sb = new StringBuffer(); + for (;;) { + char c = next(); + if (c == d || c == 0 || c == '\n' || c == '\r') { + if (c != 0) { + back(); + } + return sb.toString().trim(); + } + sb.append(c); + } + } + + + /** + * Get the text up but not including one of the specified delimiter + * characters or the end of line, whichever comes first. + * @param delimiters A set of delimiter characters. + * @return A string, trimmed. + */ + public String nextTo(String delimiters) throws JSONException { + char c; + StringBuffer sb = new StringBuffer(); + for (;;) { + c = next(); + if (delimiters.indexOf(c) >= 0 || c == 0 || + c == '\n' || c == '\r') { + if (c != 0) { + back(); + } + return sb.toString().trim(); + } + sb.append(c); + } + } + + + /** + * Get the next value. The value can be a Boolean, Double, Integer, + * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. + * @throws JSONException If syntax error. + * + * @return An object. + */ + public Object nextValue() throws JSONException { + char c = nextClean(); + String s; + + switch (c) { + case '"': + case '\'': + return nextString(c); + case '{': + back(); + return new JSONObject(this); + case '[': + case '(': + back(); + return new JSONArray(this); + } + + /* + * Handle unquoted text. This could be the values true, false, or + * null, or it can be a number. An implementation (such as this one) + * is allowed to also accept non-standard forms. + * + * Accumulate characters until we reach the end of the text or a + * formatting character. + */ + + StringBuffer sb = new StringBuffer(); + while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) { + sb.append(c); + c = next(); + } + back(); + + s = sb.toString().trim(); + if (s.equals("")) { + throw syntaxError("Missing value"); + } + return JSONObject.stringToValue(s); + } + + + /** + * Skip characters until the next character is the requested character. + * If the requested character is not found, no characters are skipped. + * @param to A character to skip to. + * @return The requested character, or zero if the requested character + * is not found. + */ + public char skipTo(char to) throws JSONException { + char c; + try { + int startIndex = this.index; + int startCharacter = this.character; + int startLine = this.line; + reader.mark(Integer.MAX_VALUE); + do { + c = next(); + if (c == 0) { + reader.reset(); + this.index = startIndex; + this.character = startCharacter; + this.line = startLine; + return c; + } + } while (c != to); + } catch (IOException exc) { + throw new JSONException(exc); + } + + back(); + return c; + } + + + /** + * Make a JSONException to signal a syntax error. + * + * @param message The error message. + * @return A JSONException object, suitable for throwing + */ + public JSONException syntaxError(String message) { + return new JSONException(message + toString()); + } + + + /** + * Make a printable string of this JSONTokener. + * + * @return " at {index} [character {character} line {line}]" + */ + public String toString() { + return " at " + index + " [character " + this.character + " line " + this.line + "]"; + } +} \ No newline at end of file diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONWriter.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONWriter.java new file mode 100644 index 0000000000..39812ef848 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/JSONWriter.java @@ -0,0 +1,323 @@ +package org.gephi.streaming.impl.json.parser; + +import java.io.IOException; +import java.io.Writer; + +/* +Copyright (c) 2006 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * JSONWriter provides a quick and convenient way of producing JSON text. + * The texts produced strictly conform to JSON syntax rules. No whitespace is + * added, so the results are ready for transmission or storage. Each instance of + * JSONWriter can produce one JSON text. + *

+ * A JSONWriter instance provides a value method for appending + * values to the + * text, and a key + * method for adding keys before values in objects. There are array + * and endArray methods that make and bound array values, and + * object and endObject methods which make and bound + * object values. All of these methods return the JSONWriter instance, + * permitting a cascade style. For example,

+ * new JSONWriter(myWriter)
+ *     .object()
+ *         .key("JSON")
+ *         .value("Hello, World!")
+ *     .endObject();
which writes
+ * {"JSON":"Hello, World!"}
+ *

+ * The first method called must be array or object. + * There are no methods for adding commas or colons. JSONWriter adds them for + * you. Objects and arrays can be nested up to 20 levels deep. + *

+ * This can sometimes be easier than using a JSONObject to build a string. + * @author JSON.org + * @version 2010-03-11 + */ +public class JSONWriter { + private static final int maxdepth = 20; + + /** + * The comma flag determines if a comma should be output before the next + * value. + */ + private boolean comma; + + /** + * The current mode. Values: + * 'a' (array), + * 'd' (done), + * 'i' (initial), + * 'k' (key), + * 'o' (object). + */ + protected char mode; + + /** + * The object/array stack. + */ + private JSONObject stack[]; + + /** + * The stack top index. A value of 0 indicates that the stack is empty. + */ + private int top; + + /** + * The writer that will receive the output. + */ + protected Writer writer; + + /** + * Make a fresh JSONWriter. It can be used to build one JSON text. + */ + public JSONWriter(Writer w) { + this.comma = false; + this.mode = 'i'; + this.stack = new JSONObject[maxdepth]; + this.top = 0; + this.writer = w; + } + + /** + * Append a value. + * @param s A string value. + * @return this + * @throws JSONException If the value is out of sequence. + */ + private JSONWriter append(String s) throws JSONException { + if (s == null) { + throw new JSONException("Null pointer"); + } + if (this.mode == 'o' || this.mode == 'a') { + try { + if (this.comma && this.mode == 'a') { + this.writer.write(','); + } + this.writer.write(s); + } catch (IOException e) { + throw new JSONException(e); + } + if (this.mode == 'o') { + this.mode = 'k'; + } + this.comma = true; + return this; + } + throw new JSONException("Value out of sequence."); + } + + /** + * Begin appending a new array. All values until the balancing + * endArray will be appended to this array. The + * endArray method must be called to mark the array's end. + * @return this + * @throws JSONException If the nesting is too deep, or if the object is + * started in the wrong place (for example as a key or after the end of the + * outermost array or object). + */ + public JSONWriter array() throws JSONException { + if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') { + this.push(null); + this.append("["); + this.comma = false; + return this; + } + throw new JSONException("Misplaced array."); + } + + /** + * End something. + * @param m Mode + * @param c Closing character + * @return this + * @throws JSONException If unbalanced. + */ + private JSONWriter end(char m, char c) throws JSONException { + if (this.mode != m) { + throw new JSONException(m == 'a' ? "Misplaced endArray." : + "Misplaced endObject."); + } + this.pop(m); + try { + this.writer.write(c); + } catch (IOException e) { + throw new JSONException(e); + } + this.comma = true; + return this; + } + + /** + * End an array. This method most be called to balance calls to + * array. + * @return this + * @throws JSONException If incorrectly nested. + */ + public JSONWriter endArray() throws JSONException { + return this.end('a', ']'); + } + + /** + * End an object. This method most be called to balance calls to + * object. + * @return this + * @throws JSONException If incorrectly nested. + */ + public JSONWriter endObject() throws JSONException { + return this.end('k', '}'); + } + + /** + * Append a key. The key will be associated with the next value. In an + * object, every value must be preceded by a key. + * @param s A key string. + * @return this + * @throws JSONException If the key is out of place. For example, keys + * do not belong in arrays or if the key is null. + */ + public JSONWriter key(String s) throws JSONException { + if (s == null) { + throw new JSONException("Null key."); + } + if (this.mode == 'k') { + try { + stack[top - 1].putOnce(s, Boolean.TRUE); + if (this.comma) { + this.writer.write(','); + } + this.writer.write(JSONObject.quote(s)); + this.writer.write(':'); + this.comma = false; + this.mode = 'o'; + return this; + } catch (IOException e) { + throw new JSONException(e); + } + } + throw new JSONException("Misplaced key."); + } + + + /** + * Begin appending a new object. All keys and values until the balancing + * endObject will be appended to this object. The + * endObject method must be called to mark the object's end. + * @return this + * @throws JSONException If the nesting is too deep, or if the object is + * started in the wrong place (for example as a key or after the end of the + * outermost array or object). + */ + public JSONWriter object() throws JSONException { + if (this.mode == 'i') { + this.mode = 'o'; + } + if (this.mode == 'o' || this.mode == 'a') { + this.append("{"); + this.push(new JSONObject()); + this.comma = false; + return this; + } + throw new JSONException("Misplaced object."); + + } + + + /** + * Pop an array or object scope. + * @param c The scope to close. + * @throws JSONException If nesting is wrong. + */ + private void pop(char c) throws JSONException { + if (this.top <= 0) { + throw new JSONException("Nesting error."); + } + char m = this.stack[this.top - 1] == null ? 'a' : 'k'; + if (m != c) { + throw new JSONException("Nesting error."); + } + this.top -= 1; + this.mode = this.top == 0 ? 'd' : this.stack[this.top - 1] == null ? 'a' : 'k'; + } + + /** + * Push an array or object scope. + * @param c The scope to open. + * @throws JSONException If nesting is too deep. + */ + private void push(JSONObject jo) throws JSONException { + if (this.top >= maxdepth) { + throw new JSONException("Nesting too deep."); + } + this.stack[this.top] = jo; + this.mode = jo == null ? 'a' : 'k'; + this.top += 1; + } + + + /** + * Append either the value true or the value + * false. + * @param b A boolean. + * @return this + * @throws JSONException + */ + public JSONWriter value(boolean b) throws JSONException { + return this.append(b ? "true" : "false"); + } + + /** + * Append a double value. + * @param d A double. + * @return this + * @throws JSONException If the number is not finite. + */ + public JSONWriter value(double d) throws JSONException { + return this.value(new Double(d)); + } + + /** + * Append a long value. + * @param l A long. + * @return this + * @throws JSONException + */ + public JSONWriter value(long l) throws JSONException { + return this.append(Long.toString(l)); + } + + + /** + * Append an object value. + * @param o The object to append. It can be null, or a Boolean, Number, + * String, JSONObject, or JSONArray, or an object with a toJSONString() + * method. + * @return this + * @throws JSONException If the value is out of sequence. + */ + public JSONWriter value(Object o) throws JSONException { + return this.append(JSONObject.valueToString(o)); + } +} diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/XML.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/XML.java new file mode 100644 index 0000000000..f9e56526a6 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/XML.java @@ -0,0 +1,441 @@ +package org.gephi.streaming.impl.json.parser; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +import java.util.Iterator; + + +/** + * This provides static methods to convert an XML text into a JSONObject, + * and to covert a JSONObject into an XML text. + * @author JSON.org + * @version 2010-04-08 + */ +public class XML { + + /** The Character '&'. */ + public static final Character AMP = new Character('&'); + + /** The Character '''. */ + public static final Character APOS = new Character('\''); + + /** The Character '!'. */ + public static final Character BANG = new Character('!'); + + /** The Character '='. */ + public static final Character EQ = new Character('='); + + /** The Character '>'. */ + public static final Character GT = new Character('>'); + + /** The Character '<'. */ + public static final Character LT = new Character('<'); + + /** The Character '?'. */ + public static final Character QUEST = new Character('?'); + + /** The Character '"'. */ + public static final Character QUOT = new Character('"'); + + /** The Character '/'. */ + public static final Character SLASH = new Character('/'); + + /** + * Replace special characters with XML escapes: + *

+     * & (ampersand) is replaced by &amp;
+     * < (less than) is replaced by &lt;
+     * > (greater than) is replaced by &gt;
+     * " (double quote) is replaced by &quot;
+     * 
+ * @param string The string to be escaped. + * @return The escaped string. + */ + public static String escape(String string) { + StringBuffer sb = new StringBuffer(); + for (int i = 0, len = string.length(); i < len; i++) { + char c = string.charAt(i); + switch (c) { + case '&': + sb.append("&"); + break; + case '<': + sb.append("<"); + break; + case '>': + sb.append(">"); + break; + case '"': + sb.append("""); + break; + default: + sb.append(c); + } + } + return sb.toString(); + } + + /** + * Throw an exception if the string contains whitespace. + * Whitespace is not allowed in tagNames and attributes. + * @param string + * @throws JSONException + */ + public static void noSpace(String string) throws JSONException { + int i, length = string.length(); + if (length == 0) { + throw new JSONException("Empty string."); + } + for (i = 0; i < length; i += 1) { + if (Character.isWhitespace(string.charAt(i))) { + throw new JSONException("'" + string + + "' contains a space character."); + } + } + } + + /** + * Scan the content following the named tag, attaching it to the context. + * @param x The XMLTokener containing the source string. + * @param context The JSONObject that will include the new material. + * @param name The tag name. + * @return true if the close tag is processed. + * @throws JSONException + */ + private static boolean parse(XMLTokener x, JSONObject context, + String name) throws JSONException { + char c; + int i; + String n; + JSONObject o = null; + String s; + Object t; + +// Test for and skip past these forms: +// +// +// +// +// Report errors for these forms: +// <> +// <= +// << + + t = x.nextToken(); + +// "); + return false; + } + x.back(); + } else if (c == '[') { + t = x.nextToken(); + if (t.equals("CDATA")) { + if (x.next() == '[') { + s = x.nextCDATA(); + if (s.length() > 0) { + context.accumulate("content", s); + } + return false; + } + } + throw x.syntaxError("Expected 'CDATA['"); + } + i = 1; + do { + t = x.nextMeta(); + if (t == null) { + throw x.syntaxError("Missing '>' after ' 0); + return false; + } else if (t == QUEST) { + +// "); + return false; + } else if (t == SLASH) { + +// Close tag + + } else if (t == SLASH) { + if (x.nextToken() != GT) { + throw x.syntaxError("Misshaped tag"); + } + if (o.length() > 0) { + context.accumulate(n, o); + } else { + context.accumulate(n, ""); + } + return false; + +// Content, between <...> and + + } else if (t == GT) { + for (;;) { + t = x.nextContent(); + if (t == null) { + if (n != null) { + throw x.syntaxError("Unclosed tag " + n); + } + return false; + } else if (t instanceof String) { + s = (String)t; + if (s.length() > 0) { + o.accumulate("content", JSONObject.stringToValue(s)); + } + +// Nested element + + } else if (t == LT) { + if (parse(x, o, n)) { + if (o.length() == 0) { + context.accumulate(n, ""); + } else if (o.length() == 1 && + o.opt("content") != null) { + context.accumulate(n, o.opt("content")); + } else { + context.accumulate(n, o); + } + return false; + } + } + } + } else { + throw x.syntaxError("Misshaped tag"); + } + } + } + } + + + /** + * Convert a well-formed (but not necessarily valid) XML string into a + * JSONObject. Some information may be lost in this transformation + * because JSON is a data format and XML is a document format. XML uses + * elements, attributes, and content text, while JSON uses unordered + * collections of name/value pairs and arrays of values. JSON does not + * does not like to distinguish between elements and attributes. + * Sequences of similar elements are represented as JSONArrays. Content + * text may be placed in a "content" member. Comments, prologs, DTDs, and + * <[ [ ]]> are ignored. + * @param string The source string. + * @return A JSONObject containing the structured data from the XML string. + * @throws JSONException + */ + public static JSONObject toJSONObject(String string) throws JSONException { + JSONObject o = new JSONObject(); + XMLTokener x = new XMLTokener(string); + while (x.more() && x.skipPast("<")) { + parse(x, o, null); + } + return o; + } + + + /** + * Convert a JSONObject into a well-formed, element-normal XML string. + * @param o A JSONObject. + * @return A string. + * @throws JSONException + */ + public static String toString(Object o) throws JSONException { + return toString(o, null); + } + + + /** + * Convert a JSONObject into a well-formed, element-normal XML string. + * @param o A JSONObject. + * @param tagName The optional name of the enclosing tag. + * @return A string. + * @throws JSONException + */ + public static String toString(Object o, String tagName) + throws JSONException { + StringBuffer b = new StringBuffer(); + int i; + JSONArray ja; + JSONObject jo; + String k; + Iterator keys; + int len; + String s; + Object v; + if (o instanceof JSONObject) { + +// Emit + + if (tagName != null) { + b.append('<'); + b.append(tagName); + b.append('>'); + } + +// Loop thru the keys. + + jo = (JSONObject)o; + keys = jo.keys(); + while (keys.hasNext()) { + k = keys.next().toString(); + v = jo.opt(k); + if (v == null) { + v = ""; + } + if (v instanceof String) { + s = (String)v; + } else { + s = null; + } + +// Emit content in body + + if (k.equals("content")) { + if (v instanceof JSONArray) { + ja = (JSONArray)v; + len = ja.length(); + for (i = 0; i < len; i += 1) { + if (i > 0) { + b.append('\n'); + } + b.append(escape(ja.get(i).toString())); + } + } else { + b.append(escape(v.toString())); + } + +// Emit an array of similar keys + + } else if (v instanceof JSONArray) { + ja = (JSONArray)v; + len = ja.length(); + for (i = 0; i < len; i += 1) { + v = ja.get(i); + if (v instanceof JSONArray) { + b.append('<'); + b.append(k); + b.append('>'); + b.append(toString(v)); + b.append("'); + } else { + b.append(toString(v, k)); + } + } + } else if (v.equals("")) { + b.append('<'); + b.append(k); + b.append("/>"); + +// Emit a new tag + + } else { + b.append(toString(v, k)); + } + } + if (tagName != null) { + +// Emit the close tag + + b.append("'); + } + return b.toString(); + +// XML does not have good support for arrays. If an array appears in a place +// where XML is lacking, synthesize an element. + + } else if (o instanceof JSONArray) { + ja = (JSONArray)o; + len = ja.length(); + for (i = 0; i < len; ++i) { + v = ja.opt(i); + b.append(toString(v, (tagName == null) ? "array" : tagName)); + } + return b.toString(); + } else { + s = (o == null) ? "null" : escape(o.toString()); + return (tagName == null) ? "\"" + s + "\"" : + (s.length() == 0) ? "<" + tagName + "/>" : + "<" + tagName + ">" + s + ""; + } + } +} \ No newline at end of file diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/XMLTokener.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/XMLTokener.java new file mode 100644 index 0000000000..5ea71186e1 --- /dev/null +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/json/parser/XMLTokener.java @@ -0,0 +1,365 @@ +package org.gephi.streaming.impl.json.parser; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * The XMLTokener extends the JSONTokener to provide additional methods + * for the parsing of XML texts. + * @author JSON.org + * @version 2010-01-30 + */ +public class XMLTokener extends JSONTokener { + + + /** The table of entity values. It initially contains Character values for + * amp, apos, gt, lt, quot. + */ + public static final java.util.HashMap entity; + + static { + entity = new java.util.HashMap(8); + entity.put("amp", XML.AMP); + entity.put("apos", XML.APOS); + entity.put("gt", XML.GT); + entity.put("lt", XML.LT); + entity.put("quot", XML.QUOT); + } + + /** + * Construct an XMLTokener from a string. + * @param s A source string. + */ + public XMLTokener(String s) { + super(s); + } + + /** + * Get the text in the CDATA block. + * @return The string up to the ]]>. + * @throws JSONException If the ]]> is not found. + */ + public String nextCDATA() throws JSONException { + char c; + int i; + StringBuffer sb = new StringBuffer(); + for (;;) { + c = next(); + if (end()) { + throw syntaxError("Unclosed CDATA"); + } + sb.append(c); + i = sb.length() - 3; + if (i >= 0 && sb.charAt(i) == ']' && + sb.charAt(i + 1) == ']' && sb.charAt(i + 2) == '>') { + sb.setLength(i); + return sb.toString(); + } + } + } + + + /** + * Get the next XML outer token, trimming whitespace. There are two kinds + * of tokens: the '<' character which begins a markup tag, and the content + * text between markup tags. + * + * @return A string, or a '<' Character, or null if there is no more + * source text. + * @throws JSONException + */ + public Object nextContent() throws JSONException { + char c; + StringBuffer sb; + do { + c = next(); + } while (Character.isWhitespace(c)); + if (c == 0) { + return null; + } + if (c == '<') { + return XML.LT; + } + sb = new StringBuffer(); + for (;;) { + if (c == '<' || c == 0) { + back(); + return sb.toString().trim(); + } + if (c == '&') { + sb.append(nextEntity(c)); + } else { + sb.append(c); + } + c = next(); + } + } + + + /** + * Return the next entity. These entities are translated to Characters: + * & ' > < ". + * @param a An ampersand character. + * @return A Character or an entity String if the entity is not recognized. + * @throws JSONException If missing ';' in XML entity. + */ + public Object nextEntity(char a) throws JSONException { + StringBuffer sb = new StringBuffer(); + for (;;) { + char c = next(); + if (Character.isLetterOrDigit(c) || c == '#') { + sb.append(Character.toLowerCase(c)); + } else if (c == ';') { + break; + } else { + throw syntaxError("Missing ';' in XML entity: &" + sb); + } + } + String s = sb.toString(); + Object e = entity.get(s); + return e != null ? e : a + s + ";"; + } + + + /** + * Returns the next XML meta token. This is used for skipping over + * and structures. + * @return Syntax characters (< > / = ! ?) are returned as + * Character, and strings and names are returned as Boolean. We don't care + * what the values actually are. + * @throws JSONException If a string is not properly closed or if the XML + * is badly structured. + */ + public Object nextMeta() throws JSONException { + char c; + char q; + do { + c = next(); + } while (Character.isWhitespace(c)); + switch (c) { + case 0: + throw syntaxError("Misshaped meta tag"); + case '<': + return XML.LT; + case '>': + return XML.GT; + case '/': + return XML.SLASH; + case '=': + return XML.EQ; + case '!': + return XML.BANG; + case '?': + return XML.QUEST; + case '"': + case '\'': + q = c; + for (;;) { + c = next(); + if (c == 0) { + throw syntaxError("Unterminated string"); + } + if (c == q) { + return Boolean.TRUE; + } + } + default: + for (;;) { + c = next(); + if (Character.isWhitespace(c)) { + return Boolean.TRUE; + } + switch (c) { + case 0: + case '<': + case '>': + case '/': + case '=': + case '!': + case '?': + case '"': + case '\'': + back(); + return Boolean.TRUE; + } + } + } + } + + + /** + * Get the next XML Token. These tokens are found inside of angle + * brackets. It may be one of these characters: / > = ! ? or it + * may be a string wrapped in single quotes or double quotes, or it may be a + * name. + * @return a String or a Character. + * @throws JSONException If the XML is not well formed. + */ + public Object nextToken() throws JSONException { + char c; + char q; + StringBuffer sb; + do { + c = next(); + } while (Character.isWhitespace(c)); + switch (c) { + case 0: + throw syntaxError("Misshaped element"); + case '<': + throw syntaxError("Misplaced '<'"); + case '>': + return XML.GT; + case '/': + return XML.SLASH; + case '=': + return XML.EQ; + case '!': + return XML.BANG; + case '?': + return XML.QUEST; + +// Quoted string + + case '"': + case '\'': + q = c; + sb = new StringBuffer(); + for (;;) { + c = next(); + if (c == 0) { + throw syntaxError("Unterminated string"); + } + if (c == q) { + return sb.toString(); + } + if (c == '&') { + sb.append(nextEntity(c)); + } else { + sb.append(c); + } + } + default: + +// Name + + sb = new StringBuffer(); + for (;;) { + sb.append(c); + c = next(); + if (Character.isWhitespace(c)) { + return sb.toString(); + } + switch (c) { + case 0: + return sb.toString(); + case '>': + case '/': + case '=': + case '!': + case '?': + case '[': + case ']': + back(); + return sb.toString(); + case '<': + case '"': + case '\'': + throw syntaxError("Bad character in a name"); + } + } + } + } + + + /** + * Skip characters until past the requested string. + * If it is not found, we are left at the end of the source with a result of false. + * @param to A string to skip past. + * @throws JSONException + */ + public boolean skipPast(String to) throws JSONException { + boolean b; + char c; + int i; + int j; + int offset = 0; + int n = to.length(); + char[] circle = new char[n]; + + /* + * First fill the circle buffer with as many characters as are in the + * to string. If we reach an early end, bail. + */ + + for (i = 0; i < n; i += 1) { + c = next(); + if (c == 0) { + return false; + } + circle[i] = c; + } + /* + * We will loop, possibly for all of the remaining characters. + */ + for (;;) { + j = offset; + b = true; + /* + * Compare the circle buffer with the to string. + */ + for (i = 0; i < n; i += 1) { + if (circle[j] != to.charAt(i)) { + b = false; + break; + } + j += 1; + if (j >= n) { + j -= n; + } + } + /* + * If we exit the loop with b intact, then victory is ours. + */ + if (b) { + return true; + } + /* + * Get the next character. If there isn't one, then defeat is ours. + */ + c = next(); + if (c == 0) { + return false; + } + /* + * Shove the character in the circle buffer and advance the + * circle offset. The offset is mod n. + */ + circle[offset] = c; + offset += 1; + if (offset >= n) { + offset -= n; + } + } + } +} diff --git a/modules/StreamingImpl/src/main/nbm/manifest.mf b/modules/StreamingImpl/src/main/nbm/manifest.mf new file mode 100644 index 0000000000..b5ab519d74 --- /dev/null +++ b/modules/StreamingImpl/src/main/nbm/manifest.mf @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +OpenIDE-Module-Localizing-Bundle: org/gephi/streaming/impl/Bundle.properties diff --git a/modules/StreamingImpl/src/main/resources/org/gephi/streaming/impl/Bundle.properties b/modules/StreamingImpl/src/main/resources/org/gephi/streaming/impl/Bundle.properties new file mode 100644 index 0000000000..f10f6f7f89 --- /dev/null +++ b/modules/StreamingImpl/src/main/resources/org/gephi/streaming/impl/Bundle.properties @@ -0,0 +1,5 @@ +OpenIDE-Module-Display-Category=Gephi Core +OpenIDE-Module-Long-Description=\ + Streaming Implementation +OpenIDE-Module-Name=Streaming Impl +OpenIDE-Module-Short-Description=Streaming Implementation diff --git a/modules/StreamingImpl/src/main/resources/org/gephi/streaming/impl/cacerts b/modules/StreamingImpl/src/main/resources/org/gephi/streaming/impl/cacerts new file mode 100644 index 0000000000000000000000000000000000000000..b367cf1352a11eec84f4da60c018b41bafe65f67 GIT binary patch literal 1266 zcmezO_TO6u1_mY|W(3o$xs}x|XvNx5y7rA<^0WTY;R+~rLcV0$DR#pas#%YG!2Apinp)72|Or9Zz{6Gaj4u>#X zNPbafUOr5K9WKCUzyngjCCuucT9A=xC}AK5;&Ti0gZT=Kz^Ya|iK}PE37o{7>iSrs77?>GY z7#bOy7#l?axrRvG!RzoQMkVAxW@KexZerwTFlb`rVrpV!WH|8e?vz|6pO7gXT6<>Q zNNE3eIH6nZh^6}4{4I-1K5L%cv+DfSf6r&+n)tdNtywicpztMYkXW#uzE|Vt)HmB| zD)}Ea&-D;$I`U6{Q-8yD>7QZ0-=FBI&wcM&{?I;5dxg@aV+XFV@q1eH94Y9Hp0G25 zJ)@CvQ--p3$2EKTxn=T2-WTkBw@eFJYd8JH?!T{7_)7S88*F3^*lW6`ce?p1@d>vM zN%cFF2l>v>ZE6cD+}c#3CC?;rY#$&0%=?a}Y@62EaKf%*epF zxbdGs<8K35VET~dV-aH!NscR7oOtHj8A*LJLGQw7eY5|+c?=p)gXEQ28V?&Z9%#U- zW?|!;C5Tx&VvUSGK{B+=pf;bg>2#bYyC)&!{9Pu#lZNLNgK`Qk~7s<-}5Ez*5=dG~=+ zOWof6x^(|SlgYIe*H<(jdVM%EW3Ao=gR9|6MH_M!Ha?KHo!_nf;K>uN%PqG%7kpy0 z^}ltnx^DM{*gQ+VugXQ1*LIo7&K7&|kh%0@-_oZS=gY2ID*OGoV~OCyeZI5j&fIoI z@2t{>%L^YnwoW;;F7eki@!c6I>wml_Uu%$ z6l77eeZSUm>48_%|FbSPRAEaB+{|eAO{u9PeBG6$X; + + + diff --git a/modules/StreamingServer/pom.xml b/modules/StreamingServer/pom.xml new file mode 100644 index 0000000000..b67f83e428 --- /dev/null +++ b/modules/StreamingServer/pom.xml @@ -0,0 +1,69 @@ + + + 4.0.0 + + gephi-plugin-parent + org.gephi + 0.9.0 + + + org.gephi.streaming + server + 1.0.0 + nbm + + StreamingServer + + + + javax.servlet + javax.servlet-api + 3.1.0 + + + org.eclipse.jetty.websocket + websocket-server + 9.3.6.v20151106 + + + org.gephi.streaming + impl + 1.0.0 + + + + + org.netbeans.api + org-openide-util-lookup + + + org.gephi + graph-api + + + org.netbeans.api + org-openide-util + + + org.gephi + project-api + + + + + + + org.codehaus.mojo + nbm-maven-plugin + + Unknown + $sourcecode_url + + + org.gephi.streaming.server + + + + + + diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/AuthenticationFilter.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/AuthenticationFilter.java new file mode 100644 index 0000000000..be917b0c91 --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/AuthenticationFilter.java @@ -0,0 +1,113 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + + +/** + * Filter for server authentication. + * + * @author panisson + * + */ +public interface AuthenticationFilter { + + /** + * This is used to enable or disable the authentication filter. + * + * @param enabled set it to true if the filter should be enabled, + * false to disable it + */ + public void setAuthenticationEnabled(boolean enabled); + + /** + * This is used to verify if the filter is enabled + * + * @return true if the filter is enabled, false otherwise + */ + public boolean isAuthenticationEnabled(); + + /** + * This is used to get the user currently configured in this + * AuthenticationFilter + * + * @return the configured user + */ + public String getUser(); + + /** + * This is used to set the user currently configured in this + * AuthenticationFilter + * + * @param user - the user to be used by this filter + */ + public void setUser(String user); + + /** + * This is used to get the password currently configured in this + * AuthenticationFilter + * + * @return the configured password + */ + public String getPassword(); + + /** + * This is used to set the password currently configured in this + * AuthenticationFilter + * + * @param password - the password to be used by this filter + */ + public void setPassword(String password); + + /** + * This is used to verify the user and password provided by the client + * against the currently configured user and password. + * + * @param request - the request to be used to check + * @param response - the response to send errors + * @return true if the user and password are valid, false otherwise + */ + public boolean authenticate(HttpServletRequest request, HttpServletResponse response); + +} diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/ClientManager.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/ClientManager.java new file mode 100644 index 0000000000..5ca7d86532 --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/ClientManager.java @@ -0,0 +1,68 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ + +package org.gephi.streaming.server; + +import javax.servlet.http.HttpServletRequest; + +/** + * + * @author panisson + */ +public interface ClientManager { + + String add(HttpServletRequest request); + + void addClientManagerListener(ClientManagerListener listener); + + void remove(HttpServletRequest request); + + void remove(String clientId); + + void stopAll(); + + public interface ClientManagerListener { + public void clientConnected(String client); + public void clientDisconnected(String client); + } + +} diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/ServerController.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/ServerController.java new file mode 100644 index 0000000000..2b9bdcdf8d --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/ServerController.java @@ -0,0 +1,76 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.gephi.streaming.server.impl.ServerOperationExecutor; + +/** + * Controller used by the server to handle requests and responses. + * + * @author panisson + */ +public interface ServerController { + + /** + * Used to manage the client connections. + * + * @return the ClientManager to manage client connections. + */ + public ClientManager getClientManager(); + + public ServerOperationExecutor getServerOperationExecutor(); + + /** + * Used by the server to handle requests and responses. + * @param request + * @param response + */ + public void handle(HttpServletRequest request, HttpServletResponse response); + + /** + * Stop this controller. + */ + public void stop(); + +} \ No newline at end of file diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/ServerControllerFactory.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/ServerControllerFactory.java new file mode 100644 index 0000000000..8825cda2a8 --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/ServerControllerFactory.java @@ -0,0 +1,64 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server; + +import org.gephi.graph.api.Graph; + +/** + * Used to create a server controller associated with a graph instance. + * The controller will handle requests and responses and update the graph + * with received information, and will listen to graph changes in order to + * send events to connected clients. + * + * @author panisson + * + */ +public interface ServerControllerFactory { + + /** + * Create a server controller associated with the given graph. + * @param graph - the Graph to associate + * @return a new server controller instance + */ + public ServerController createServerController(Graph graph); + +} diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/StreamingServer.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/StreamingServer.java new file mode 100644 index 0000000000..717e99f40e --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/StreamingServer.java @@ -0,0 +1,103 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server; + +import java.io.IOException; + +/** + * The streaming server instance. + * + * @author panisson + * + */ +public interface StreamingServer { + + /** + * This is used to register a ServerController under a given server + * context. + *

For example, registering a ServerController under the + * context /streaming will result that the ServerController + * will be accessible under the address + * http://localhost:8080/streaming + * + * @param controller - the ServerController to be accessible under the + * context + * @param context - the context that the controller will be accessible + */ + public void register(ServerController controller, String context); + + /** + * This is used to unregister a ServerController already registered + * under the given context. + * + * @param context - the context to be unregistered + */ + public void unregister(String context); + + /** + * Start the server with the configured parameters. + * + * @throws IOException + */ + public void start() throws IOException; + + /** + * Stop the server. + * + * @throws IOException + */ + public void stop() throws IOException; + + /** + * This is used to verify if the server is currently started. + * + * @return true if the server is started + */ + public boolean isStarted(); + + /** + * This is used to get the server settings. + * @return the server settings + */ + public StreamingServerConfig getServerSettings(); + +} \ No newline at end of file diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/StreamingServerConfig.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/StreamingServerConfig.java new file mode 100644 index 0000000000..2fd5bfae3c --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/StreamingServerConfig.java @@ -0,0 +1,170 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ + +package org.gephi.streaming.server; + +import org.openide.util.NbPreferences; + +/** + * An object that represent the server configuration and preferences. + * @author panisson + */ +public class StreamingServerConfig { + + //Const Default Config + public static final String PORT = "StreamingServerConfig.port"; + public static final String USE_SSL = "StreamingServerConfig.useSSL"; + public static final String SSL_PORT = "StreamingServerConfig.sslPort"; + public static final String USER = "StreamingServerConfig.user"; + public static final String PASSWORD = "StreamingServerConfig.password"; + public static final String BASIC_AUTHENTICATION = "StreamingServerConfig.basicAuthentication"; + + //Default values + public static final int DEFAULT_PORT = 8080; + public static final boolean DEFAULT_USE_SSL = true; + public static final int DEFAULT_SSL_PORT = 8443; + public static final String DEFAULT_USER = "gephi"; + public static final String DEFAULT_PASSWORD = "gephi"; + public static final boolean DEFAULT_BASIC_AUTHENTICATION = false; + + //Preferences + protected int port = NbPreferences.forModule(StreamingServerConfig.class).getInt(PORT, DEFAULT_PORT); + protected boolean useSSL = NbPreferences.forModule(StreamingServerConfig.class).getBoolean(USE_SSL, DEFAULT_USE_SSL); + protected int sslPort = NbPreferences.forModule(StreamingServerConfig.class).getInt(SSL_PORT, DEFAULT_SSL_PORT); + protected String user = NbPreferences.forModule(StreamingServerConfig.class).get(USER, DEFAULT_USER); + protected String password = NbPreferences.forModule(StreamingServerConfig.class).get(PASSWORD, DEFAULT_PASSWORD); + protected boolean basicAuthentication = NbPreferences.forModule(StreamingServerConfig.class).getBoolean(BASIC_AUTHENTICATION, DEFAULT_BASIC_AUTHENTICATION); + + /** + * Used to get the HTTP port that the server is configured to listen. + * + * @return the configured port + */ + public int getPort() { + return port; + } + + /** + * Used to configure a new HTTP port that the server will listen + * + * @param port the port to set + */ + public void setPort(int port) { + NbPreferences.forModule(StreamingServerConfig.class).putInt(PORT, port); + this.port = port; + } + + /** + * @return the useSSL + */ + public boolean isUseSSL() { + return useSSL; + } + + /** + * @param useSSL the useSSL to set + */ + public void setUseSSL(boolean useSSL) { + NbPreferences.forModule(StreamingServerConfig.class).putBoolean(USE_SSL, useSSL); + this.useSSL = useSSL; + } + + /** + * @return the sslPort + */ + public int getSslPort() { + return sslPort; + } + + /** + * @param sslPort the sslPort to set + */ + public void setSslPort(int sslPort) { + NbPreferences.forModule(StreamingServerConfig.class).putInt(SSL_PORT, sslPort); + this.sslPort = sslPort; + } + + /** + * @return the user + */ + public String getUser() { + return user; + } + + /** + * @param user the user to set + */ + public void setUser(String user) { + NbPreferences.forModule(StreamingServerConfig.class).put(USER, user); + this.user = user; + } + + /** + * @return the password + */ + public String getPassword() { + return password; + } + + /** + * @param password the password to set + */ + public void setPassword(String password) { + NbPreferences.forModule(StreamingServerConfig.class).put(PASSWORD, password); + this.password = password; + } + + /** + * @return the basicAuthentication + */ + public boolean isBasicAuthentication() { + return basicAuthentication; + } + + /** + * @param basicAuthentication the basicAuthentication to set + */ + public void setBasicAuthentication(boolean basicAuthentication) { + NbPreferences.forModule(StreamingServerConfig.class).putBoolean(BASIC_AUTHENTICATION, basicAuthentication); + this.basicAuthentication = basicAuthentication; + } +} diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/Base64.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/Base64.java new file mode 100644 index 0000000000..97f715aa6b --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/Base64.java @@ -0,0 +1,575 @@ +package org.gephi.streaming.server.impl; + +import java.util.Arrays; + +/** A very fast and memory efficient class to encode and decode to and from BASE64 in full accordance + * with RFC 2045.

+ * On Windows XP sp1 with 1.4.2_04 and later ;), this encoder and decoder is about 10 times faster + * on small arrays (10 - 1000 bytes) and 2-3 times as fast on larger arrays (10000 - 1000000 bytes) + * compared to sun.misc.Encoder()/Decoder().

+ * + * On byte arrays the encoder is about 20% faster than Jakarta Commons Base64 Codec for encode and + * about 50% faster for decoding large arrays. This implementation is about twice as fast on very small + * arrays (< 30 bytes). If source/destination is a String this + * version is about three times as fast due to the fact that the Commons Codec result has to be recoded + * to a String from byte[], which is very expensive.

+ * + * This encode/decode algorithm doesn't create any temporary arrays as many other codecs do, it only + * allocates the resulting array. This produces less garbage and it is possible to handle arrays twice + * as large as algorithms that create a temporary array. (E.g. Jakarta Commons Codec). It is unknown + * whether Sun's sun.misc.Encoder()/Decoder() produce temporary arrays but since performance + * is quite low it probably does.

+ * + * The encoder produces the same output as the Sun one except that the Sun's encoder appends + * a trailing line separator if the last character isn't a pad. Unclear why but it only adds to the + * length and is probably a side effect. Both are in conformance with RFC 2045 though.
+ * Commons codec seem to always att a trailing line separator.

+ * + * Note! + * The encode/decode method pairs (types) come in three versions with the exact same algorithm and + * thus a lot of code redundancy. This is to not create any temporary arrays for transcoding to/from different + * format types. The methods not used can simply be commented out.

+ * + * There is also a "fast" version of all decode methods that works the same way as the normal ones, but + * har a few demands on the decoded input. Normally though, these fast verions should be used if the source if + * the input is known and it hasn't bee tampered with.

+ * + * If you find the code useful or you find a bug, please send me a note at base64 @ miginfocom . com. + * + * Licence (BSD): + * ============== + * + * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (base64 @ miginfocom . com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * Neither the name of the MiG InfoCom AB nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * @version 2.2 + * @author Mikael Grev + * Date: 2004-aug-02 + * Time: 11:31:11 + */ + +public class Base64 +{ + private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); + private static final int[] IA = new int[256]; + static { + Arrays.fill(IA, -1); + for (int i = 0, iS = CA.length; i < iS; i++) + IA[CA[i]] = i; + IA['='] = 0; + } + + // **************************************************************************************** + // * char[] version + // **************************************************************************************** + + /** Encodes a raw byte array into a BASE64 char[] representation i accordance with RFC 2045. + * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + public final static char[] encodeToChar(byte[] sArr, boolean lineSep) + { + // Check special case + int sLen = sArr != null ? sArr.length : 0; + if (sLen == 0) + return new char[0]; + + int eLen = (sLen / 3) * 3; // Length of even 24-bits. + int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count + int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array + char[] dArr = new char[dLen]; + + // Encode even 24-bits + for (int s = 0, d = 0, cc = 0; s < eLen;) { + // Copy next three bytes into lower 24 bits of int, paying attension to sign. + int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); + + // Encode the int into four chars + dArr[d++] = CA[(i >>> 18) & 0x3f]; + dArr[d++] = CA[(i >>> 12) & 0x3f]; + dArr[d++] = CA[(i >>> 6) & 0x3f]; + dArr[d++] = CA[i & 0x3f]; + + // Add optional line separator + if (lineSep && ++cc == 19 && d < dLen - 2) { + dArr[d++] = '\r'; + dArr[d++] = '\n'; + cc = 0; + } + } + + // Pad and encode last bits if source isn't even 24 bits. + int left = sLen - eLen; // 0 - 2. + if (left > 0) { + // Prepare the int + int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); + + // Set last four chars + dArr[dLen - 4] = CA[i >> 12]; + dArr[dLen - 3] = CA[(i >>> 6) & 0x3f]; + dArr[dLen - 2] = left == 2 ? CA[i & 0x3f] : '='; + dArr[dLen - 1] = '='; + } + return dArr; + } + + /** Decodes a BASE64 encoded char array. All illegal characters will be ignored and can handle both arrays with + * and without line separators. + * @param sArr The source array. null or length 0 will return an empty array. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + */ + public final static byte[] decode(char[] sArr) + { + // Check special case + int sLen = sArr != null ? sArr.length : 0; + if (sLen == 0) + return new byte[0]; + + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if (IA[sArr[i]] < 0) + sepCnt++; + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if ((sLen - sepCnt) % 4 != 0) + return null; + + int pad = 0; + for (int i = sLen; i > 1 && IA[sArr[--i]] <= 0;) + if (sArr[i] == '=') + pad++; + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for (int s = 0, d = 0; d < len;) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IA[sArr[s++]]; + if (c >= 0) + i |= c << (18 - j * 6); + else + j--; + } + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if (d < len) { + dArr[d++]= (byte) (i >> 8); + if (d < len) + dArr[d++] = (byte) i; + } + } + return dArr; + } + + /** Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as + * fast as {@link #decode(char[])}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + */ + public final static byte[] decodeFast(char[] sArr) + { + // Check special case + int sLen = sArr.length; + if (sLen == 0) + return new byte[0]; + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while (sIx < eIx && IA[sArr[sIx]] < 0) + sIx++; + + // Trim illegal chars from end + while (eIx > 0 && IA[sArr[eIx]] < 0) + eIx--; + + // get the padding count (=) (0, 1 or 2) + int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) { + // Assemble three bytes into an int from four "valid" characters. + int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if (sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if (d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for (int j = 0; sIx <= eIx - pad; j++) + i |= IA[sArr[sIx++]] << (18 - j * 6); + + for (int r = 16; d < len; r -= 8) + dArr[d++] = (byte) (i >> r); + } + + return dArr; + } + + // **************************************************************************************** + // * byte[] version + // **************************************************************************************** + + /** Encodes a raw byte array into a BASE64 byte[] representation i accordance with RFC 2045. + * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + public final static byte[] encodeToByte(byte[] sArr, boolean lineSep) + { + // Check special case + int sLen = sArr != null ? sArr.length : 0; + if (sLen == 0) + return new byte[0]; + + int eLen = (sLen / 3) * 3; // Length of even 24-bits. + int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count + int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array + byte[] dArr = new byte[dLen]; + + // Encode even 24-bits + for (int s = 0, d = 0, cc = 0; s < eLen;) { + // Copy next three bytes into lower 24 bits of int, paying attension to sign. + int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); + + // Encode the int into four chars + dArr[d++] = (byte) CA[(i >>> 18) & 0x3f]; + dArr[d++] = (byte) CA[(i >>> 12) & 0x3f]; + dArr[d++] = (byte) CA[(i >>> 6) & 0x3f]; + dArr[d++] = (byte) CA[i & 0x3f]; + + // Add optional line separator + if (lineSep && ++cc == 19 && d < dLen - 2) { + dArr[d++] = '\r'; + dArr[d++] = '\n'; + cc = 0; + } + } + + // Pad and encode last bits if source isn't an even 24 bits. + int left = sLen - eLen; // 0 - 2. + if (left > 0) { + // Prepare the int + int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); + + // Set last four chars + dArr[dLen - 4] = (byte) CA[i >> 12]; + dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f]; + dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '='; + dArr[dLen - 1] = '='; + } + return dArr; + } + + /** Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with + * and without line separators. + * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + */ + public final static byte[] decode(byte[] sArr) + { + // Check special case + int sLen = sArr.length; + + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if (IA[sArr[i] & 0xff] < 0) + sepCnt++; + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if ((sLen - sepCnt) % 4 != 0) + return null; + + int pad = 0; + for (int i = sLen; i > 1 && IA[sArr[--i] & 0xff] <= 0;) + if (sArr[i] == '=') + pad++; + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for (int s = 0, d = 0; d < len;) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IA[sArr[s++] & 0xff]; + if (c >= 0) + i |= c << (18 - j * 6); + else + j--; + } + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if (d < len) { + dArr[d++]= (byte) (i >> 8); + if (d < len) + dArr[d++] = (byte) i; + } + } + + return dArr; + } + + + /** Decodes a BASE64 encoded byte array that is known to be resonably well formatted. The method is about twice as + * fast as {@link #decode(byte[])}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + */ + public final static byte[] decodeFast(byte[] sArr) + { + // Check special case + int sLen = sArr.length; + if (sLen == 0) + return new byte[0]; + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while (sIx < eIx && IA[sArr[sIx] & 0xff] < 0) + sIx++; + + // Trim illegal chars from end + while (eIx > 0 && IA[sArr[eIx] & 0xff] < 0) + eIx--; + + // get the padding count (=) (0, 1 or 2) + int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) { + // Assemble three bytes into an int from four "valid" characters. + int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if (sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if (d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for (int j = 0; sIx <= eIx - pad; j++) + i |= IA[sArr[sIx++]] << (18 - j * 6); + + for (int r = 16; d < len; r -= 8) + dArr[d++] = (byte) (i >> r); + } + + return dArr; + } + + // **************************************************************************************** + // * String version + // **************************************************************************************** + + /** Encodes a raw byte array into a BASE64 String representation i accordance with RFC 2045. + * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + public final static String encodeToString(byte[] sArr, boolean lineSep) + { + // Reuse char[] since we can't create a String incrementally anyway and StringBuffer/Builder would be slower. + return new String(encodeToChar(sArr, lineSep)); + } + + /** Decodes a BASE64 encoded String. All illegal characters will be ignored and can handle both strings with + * and without line separators.
+ * Note! It can be up to about 2x the speed to call decode(str.toCharArray()) instead. That + * will create a temporary array though. This version will use str.charAt(i) to iterate the string. + * @param str The source string. null or length 0 will return an empty array. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + */ + public final static byte[] decode(String str) + { + // Check special case + int sLen = str != null ? str.length() : 0; + if (sLen == 0) + return new byte[0]; + + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if (IA[str.charAt(i)] < 0) + sepCnt++; + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if ((sLen - sepCnt) % 4 != 0) + return null; + + // Count '=' at end + int pad = 0; + for (int i = sLen; i > 1 && IA[str.charAt(--i)] <= 0;) + if (str.charAt(i) == '=') + pad++; + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for (int s = 0, d = 0; d < len;) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IA[str.charAt(s++)]; + if (c >= 0) + i |= c << (18 - j * 6); + else + j--; + } + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if (d < len) { + dArr[d++]= (byte) (i >> 8); + if (d < len) + dArr[d++] = (byte) i; + } + } + return dArr; + } + + /** Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as + * fast as {@link #decode(String)}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * @param s The source string. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + */ + public final static byte[] decodeFast(String s) + { + // Check special case + int sLen = s.length(); + if (sLen == 0) + return new byte[0]; + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0) + sIx++; + + // Trim illegal chars from end + while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0) + eIx--; + + // get the padding count (=) (0, 1 or 2) + int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) { + // Assemble three bytes into an int from four "valid" characters. + int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6 | IA[s.charAt(sIx++)]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if (sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if (d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for (int j = 0; sIx <= eIx - pad; j++) + i |= IA[s.charAt(sIx++)] << (18 - j * 6); + + for (int r = 16; d < len; r -= 8) + dArr[d++] = (byte) (i >> r); + } + + return dArr; + } +} \ No newline at end of file diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/BasicAuthenticationFilter.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/BasicAuthenticationFilter.java new file mode 100644 index 0000000000..2b4bdf045c --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/BasicAuthenticationFilter.java @@ -0,0 +1,134 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server.impl; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.gephi.streaming.server.AuthenticationFilter; + +/** + * This is the AuthenticationFilter implementation for the + * HTTP Basic Authentication. + * + * @author panisson + * + */ +public class BasicAuthenticationFilter implements AuthenticationFilter { + + private static final String REALM = "Gephi GraphStreaming"; + + private String user; + private String password; + private boolean enabled = false; + + public String getUser() { + return user; + } + public void setUser(String user) { + this.user = user; + } + public String getPassword() { + return password; + } + public void setPassword(String password) { + this.password = password; + } + + public boolean authenticate(HttpServletRequest request, HttpServletResponse response) { + if (enabled && !doAuthenticate(request, response)) { + send401(request, response); + return false; + } else + return true; + } + + private boolean doAuthenticate(HttpServletRequest request, HttpServletResponse response) { + + String encoded = request.getHeader("Authorization"); + + if (encoded==null) { + return false; + } + + encoded = encoded.replace("Basic ", ""); + + String decoded = null; + try { + decoded = new String(Base64.decode(encoded)); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + String[] userpass = decoded.split(":"); + if(userpass.length < 2) { + return false; + } + + if(userpass[0].equals(user) && userpass[1].equals(password)) + return true; + else + return false; + } + + private void send401(HttpServletRequest request, HttpServletResponse response) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.addHeader("WWW-Authenticate", "Basic realm=\""+REALM+"\""); + + try { + response.getOutputStream().println("HTTP 401: Authorization Required"); + } catch (IOException e) {} + } + + @Override + public void setAuthenticationEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isAuthenticationEnabled() { + return enabled; + } + +} diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ClientManagerImpl.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ClientManagerImpl.java new file mode 100644 index 0000000000..d715e5dcca --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ClientManagerImpl.java @@ -0,0 +1,133 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ + +package org.gephi.streaming.server.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +import java.util.Map; +import java.util.logging.Logger; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.gephi.streaming.server.ClientManager; +import org.openide.util.Exceptions; + +/** + * + * @author panisson + */ +public class ClientManagerImpl implements ClientManager { + + private static final Logger logger = Logger.getLogger(ClientManagerImpl.class.getName()); + + private Map registeredClients = new HashMap(); + private List listeners = new ArrayList(); + private int idCount = 0; + + public String add(HttpServletRequest request) { + String clientId = request.getRemoteAddr(); + while (registeredClients.containsKey(clientId)) { + clientId = clientId + (idCount++); + } + registeredClients.put(clientId, new ClientData(clientId, request)); + request.setAttribute("CLIENT_IDENTIFIER", clientId); + for (ClientManagerListener listener: listeners) { + listener.clientConnected(clientId); + } + return clientId; + } + + public void stopAll() { + Iterator clients = registeredClients.values().iterator(); + while (clients.hasNext()) { + ClientData client = clients.next(); + // TODO: verify request status + try { + client.request.getAsyncContext().complete(); + } catch (IllegalStateException e) { + logger.warning(e.getMessage()); + } + clients.remove(); + for (ClientManagerListener listener: listeners) { + listener.clientDisconnected(client.clientId); + } + } + } + + public void remove(HttpServletRequest request) { + String clientId = (String)request.getAttribute("CLIENT_IDENTIFIER"); + remove(clientId); + } + + public void remove(String clientId) { + + ClientData client = registeredClients.get(clientId); + registeredClients.remove(clientId); + // TODO: verify request status + try { + client.request.getAsyncContext().complete(); + } catch (IllegalStateException e) { + logger.warning(e.getMessage()); + } + for (ClientManagerListener listener: listeners) { + listener.clientDisconnected(clientId); + } + } + + public void addClientManagerListener(ClientManagerListener listener) { + this.listeners.add(listener); + } + + private class ClientData { + private final HttpServletRequest request; + private final String clientId; + public ClientData(String clientId, HttpServletRequest request) { + this.request = request; + this.clientId = clientId; + } + } + +} diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/DynamicGraphWriter.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/DynamicGraphWriter.java new file mode 100644 index 0000000000..34c2473029 --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/DynamicGraphWriter.java @@ -0,0 +1,349 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; + +import org.gephi.data.attributes.api.AttributeRow; +import org.gephi.data.attributes.api.AttributeValue; +import org.gephi.data.attributes.type.DynamicType; +import org.gephi.data.attributes.type.Interval; +import org.gephi.data.properties.PropertiesColumn; +import org.gephi.dynamic.api.DynamicModel; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.Node; +import org.gephi.streaming.api.GraphEventHandler; +import org.gephi.streaming.api.event.ElementType; +import org.gephi.streaming.api.event.EventType; +import org.gephi.streaming.api.event.GraphEvent; + +/** + * @author panisson + * + */ +public class DynamicGraphWriter extends GraphWriter { + + public DynamicGraphWriter(Graph graph, boolean sendVizData) { + super(graph, sendVizData); + } + + @Override + public void writeGraph(GraphEventHandler operationSupport) { + + try { + graph.readLock(); + + double minRange = Double.MAX_VALUE; + double maxRange = Double.MIN_VALUE; + Map> creation = new HashMap>(); + Map> remotion = new HashMap>(); + Map> changing = new HashMap>(); + + for (Node node: graph.getNodes()) { + + String nodeId = (String) node.getId(); + DynamicType timeInterval = (DynamicType)node.getAttribute(DynamicModel.TIMEINTERVAL_COLUMN); + + List ranges = timeInterval.getIntervals(); + for (Interval range: ranges) { + double created = range.getLow(); + double removed = range.getHigh(); + + if (created < minRange) { + minRange = created; + } + if (removed > maxRange) { + maxRange = removed; + } + + List createdAt = creation.get(created); + if (createdAt == null) { + createdAt = new ArrayList(); + creation.put(created, createdAt); + } + + { + Map attributes = getNodeAttributes(node); + createdAt.add(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, nodeId, attributes)); + } + + List removedAt = remotion.get(removed); + if (removedAt == null) { + removedAt = new ArrayList(); + remotion.put(removed, removedAt); + } + { + Map attributes = new HashMap(); + removedAt.add(eventBuilder.graphEvent(ElementType.NODE, EventType.REMOVE, nodeId, attributes)); + } + + AttributeRow row = (AttributeRow) node.getAttributes(); + if (row != null) { + for (AttributeValue attributeValue : row.getValues()) { + if (attributeValue.getValue() != null + && attributeValue.getColumn().getType().isDynamicType() + && !attributeValue.getColumn().getId().equals(DynamicModel.TIMEINTERVAL_COLUMN)) { + + DynamicType dynamicValue = (DynamicType)attributeValue.getValue(); + List intervals = dynamicValue.getIntervals(); + + for (Interval interval: intervals) { + + Object value = interval.getValue(); + + List changedAt = changing.get(interval.getLow()); + if (changedAt == null) { + changedAt = new ArrayList(); + changing.put(interval.getLow(), changedAt); + } + Map attributes = new HashMap(); + attributes.put(attributeValue.getColumn().getTitle(), value); + + changedAt.add(eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, nodeId, attributes)); + + } + } + } + } + } + } + + for (Edge edge: graph.getEdges()) { + + String edgeId = (String) edge.getId(); + DynamicType timeInterval = (DynamicType)edge.getAttributes(DynamicModel.TIMEINTERVAL_COLUMN); + + List ranges = timeInterval.getIntervals(); + for (Interval range: ranges) { + double created = range.getLow(); + double removed = range.getHigh(); + + if (created < minRange) { + minRange = created; + } + if (removed > maxRange) { + maxRange = removed; + } + + List createdAt = creation.get(created); + if (createdAt == null) { + createdAt = new ArrayList(); + creation.put(created, createdAt); + } + { + Map attributes = getEdgeAttributes(edge); + String sourceId = (String) edge.getSource().getId(); + String targetId = (String) edge.getTarget().getId(); + createdAt.add(eventBuilder.edgeAddedEvent(edgeId, sourceId, targetId, edge.isDirected(), attributes)); + } + + List removedAt = remotion.get(removed); + if (removedAt == null) { + removedAt = new ArrayList(); + remotion.put(removed, removedAt); + } + { + Map attributes = new HashMap(); + removedAt.add(0,eventBuilder.graphEvent(ElementType.EDGE, EventType.REMOVE, edgeId, attributes)); + } + + AttributeRow row = (AttributeRow) edge.getAttributes(); + if (row != null) { + for (AttributeValue attributeValue : row.getValues()) { + if (attributeValue.getValue() != null + && attributeValue.getColumn().getType().isDynamicType() + && !attributeValue.getColumn().getId().equals(DynamicModel.TIMEINTERVAL_COLUMN)) { + + DynamicType dynamicValue = (DynamicType)attributeValue.getValue(); + List intervals = dynamicValue.getIntervals(); + + for (Interval interval: intervals) { + + Object value = interval.getValue(); + + List changedAt = changing.get(interval.getLow()); + if (changedAt == null) { + changedAt = new ArrayList(); + changing.put(interval.getLow(), changedAt); + } + Map attributes = new HashMap(); + attributes.put(attributeValue.getColumn().getTitle(), value); + + changedAt.add(eventBuilder.graphEvent(ElementType.EDGE, EventType.CHANGE, edgeId, attributes)); + + } + } + } + } + } + } + + List pre; + pre = creation.get(Double.NEGATIVE_INFINITY); + if (pre!=null) { + List first = creation.get(minRange); + first.addAll(pre); + creation.remove(Double.NEGATIVE_INFINITY); + } + pre = changing.get(Double.NEGATIVE_INFINITY); + if (pre!=null) { + List first = creation.get(minRange); + first.addAll(pre); + changing.remove(Double.NEGATIVE_INFINITY); + } + + List after = remotion.get(Double.POSITIVE_INFINITY); + if (after!=null) { + List last = remotion.get(maxRange); + last.addAll(pre); + remotion.remove(Double.POSITIVE_INFINITY); + } + + TreeSet ordered = new TreeSet(); + ordered.addAll(creation.keySet()); + ordered.addAll(remotion.keySet()); + ordered.addAll(changing.keySet()); + + for (Double ts: ordered) { + + List createdAt = creation.get(ts); + if (createdAt!=null) { + for (GraphEvent e: createdAt) { + e.setTimestamp(ts); + operationSupport.handleGraphEvent(e); + } + } + + List changedAt = changing.get(ts); + if (changedAt!=null) { + for (GraphEvent e: changedAt) { + e.setTimestamp(ts); + operationSupport.handleGraphEvent(e); + } + } + + //if (ts == ordered.last()) break; + + List removedAt = remotion.get(ts); + if (removedAt!=null) { + for (GraphEvent e: removedAt) { + e.setTimestamp(ts); + operationSupport.handleGraphEvent(e); + } + } + } + + } catch (Exception e) { + e.printStackTrace(); + } finally { + graph.readUnlock(); + } + } + + private Map getNodeAttributes(Node node) { + Map attributes = new HashMap(); + AttributeRow row = (AttributeRow) node.getAttributes(); + + if (row != null) + for (AttributeValue attributeValue: row.getValues()) { + if (attributeValue.getColumn().getIndex()!=PropertiesColumn.NODE_ID.getIndex() + && attributeValue.getValue()!=null + && !attributeValue.getColumn().getType().isDynamicType()) + attributes.put(attributeValue.getColumn().getTitle(), attributeValue.getValue()); + } + + if (sendVizData) { + attributes.put("x", node.x()); + attributes.put("y", node.y()); + attributes.put("z", node.z()); + + attributes.put("r", node.r()); + attributes.put("g", node.g()); + attributes.put("b", node.b()); + + attributes.put("size", node.getAttribute("size")); + } + + return attributes; + } + + private Map getEdgeAttributes(Edge edge) { + Map attributes = new HashMap(); + AttributeRow row = (AttributeRow) edge.getAttributes(); + if (row != null) + for (AttributeValue attributeValue: row.getValues()) { + if (attributeValue.getColumn().getIndex()!=PropertiesColumn.EDGE_ID.getIndex() + && attributeValue.getValue()!=null + && !attributeValue.getColumn().getType().isDynamicType()) + attributes.put(attributeValue.getColumn().getTitle(), attributeValue.getValue()); + } + + if (sendVizData) { + + attributes.put("r", edge.r()); + attributes.put("g", edge.g()); + attributes.put("b", edge.b()); + + attributes.put("weight", edge.getWeight()); + } + + return attributes; + } + + private class ChangingData { + private Object object; + private String attributeName; + private Object attributeValue; + + public ChangingData(Object object, String attributeName, Object attributeValue) { + this.object = object; + this.attributeName = attributeName; + this.attributeValue = attributeValue; + } + } + +} diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/FilteredGraphEventHandler.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/FilteredGraphEventHandler.java new file mode 100644 index 0000000000..1e675524a7 --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/FilteredGraphEventHandler.java @@ -0,0 +1,70 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server.impl; + +import java.util.Set; + +import org.gephi.streaming.api.GraphEventHandler; +import org.gephi.streaming.api.event.GraphEvent; + +/** + * @author panisson + * + */ +public class FilteredGraphEventHandler implements GraphEventHandler { + + private Set filteredEvents; + private GraphEventHandler operationSupport; + private Object source = this; + + public FilteredGraphEventHandler(GraphEventHandler operationSupport, Set filteredEvents) { + this.operationSupport = operationSupport; + this.filteredEvents = filteredEvents; + } + + @Override + public void handleGraphEvent(GraphEvent event) { + if(!filteredEvents.contains(event)) + operationSupport.handleGraphEvent(event); + } + +} diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphBufferedEventHandler.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphBufferedEventHandler.java new file mode 100644 index 0000000000..55a9634261 --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphBufferedEventHandler.java @@ -0,0 +1,78 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server.impl; + +import org.gephi.data.attributes.api.AttributeController; +import org.gephi.data.attributes.api.AttributeModel; +import org.gephi.dynamic.api.DynamicModel; +import org.gephi.graph.api.Graph; +import org.gephi.streaming.api.CompositeGraphEventHandler; +import org.gephi.streaming.api.GraphEventHandler; +import org.openide.util.Lookup; + +/** + * @author panisson + * + */ +public class GraphBufferedEventHandler extends CompositeGraphEventHandler { + + private GraphWriter graphWriter; + + public GraphBufferedEventHandler(Graph graph) { + + // check if graph is dynamic + AttributeModel attributeModel = Lookup.getDefault().lookup(AttributeController.class) + .getModel(graph.getModel().getWorkspace()); + if (attributeModel.getNodeTable().hasColumn(DynamicModel.TIMEINTERVAL_COLUMN) + && attributeModel.getEdgeTable().hasColumn(DynamicModel.TIMEINTERVAL_COLUMN)) { + this.graphWriter = new DynamicGraphWriter(graph, false); + } else { + this.graphWriter = new GraphWriter(graph, true); + } + } + + @Override + public void addHandler(GraphEventHandler operationSupport) { + graphWriter.writeGraph(operationSupport); + super.addHandler(operationSupport); + } +} diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphWriter.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphWriter.java new file mode 100644 index 0000000000..96dcb15d4f --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphWriter.java @@ -0,0 +1,172 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server.impl; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.gephi.data.attributes.api.AttributeRow; +import org.gephi.data.attributes.api.AttributeValue; +import org.gephi.data.properties.PropertiesColumn; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.Node; +import org.gephi.streaming.api.CompositeGraphEventHandler; +import org.gephi.streaming.api.event.GraphEventBuilder; +import org.gephi.streaming.api.GraphEventHandler; +import org.gephi.streaming.api.event.ElementType; +import org.gephi.streaming.api.event.EventType; + +/** + * @author panisson + * + */ +public class GraphWriter extends CompositeGraphEventHandler { + + protected final Graph graph; + protected final boolean sendVizData; + protected final GraphEventBuilder eventBuilder; + + public GraphWriter(Graph graph, boolean sendVizData) { + this.graph = graph; + this.sendVizData = sendVizData; + this.eventBuilder = new GraphEventBuilder(this); + } + + public void writeGraph(GraphEventHandler operationSupport) { + + Set writtenNodes = new HashSet(); + Set writtenEdges = new HashSet(); + + try { + graph.readLock(); + + for (Node node: graph.getNodes()) { + String nodeId = (String) node.getId(); + operationSupport.handleGraphEvent( + eventBuilder.graphEvent(ElementType.NODE, + EventType.ADD, nodeId, getNodeAttributes(node))); + writtenNodes.add(nodeId); + + for (Edge edge: graph.getEdges(node)) { + String edgeId = (String) edge.getId(); + if (writtenEdges.contains(edgeId)) continue; + String sourceId = (String) edge.getSource().getId(); + String targetId = (String) edge.getTarget().getId(); + if (writtenNodes.contains(sourceId) + && writtenNodes.contains(targetId) ) { + operationSupport.handleGraphEvent( + eventBuilder.edgeAddedEvent(edgeId, sourceId, + targetId, edge.isDirected(), getEdgeAttributes(edge))); + writtenEdges.add(edgeId); + } + } + + } + +// for (Edge edge: graph.getEdges()) { +// String edgeId = edge.getEdgeData().getId(); +// String sourceId = edge.getSource().getNodeData().getId(); +// String targetId = edge.getTarget().getNodeData().getId(); +// operationSupport.handleGraphEvent( +// eventBuilder.edgeAddedEvent(edgeId, sourceId, +// targetId, edge.isDirected(), getEdgeAttributes(edge))); +// } + + } catch (Exception e) { + e.printStackTrace(); + } finally { + graph.readUnlock(); + } + } + + private Map getNodeAttributes(Node node) { + Map attributes = new HashMap(); + AttributeRow row = (AttributeRow) node.getAttributes(); + + if (row != null) + for (AttributeValue attributeValue: row.getValues()) { + if (attributeValue.getColumn().getIndex()!=PropertiesColumn.NODE_ID.getIndex() + && attributeValue.getValue()!=null) + attributes.put(attributeValue.getColumn().getTitle(), attributeValue.getValue()); + } + + if (sendVizData) { + attributes.put("x", node.x()); + attributes.put("y", node.y()); + attributes.put("z", node.z()); + + attributes.put("r", node.r()); + attributes.put("g", node.g()); + attributes.put("b", node.b()); + + attributes.put("size", node.getAttribute("size")); + } + + return attributes; + } + + private Map getEdgeAttributes(Edge edge) { + Map attributes = new HashMap(); + AttributeRow row = (AttributeRow) edge.getAttributes(); + if (row != null) + for (AttributeValue attributeValue: row.getValues()) { + if (attributeValue.getColumn().getIndex()!=PropertiesColumn.EDGE_ID.getIndex() + && attributeValue.getValue()!=null) + attributes.put(attributeValue.getColumn().getTitle(), attributeValue.getValue()); + } + + if (sendVizData) { + + attributes.put("r", edge.r()); + attributes.put("g", edge.g()); + attributes.put("b", edge.b()); + + attributes.put("weight", edge.getWeight()); + } + + return attributes; + } + +} diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerControllerFactoryImpl.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerControllerFactoryImpl.java new file mode 100644 index 0000000000..f90a38eb16 --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerControllerFactoryImpl.java @@ -0,0 +1,64 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server.impl; + +import org.gephi.graph.api.Graph; +import org.gephi.streaming.server.ServerController; +import org.gephi.streaming.server.ServerControllerFactory; +import org.openide.util.lookup.ServiceProvider; + +/** + * @author panisson + * + */ +@ServiceProvider(service = ServerControllerFactory.class) +public class ServerControllerFactoryImpl implements ServerControllerFactory { + + /* (non-Javadoc) + * @see org.gephi.streaming.server.ServerControllerFactory#createServerController(org.gephi.graph.api.Graph) + */ + @Override + public ServerController createServerController(Graph graph) { + return new ServerControllerImpl(graph); + } + +} diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerControllerImpl.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerControllerImpl.java new file mode 100644 index 0000000000..5942c5aac0 --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerControllerImpl.java @@ -0,0 +1,210 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server.impl; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.servlet.AsyncContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.gephi.graph.api.Graph; +import org.gephi.streaming.server.ServerController; +import org.openide.util.Exceptions; + +/** + * @author panisson + * + */ +public class ServerControllerImpl implements ServerController { + + private ClientManagerImpl clientManager; + + private enum Operations { + GET_GRAPH("getGraph"), + GET_NODE("getNode"), + GET_EDGE("getEdge"), + UPDATE_GRAPH("updateGraph") + ; + + private final String url; + private Operations(String url) { + this.url = url; + } + + public String getURL() { + return url; + } + } + + private final ServerOperationExecutor executor; + + public ServerControllerImpl(Graph graph) { + clientManager = new ClientManagerImpl(); + executor = new ServerOperationExecutor(graph, clientManager); + + } + + /* (non-Javadoc) + * @see org.gephi.streaming.server.impl.ServerController#getClientManager() + */ + public ClientManagerImpl getClientManager() { + return clientManager; + } + + public ServerOperationExecutor getServerOperationExecutor() { + return executor; + } + + /* (non-Javadoc) + * @see org.gephi.streaming.server.impl.ServerController#handle(org.gephi.streaming.server.Request, org.gephi.streaming.server.Response) + */ + public void handle(HttpServletRequest request, HttpServletResponse response) { + + long time = System.currentTimeMillis(); + + response.addHeader("Content-Type", "text/plain"); + response.addHeader("Server", "Gephi/0.7 alpha4"); + response.addHeader("Connection", "close"); + response.setDateHeader("Date", time); + response.setDateHeader("Last-Modified", time); + + // Collect body data before getting parameters + byte[] body; + InputStream inputStream = null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + inputStream = request.getInputStream(); + int read; + while ((read = inputStream.read())!=-1) { + baos.write(read); + } + body = baos.toByteArray(); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + body = new byte[0]; + } finally { + try { + inputStream.close(); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + + try { + + + String operation = request.getParameter("operation"); + if(operation==null) { + // Default operation is GET_GRAPH + operation = Operations.GET_GRAPH.getURL(); + } + String format = request.getParameter("format"); + if(format==null) { + // Default format is JSON + format = "JSON"; + } + OutputStream outputStream = response.getOutputStream(); + System.out.println("Handling request for operation "+operation+", format "+format); + + if (operation.equals(Operations.GET_GRAPH.getURL())) { + response.flushBuffer(); + String clientId = clientManager.add(request); + + executor.executeGetGraph(format, clientId, outputStream); + + String close = request.getParameter("close"); + if (close == null) { + AsyncContext aCtx = request.startAsync(); + aCtx.setTimeout(-1); + } + + } else if (operation.equals(Operations.GET_NODE.getURL())) { + // gets the node id and write info to output stream + String id = request.getParameter("id"); + if(id==null) { + executeError(response, "Invalid id"); + return; + } + executor.executeGetNode(id, format, outputStream); + + } else if (operation.equals(Operations.GET_EDGE.getURL())) { + // gets the edge id and write info to output stream + String id = request.getParameter("id"); + if(id==null) { + executeError(response, "Invalid id"); + return; + } + executor.executeGetEdge(id, format, outputStream); + + } else if (operation.equals(Operations.UPDATE_GRAPH.getURL())) { + executor.executeUpdateGraph(format, new ByteArrayInputStream(body), outputStream); + } else { + executeError(response, "Invalid operation: "+operation); + return; + } + } catch (Exception e) { + e.printStackTrace(); + try { + executeError(response, "Error: "+e.getMessage()); + } catch (IOException e1) { } + return; + } + } + + /* (non-Javadoc) + * @see org.gephi.streaming.server.impl.ServerController#stop() + */ + public void stop() { + clientManager.stopAll(); + } + + private void executeError(HttpServletResponse response, String message) throws IOException { + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + response.getOutputStream().println(message); + } + +} diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerOperationExecutor.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerOperationExecutor.java new file mode 100644 index 0000000000..aef09f28a9 --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerOperationExecutor.java @@ -0,0 +1,274 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server.impl; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.SocketException; +import java.util.HashMap; +import java.util.Map; + +import org.gephi.data.attributes.api.AttributeController; +import org.gephi.data.attributes.api.AttributeRow; +import org.gephi.data.attributes.api.AttributeValue; +import org.gephi.data.properties.PropertiesColumn; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.Node; +import org.gephi.streaming.api.CompositeGraphEventHandler; +import org.gephi.streaming.api.Graph2EventListener; +import org.gephi.streaming.api.GraphEventHandler; +import org.gephi.streaming.api.event.GraphEvent; +import org.gephi.streaming.api.event.GraphEventBuilder; +import org.gephi.streaming.api.GraphUpdaterEventHandler; +import org.gephi.streaming.api.StreamReader; +import org.gephi.streaming.api.StreamReaderFactory; +import org.gephi.streaming.api.StreamWriter; +import org.gephi.streaming.api.StreamWriterFactory; +import org.gephi.streaming.api.event.ElementType; +import org.gephi.streaming.api.event.EventType; +import org.openide.util.Lookup; + +/** + * @author panisson + * + */ +public class ServerOperationExecutor { + + private final GraphBufferedEventHandler graphBufferedOperationSupport; + private final GraphUpdaterEventHandler graphUpdaterOperationSupport; + private final Graph graph; + private final StreamWriterFactory writerFactory; + private final StreamReaderFactory readerFactory; + private boolean sendVizData = true; + private final GraphEventBuilder eventBuilder; + private ClientManagerImpl clientManager; + + public ServerOperationExecutor(Graph graph, ClientManagerImpl clientManager) { + graphBufferedOperationSupport = new GraphBufferedEventHandler(graph); + graphUpdaterOperationSupport = new GraphUpdaterEventHandler(graph); + this.graph = graph; + this.clientManager = clientManager; + writerFactory = Lookup.getDefault().lookup(StreamWriterFactory.class); + readerFactory = Lookup.getDefault().lookup(StreamReaderFactory.class); + eventBuilder = new GraphEventBuilder(this); + + Graph2EventListener changeListener = new Graph2EventListener(graph, graphBufferedOperationSupport); + graph.getGraphModel().addGraphListener(changeListener); + AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); + ac.getModel().addAttributeListener(changeListener); + } + + /** + * Test with
+ * curl http://localhost:8080/graphstream?operation=getGraph&format=DGS + * + * @param format + * @param outputStream + */ + public void executeGetGraph(String format, final String clientId, OutputStream outputStream) throws IOException { + + final StreamWriter writer = writerFactory.createStreamWriter(format, outputStream); + writer.startStream(); + + GraphEventHandler wrapper = new GraphEventHandler() { + + public void handleGraphEvent(GraphEvent event) { + try { + writer.handleGraphEvent(event); + } catch (RuntimeException e) { + Throwable cause = e.getCause(); + if (cause instanceof SocketException || cause instanceof EOFException) { + System.out.println("*Socket closed*"); + graphBufferedOperationSupport.removeHandler(this); + clientManager.remove(clientId); + } else { + throw e; + } + } + } + }; + + graphBufferedOperationSupport.addHandler(wrapper); + } + + /** + * Test with
+ * curl "http://localhost:8080/graphstream?operation=getNode&id=0201485419&format=DGS" + * + * @param id + * @param format + * @param outputStream + * @throws IOException + */ + public void executeGetNode(String id, String format, OutputStream outputStream) throws IOException { + StreamWriter writer = writerFactory.createStreamWriter(format, outputStream); + writer.startStream(); + + graph.readLock(); + try { + Node node = graph.getNode(id); + if (node != null) { + String nodeId = node.getNodeData().getId(); + writer.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, nodeId, getNodeAttributes(node))); + } + } finally { + graph.readUnlock(); + } + + writer.endStream(); + } + + /** + * Test with
+ * curl http://localhost:8080/graphstream?operation=getEdge&id=0201485419_0321335708&format=DGS + * + * @param id + * @param format + * @param outputStream + * @throws IOException + */ + public void executeGetEdge(String id, String format, OutputStream outputStream) throws IOException { + + StreamWriter writer = writerFactory.createStreamWriter(format, outputStream); + writer.startStream(); + + graph.readLock(); + try { + Edge edge = graph.getEdge(id); + if (edge != null) { + String edgeId = edge.getEdgeData().getId(); + String sourceId = edge.getSource().getNodeData().getId(); + String targetId = edge.getTarget().getNodeData().getId(); + writer.handleGraphEvent(eventBuilder.edgeAddedEvent(edgeId, sourceId, targetId, edge.isDirected(), getEdgeAttributes(edge))); + } + } finally { + graph.readUnlock(); + } + writer.endStream(); + } + + /** + * Test with:
+ * curl "http://localhost:8080/graphstream?operation=updateGraph&format=DGS" -d "DGS004
+ * updatedObjects 0 0
+ * an 1111" + * + *

The entire graph can be loaded using
+ * curl "http://localhost:8080/graphstream?operation=updateGraph&format=DGS" --data-binary @amazon.dgs + * + * + * @param format + * @param inputStream + * @param outputStream + * @throws IOException + */ + public void executeUpdateGraph(String format, InputStream inputStream, OutputStream outputStream) + throws IOException { + +// BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); +// +// String line; +// while ((line=reader.readLine())!=null) +// System.out.println(line); +// outputStream.close(); + + StreamWriter writer = writerFactory.createStreamWriter(format, outputStream); + CompositeGraphEventHandler cos = new CompositeGraphEventHandler(); + cos.addHandler(graphUpdaterOperationSupport); + cos.addHandler(writer); +// cos.addHandler(graphBufferedOperationSupport); + + StreamReader reader = readerFactory.createStreamReader(format, cos, eventBuilder); + reader.processStream(inputStream); + } + + private Map getNodeAttributes(Node node) { + Map attributes = new HashMap(); + AttributeRow row = (AttributeRow) node.getNodeData().getAttributes(); + + if (row != null) + for (AttributeValue attributeValue: row.getValues()) { + if (attributeValue.getColumn().getIndex()!=PropertiesColumn.NODE_ID.getIndex() + && attributeValue.getValue()!=null) + attributes.put(attributeValue.getColumn().getTitle(), attributeValue.getValue()); + } + + if (sendVizData) { + attributes.put("x", node.getNodeData().x()); + attributes.put("y", node.getNodeData().y()); + attributes.put("z", node.getNodeData().z()); + + attributes.put("r", node.getNodeData().r()); + attributes.put("g", node.getNodeData().g()); + attributes.put("b", node.getNodeData().b()); + + attributes.put("size", node.getNodeData().getSize()); + } + + return attributes; + } + + private Map getEdgeAttributes(Edge edge) { + Map attributes = new HashMap(); + AttributeRow row = (AttributeRow) edge.getEdgeData().getAttributes(); + if (row != null) + for (AttributeValue attributeValue: row.getValues()) { + if (attributeValue.getColumn().getIndex()!=PropertiesColumn.EDGE_ID.getIndex() + && attributeValue.getValue()!=null) + attributes.put(attributeValue.getColumn().getTitle(), attributeValue.getValue()); + } + + if (sendVizData) { + + attributes.put("r", edge.getEdgeData().r()); + attributes.put("g", edge.getEdgeData().g()); + attributes.put("b", edge.getEdgeData().b()); + + attributes.put("weight", edge.getWeight()); + } + + return attributes; + } +} diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/jetty/GephiWebSocket.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/jetty/GephiWebSocket.java new file mode 100644 index 0000000000..f23ffb3211 --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/jetty/GephiWebSocket.java @@ -0,0 +1,119 @@ +/* +Copyright 2008-2010 Gephi +Author : Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server.impl.jetty; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import javax.servlet.http.HttpServletRequest; +import org.eclipse.jetty.websocket.WebSocket; +import org.gephi.streaming.server.ClientManager; +import org.gephi.streaming.server.impl.ServerOperationExecutor; +import org.openide.util.Exceptions; + +/** + * + * @author panisson + */ +public class GephiWebSocket implements WebSocket.OnTextMessage { + + private Connection connection; + private final ServerOperationExecutor executor; + private final ClientManager clientManager; + private String clientId; + private String format; + + public GephiWebSocket(HttpServletRequest request, ServerOperationExecutor executor, ClientManager clientManager) { + this.executor = executor; + this.clientManager = clientManager; + + format = request.getParameter("format"); + if(format==null) { + // Default format is JSON + format = "JSON"; + } + clientId = clientManager.add(request); + } + + public void onMessage(String m) { + try { + executor.executeUpdateGraph(format, new ByteArrayInputStream(m.getBytes()), new ByteArrayOutputStream()); + connection.sendMessage(m); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + + public void onOpen(Connection cnctn) { + this.connection = cnctn; + try { + executor.executeGetGraph(format, clientId, new WSOutputStream()); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + + public void onClose(int i, String string) { + clientManager.remove(clientId); + } + + private class WSOutputStream extends OutputStream { + + @Override + public void write(int b) throws IOException { + this.write(new byte[]{(byte)b}, 0, 1); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + connection.sendMessage(new String(b, off, len)); + } + + @Override + public void write(byte b[]) throws IOException { + connection.sendMessage(new String(b)); + } + + } + +} diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/jetty/GephiWebSocketAcceptor.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/jetty/GephiWebSocketAcceptor.java new file mode 100644 index 0000000000..5c5e6185e2 --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/jetty/GephiWebSocketAcceptor.java @@ -0,0 +1,72 @@ +/* +Copyright 2008-2010 Gephi +Author : Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server.impl.jetty; + +import javax.servlet.http.HttpServletRequest; +import org.eclipse.jetty.websocket.WebSocket; +import org.eclipse.jetty.websocket.WebSocketFactory; +import org.gephi.streaming.server.ClientManager; +import org.gephi.streaming.server.impl.ServerOperationExecutor; + +/** + * + * @author panisson + */ +public class GephiWebSocketAcceptor implements WebSocketFactory.Acceptor { + private ServerOperationExecutor executor; + private ClientManager clientManager; + + public GephiWebSocketAcceptor(ServerOperationExecutor executor, ClientManager clientManager) { + this.executor = executor; + this.clientManager = clientManager; + } + + public WebSocket doWebSocketConnect(HttpServletRequest hsr, String string) { + return new GephiWebSocket(hsr, executor, clientManager); + } + + public boolean checkOrigin(HttpServletRequest hsr, String string) { + // Allow all origins + return true; + } + +} diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/jetty/StreamingServerImpl.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/jetty/StreamingServerImpl.java new file mode 100644 index 0000000000..f9f618edac --- /dev/null +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/jetty/StreamingServerImpl.java @@ -0,0 +1,347 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server.impl.jetty; + +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; + +import javax.servlet.Servlet; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.servlet.ServletMapping; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.websocket.WebSocketFactory; +import org.gephi.streaming.server.AuthenticationFilter; +import org.gephi.streaming.server.ServerController; +import org.gephi.streaming.server.StreamingServer; +import org.gephi.streaming.server.StreamingServerConfig; +import org.gephi.streaming.server.impl.BasicAuthenticationFilter; +import org.openide.util.Exceptions; +import org.openide.util.lookup.ServiceProvider; + +/** + * @author panisson + * + */ +@ServiceProvider(service = StreamingServer.class) +public class StreamingServerImpl implements StreamingServer { + + private static final Logger logger = Logger.getLogger(StreamingServerImpl.class.getName()); + + private StreamingServerConfig settings; + + private Map containers = + Collections.synchronizedMap(new HashMap()); + private Server server; + private ServletContextHandler context; + + private boolean started = false; + + public StreamingServerImpl() { + settings = new StreamingServerConfig(); + } + + public StreamingServerConfig getServerSettings() { + return settings; + } + + /* (non-Javadoc) + * @see org.gephi.streaming.server.impl.StreamingServer#register(org.gephi.streaming.server.ServerController, java.lang.String) + */ + public void register(ServerController controller, String context) { + logger.log(Level.INFO, "Registering controller at context {0}", context); + + if (!this.started) { + try { + this.start(); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + ContextContainer contextContainer = new ContextContainer(controller); + containers.put(context, contextContainer); + this.context.addServlet(new ServletHolder(contextContainer), context+"/*"); + } + + /* (non-Javadoc) + * @see org.gephi.streaming.server.impl.StreamingServer#unregister(java.lang.String) + */ + public void unregister(String context) { + logger.log(Level.INFO, "Unregistering controller at context {0}", context); + ContextContainer contextContainer = containers.remove(context); + try { + removeServlet(this.context, contextContainer); + } catch (ServletException ex) { + Exceptions.printStackTrace(ex); + } + contextContainer.getServerController().stop(); + + if (containers.isEmpty()) { + try { + this.stop(); + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + } + + /* (non-Javadoc) + * @see org.gephi.streaming.server.impl.StreamingServer#start() + */ + public synchronized void start() throws IOException { + + if (!started) { + logger.info("Starting StreamingServer..."); + + server = new Server(); + + SelectChannelConnector connector = new SelectChannelConnector(); + connector.setPort(settings.getPort()); + server.addConnector(connector); + + if (settings.isUseSSL()) { + + try { + SslSelectChannelConnector ssl_connector = new SslSelectChannelConnector(); + ssl_connector.setPort(settings.getSslPort()); + + KeyManagerFactory kmf = null; + KeyStore keyStore = KeyStore.getInstance("PKCS12"); + InputStream keyStoreInputStream = null; + try { + keyStoreInputStream = this.getClass().getResourceAsStream("localhost.p12"); + keyStore.load(keyStoreInputStream, "12345678".toCharArray()); + } finally { + if (keyStoreInputStream != null) { + keyStoreInputStream.close(); + } + } + + kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(keyStore, "12345678".toCharArray()); + + TrustManagerFactory tmf = null; + // Uncomment this to use a non-default trust manager. + // tmf = TrustManagerFactory.getInstance("PKIX"); + // tmf.init(keyStore); + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(kmf != null ? kmf.getKeyManagers() : null, + tmf != null ? tmf.getTrustManagers() : null, null); + + SslContextFactory cf = ssl_connector.getSslContextFactory(); + cf.setSslContext(sslContext); + // cf.setKeyStore(jetty_home + "/etc/keystore"); + // cf.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); + // cf.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); + + server.addConnector(ssl_connector); + + } catch (UnrecoverableKeyException e) { + logger.log(Level.WARNING, null, e); + } catch (KeyManagementException e) { + logger.log(Level.WARNING, null, e); + } catch (NoSuchAlgorithmException e) { + logger.log(Level.WARNING, null, e); + } catch (CertificateException e) { + logger.log(Level.WARNING, null, e); + } catch (KeyStoreException e) { + logger.log(Level.WARNING, null, e); + } + } + + context = new ServletContextHandler(); + context.setContextPath("/"); + server.setHandler(context); + + try { + server.start(); + } catch (Exception ex) { + Exceptions.printStackTrace(ex); + return; + } + + started = true; + if (logger.isLoggable(Level.INFO)) { + logger.log(Level.INFO, "StreamingServer started at {0}", new Date()); + + } + } + } + + /* (non-Javadoc) + * @see org.gephi.streaming.server.impl.StreamingServer#stop() + */ + public synchronized void stop() throws IOException { + + if (!containers.isEmpty()) { + for (ContextContainer container: containers.values()) { + container.getServerController().stop(); + } + } + + if (started) { + try { + server.stop(); + started = false; + + if (logger.isLoggable(Level.INFO)) { + logger.log(Level.INFO, "StreamingServer stopped at {0}", new Date()); + + } + + } catch (Exception ex) { + Exceptions.printStackTrace(ex); + } + } + } + + /* (non-Javadoc) + * @see org.gephi.streaming.server.impl.StreamingServer#isStarted() + */ + public boolean isStarted() { + return started; + } + + private class ContextContainer extends HttpServlet { + + private final ServerController serverController; + private final AuthenticationFilter authenticationFilter; + private WebSocketFactory wsFactory; + + public ContextContainer(ServerController serverController) { + this.serverController = serverController; + + settings = new StreamingServerConfig(); + authenticationFilter = new BasicAuthenticationFilter(); + authenticationFilter.setUser(settings.getUser()); + authenticationFilter.setPassword(settings.getPassword()); + authenticationFilter.setAuthenticationEnabled(settings.isBasicAuthentication()); + + wsFactory = new WebSocketFactory( + new GephiWebSocketAcceptor(serverController.getServerOperationExecutor(), + serverController.getClientManager())); + } + + public ServerController getServerController() { + return serverController; + } + + @Override + public void service(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + if (wsFactory.acceptWebSocket(request, response)) return; + + if (!authenticationFilter.authenticate(request, response)) + return; + + serverController.handle(request, response); + } + + } + + private void removeServlet(ServletContextHandler context, Servlet servlet) + throws ServletException { + ServletHandler handler = context.getServletHandler(); + + /* A list of all the servlets that don't implement the class 'servlet', + (i.e. They should be kept in the context */ + List servlets = new ArrayList(); + + /* The names all the servlets that we remove so we can drop the mappings too */ + Set names = new HashSet(); + + for (ServletHolder holder : handler.getServlets()) { + /* If it is the class we want to remove, then just keep track of its name */ + if (servlet.equals(holder.getServlet())) { + names.add(holder.getName()); + } else /* We keep it */ { + servlets.add(holder); + } + } + + List mappings = new ArrayList(); + + for (ServletMapping mapping : handler.getServletMappings()) { + /* Only keep the mappings that didn't point to one of the servlets we removed */ + if (!names.contains(mapping.getServletName())) { + mappings.add(mapping); + } + } + + /* Set the new configuration for the mappings and the servlets */ + handler.setServletMappings(mappings.toArray(new ServletMapping[0])); + handler.setServlets(servlets.toArray(new ServletHolder[0])); + + } + +} diff --git a/modules/StreamingServer/src/main/nbm/manifest.mf b/modules/StreamingServer/src/main/nbm/manifest.mf new file mode 100644 index 0000000000..7f98f105b9 --- /dev/null +++ b/modules/StreamingServer/src/main/nbm/manifest.mf @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +OpenIDE-Module-Localizing-Bundle: org/gephi/streaming/server/Bundle.properties diff --git a/modules/StreamingServer/src/main/resources/org/gephi/streaming/server/Bundle.properties b/modules/StreamingServer/src/main/resources/org/gephi/streaming/server/Bundle.properties new file mode 100644 index 0000000000..ae9698e32d --- /dev/null +++ b/modules/StreamingServer/src/main/resources/org/gephi/streaming/server/Bundle.properties @@ -0,0 +1,5 @@ +OpenIDE-Module-Display-Category=Plugin +OpenIDE-Module-Long-Description=\ + Streaming Server +OpenIDE-Module-Name=Streaming Server +OpenIDE-Module-Short-Description=Streaming Server diff --git a/modules/StreamingServer/src/main/resources/org/gephi/streaming/server/impl/localhost.p12 b/modules/StreamingServer/src/main/resources/org/gephi/streaming/server/impl/localhost.p12 new file mode 100644 index 0000000000000000000000000000000000000000..67debe3fc7d2eecb90960c991c28fe602b1c66cf GIT binary patch literal 2805 zcmY+^c{CJ^8V2ypLdKr0CX9VoOfq&RVGt^0H>f20mZgTq7z_qwA{31^3E7vi3)zLAz{EhtL5e^) zNdBLgtcU|!|5GuNabVP+=qiBW)SsvIPs5-nzzF&G0xJWfB0K1G+vwWRbZ8y~062XL zivx2uE#7C=Zpk3DPFVtYVBnhC>~)NkjBh)E`1875?wObno7{W1!z$xaq=hu_6Qg zmfarz%j39ezO<^J(Th0v^7M1vq-T?ce7^lL^rsp> z-nmt!{sFEYKA>X2L6izWYpMUR|AfdWv?UHOXdw!7OkRDQ zB1fdX2xJeY3}NR1IuVlvDW!_ zl~`8$7U$}b@%&q48RdZL#uDB!W(G6+W1iO(%_W6XPKxEM1@4+RB+k~XJ~xzbIke+s zl~JF)b}^At+UO(2SM@ZSDL#`d-m6Fqg*sLc7@?YZ8W)_MA2({9+VRkgr}X}az&XN{ z+S{@ytDC;rap6_Ct_-O#xtZSB1oUx0Ei;bys19>r2Xvf!Vh^Dbg z+PBO|QYg5S+OGPb+Y4pBbgq1AIx3`Cy&IqEf+kR=9}AijtzorbU;06ejsre{BR0Bt z&~2G_S`Tn#eC2>x>kOhz-Z-7WG@(`;n@^9V^H*?Ee2Ugq&=dr3Jz&${r&;n5Q^t#E zIqD^`Ef+dBb4ZO|bNg$gmJegeAv+IyOK`!?J2E|7V=`!?XQOAx(r?f)xpP=;@j(bO z$SToB2Vvok61-G zdp#EG+PdpwVTEaJ*nS!B!_5gB?I`g1B#s?^S;4++>nP@|Z~>(J9#Zn!`?0JvBKTYq zc&aeLldEJCHVt5p&&q7crfb>@s0xN(yTDK)$t0))q$Ueik+x(ISofq6@$JVHqk=b?Dv=BCIbbv;m1{aTG#m1L8w$Q>V1GK- z0L?t-OF8`1B%lFvhW_dHbg8k%J0H{VUi?;;yoxO5c+X@^ujd*2C2eM+J6@$MhqAxp@oAr^%*Ekfl{l&O< zq$vv{1HI)j#!7Jmpa)#)1FHn*@yCi0GaYfvF{ftyRLs(#!7?c){de!>-2TEpfFl0~ z&_A@oK{)V*Ke6nelVoK152p|Y02v3`RK$VS{!dxp-^%vt3at25pAP?9Sr87?6A}?U zt5xLZfixeay>G!s^30H?nbY=#?;VGjjW@nHsdjDKJR6M;57vh?L&YDuQcmu8%CgEA zjF;z0tl`&0J?y={1qsyEHoN>H*bMwIUXlz6LjqZ-xhdt?L%z?}^;t<=u=`5;o!cM0 zDflN)RSjYB24+x=h#lBpcYclS>hrrFY ziYtcCS2pG7Oxd=1=)jz;Hj^7n=R@}-_kXkOIV$K?H=5XvU ztpVCzI=w@egU>x@TObEqfKeDl4lFslOVy027sMr~PefmLT!stP=@>h~WDe4j7lQHD z;{I2%SbA!60e)5l0>~z}vTE&dAoN?rw|+JK-_zyiO{|Qyn{}geX)r9St>sh897{PR zF>O^E$%S|u2KKTS@J+CJsxG+z3p-nH0?eN3hdLm*xF(KOL- z-c``zK*v0@3BPjzEZm;HSIQzX^5%o1_#|r~Aa0#gsFd*KmOgS~?Ri47T!exu5MN@Q zM&=ml!+7&aJLR`DIG~2Nd+9y5ESKk{Og?L$M7cL;J-Qm8peE-)j1w{j9&BF_@b$c_gO94SOMKnEsYh6P-1DGMwyniQl&lna(ZVwL@(k4LUXxXs2C7B}X_20r z!liY21hW|&5td0i%@4~c&JOhV{&9;eu*x)PQsWSDsf49PqKJlHdsoq6kO0r%D& zsax}_^_T@lYp=v5)&UTOo5|x~eC{#_b?SG0x2gG(PFkI}md6uQr$p(Muwgcu=3or) zhcMBg>JtOt1)(w5f87=HUSlT5mwAWQb`ZdoYTHpLtat^bowvr19$3hdzQT0&Bf$Awy4^8H-p@6=cw@x~X9F;s)S zrs5n*J6N`2Vz{S^yL6pnQTlc}kkaa>0zl`P+@paGWP01Bv@gAOj-ZNs0cUFs``fBV zXi@A3>#7}~*3sL7%h20r34MmWv4uKYPD!ac)#DGtp89t;IJR-}hE|cpThZ&&5S}B> zN-v{M$Y1`iw0Md1eWzDHE!*d$2FpsM8f7vj%P+-@F-E>0!(Sb33bH*%fHhT?%z|U_ zf@5Cn>z_Q-s!2x*YD6Z+SG-%}73I3$^P-ZK>&^N+v(DD6zlk!k`jw1`W82Eh#DMPn zye*?ILVJGKmssI%k_GI?X{(rtIwh+I-kjP%U|Xa_Qt1*ua$I#6%+ud5Wp!HAKNJkf z;J%e~zRl80xi0T$PPZMLZaDDcWdvrXh=k_a%&+j&8 zON8W=yWS^?S1C+E5;;#q4l!;L;~!Y}og#gh-d1To8~fGJysrPKvNuf46UtSZJy5qY z(6`DnSmAgSwciQmrx-57 zpji3U-E-qT%d^V~1=5T+O~C%7-@EFU3bIi0p`@2l2pw#wum(ulh`WlBd&~XAH@>h~ zYQpm+b;+>ST-7)!MN0$v26WBU#Y#y+QS84uGV?0&fq0Tf-qEoA6u0Sa6iv@im`M(C zQlY4%D5A*B2$o|90-)eiARgj< + + + diff --git a/pom.xml b/pom.xml index a76a9928fe..180b6b8fda 100644 --- a/pom.xml +++ b/pom.xml @@ -7,11 +7,17 @@ 0.9.0 pom - gephi-plugins + graph-streaming + modules/GraphStreaming + modules/DesktopStreaming + modules/StreamingAPI + modules/StreamingImpl + modules/JettyWrapper + modules/StreamingServer From c07d33af3be8bcc3e085bef8413ed424e6ff5ff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Wed, 6 Jan 2016 15:56:51 +0100 Subject: [PATCH 02/30] StreamingImpl tests migration --- modules/StreamingImpl/pom.xml | 12 +- .../test/AbstractStreamProcessorTest.java | 447 ++++++ .../test/DGSStreamProcessorTest.java | 85 + .../streaming/test/DGSStreamWriterTest.java | 144 ++ .../streaming/test/EventComparisonTest.java | 82 + .../test/Graph2EventListenerTest.java | 98 ++ .../streaming/test/GraphUpdaterTest.java | 189 +++ .../gephi/streaming/test/JSONParsingTest.java | 737 +++++++++ .../streaming/test/JSONStreamReaderTest.java | 563 +++++++ .../streaming/test/JSONStreamWriterTest.java | 329 ++++ .../test/JsonStreamProcessorTest.java | 53 + .../gephi/streaming/test/ListenersTest.java | 111 ++ .../streaming/test/MockGraphEventHandler.java | 59 + .../streaming/test/StreamControllerTest.java | 94 ++ .../java/org/gephi/streaming/test/amazon.json | 1405 ++++++++++++++++ .../streaming/test/amazon_0201485419_400.dgs | 1407 +++++++++++++++++ .../org/gephi/streaming/test/triangle.dgs | 8 + 17 files changed, 5820 insertions(+), 3 deletions(-) create mode 100644 modules/StreamingImpl/src/test/java/org/gephi/streaming/test/AbstractStreamProcessorTest.java create mode 100644 modules/StreamingImpl/src/test/java/org/gephi/streaming/test/DGSStreamProcessorTest.java create mode 100644 modules/StreamingImpl/src/test/java/org/gephi/streaming/test/DGSStreamWriterTest.java create mode 100644 modules/StreamingImpl/src/test/java/org/gephi/streaming/test/EventComparisonTest.java create mode 100644 modules/StreamingImpl/src/test/java/org/gephi/streaming/test/Graph2EventListenerTest.java create mode 100644 modules/StreamingImpl/src/test/java/org/gephi/streaming/test/GraphUpdaterTest.java create mode 100644 modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONParsingTest.java create mode 100644 modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONStreamReaderTest.java create mode 100644 modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONStreamWriterTest.java create mode 100644 modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JsonStreamProcessorTest.java create mode 100644 modules/StreamingImpl/src/test/java/org/gephi/streaming/test/ListenersTest.java create mode 100644 modules/StreamingImpl/src/test/java/org/gephi/streaming/test/MockGraphEventHandler.java create mode 100644 modules/StreamingImpl/src/test/java/org/gephi/streaming/test/StreamControllerTest.java create mode 100644 modules/StreamingImpl/src/test/java/org/gephi/streaming/test/amazon.json create mode 100644 modules/StreamingImpl/src/test/java/org/gephi/streaming/test/amazon_0201485419_400.dgs create mode 100644 modules/StreamingImpl/src/test/java/org/gephi/streaming/test/triangle.dgs diff --git a/modules/StreamingImpl/pom.xml b/modules/StreamingImpl/pom.xml index 4ec1cecd35..dbd0b92e27 100644 --- a/modules/StreamingImpl/pom.xml +++ b/modules/StreamingImpl/pom.xml @@ -15,7 +15,7 @@ StreamingImpl - + org.gephi filters-api @@ -36,11 +36,17 @@ org.gephi graph-api - + org.gephi.streaming api 1.0.0 + + junit + junit + 4.10 + test + @@ -52,7 +58,7 @@ Unknown $sourcecode_url - + diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/AbstractStreamProcessorTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/AbstractStreamProcessorTest.java new file mode 100644 index 0000000000..bc26ba3535 --- /dev/null +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/AbstractStreamProcessorTest.java @@ -0,0 +1,447 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphController; +import org.gephi.graph.api.GraphModel; +import org.gephi.project.api.ProjectController; +import org.gephi.project.api.Workspace; +import org.gephi.streaming.api.CompositeGraphEventHandler; +import org.gephi.streaming.api.event.GraphEventBuilder; +import org.gephi.streaming.api.GraphEventHandler; +import org.gephi.streaming.api.GraphUpdaterEventHandler; +import org.gephi.streaming.api.Report; +import org.gephi.streaming.api.StreamReader; +import org.gephi.streaming.api.StreamReaderFactory; +import org.gephi.streaming.api.StreamWriter; +import org.gephi.streaming.api.StreamWriterFactory; +import org.gephi.streaming.api.StreamingConnection; +import org.gephi.streaming.api.StreamingEndpoint; +import org.gephi.streaming.api.event.ElementType; +import org.gephi.streaming.api.event.EventType; +import org.gephi.streaming.api.event.GraphEvent; +import org.gephi.streaming.impl.GraphEventContainer; +import org.gephi.streaming.impl.StreamingConnectionImpl; +import org.junit.Ignore; +import org.junit.Test; +import org.openide.util.Lookup; + +/** + * Test cases for the GraphStream DSG file format. + * + * @author panisson + * + */ +@Ignore +public abstract class AbstractStreamProcessorTest { + + protected String resource = "amazon_0201485419_400.dgs"; + protected String streamType = "DGS"; + + @Test + public void testStreamReader() throws IOException { + + InputStream inputStream = this.getClass().getResourceAsStream(resource); + + MockGraphEventHandler handler = new MockGraphEventHandler(); + + // get the event container + GraphEventContainer container = new GraphEventContainer(handler); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(resource); + StreamReader streamReader = factory.createStreamReader(streamType, container, eventBuilder); + + streamReader.processStream(inputStream); + + container.waitForDispatchAllEvents(); + container.stop(); +// assertEquals(2422, count.get()); + assertTrue(handler.getEventCount()>=1405); +// assertEquals(1405, handler.getEventCount()); +// System.out.println(count.get() + " Events"); + } + + @Test + public void testStreamWriter() throws IOException { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamWriterFactory factory = Lookup.getDefault().lookup(StreamWriterFactory.class); + StreamWriter streamWriter = factory.createStreamWriter(streamType, out); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + + // write triangle + streamWriter.startStream(); + streamWriter.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "A", null)); + streamWriter.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "B", null)); + streamWriter.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "C", null)); + streamWriter.handleGraphEvent(eventBuilder.edgeAddedEvent("AB", "A", "B", false, null)); + streamWriter.handleGraphEvent(eventBuilder.edgeAddedEvent("BC", "B", "C", false, null)); + streamWriter.handleGraphEvent(eventBuilder.edgeAddedEvent("CA", "C", "A", false, null)); + streamWriter.endStream(); + + System.out.println(new String(out.toByteArray())); + } + + @Test + public void testStreamReaderFactory() throws IOException { + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(resource); + StreamReader processor = factory.createStreamReader(streamType, new MockGraphEventHandler(), eventBuilder); + assertNotNull(processor); + } + + @Test + public void testStreamWriterFactory() throws IOException { + + StreamWriterFactory factory = Lookup.getDefault().lookup(StreamWriterFactory.class); + StreamWriter processor = factory.createStreamWriter(streamType, new ByteArrayOutputStream()); + assertNotNull(processor); + } + + @Test + public void testChaining() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamWriterFactory writerFactory = Lookup.getDefault().lookup(StreamWriterFactory.class); + StreamWriter streamWriter = writerFactory.createStreamWriter(streamType, out); + + InputStream inputStream = this.getClass().getResourceAsStream(resource); + + StreamReaderFactory readerfactory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(resource); + StreamReader streamReader = readerfactory.createStreamReader(streamType, streamWriter, eventBuilder); + + streamWriter.startStream(); + streamReader.processStream(inputStream); + streamWriter.endStream(); + + assertTrue(out.toByteArray().length>0); + +// System.out.println(new String(out.toByteArray())); + } + + @Test + public void testStreamingProcess() throws IOException { + + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + projectController.newProject(); + + final InputStream fileInputStream = this.getClass().getResourceAsStream(resource); + + InputStream inputStream = new InputStream() { + private int count = 0; + + @Override + public int read() throws IOException { + count++; + if (count%100 == 0) + try { + Thread.sleep(1); + } catch (InterruptedException e) { } + return fileInputStream.read(); + } + }; + + new Thread() { + @Override + public void run() { + try { + Thread.sleep(100); + fileInputStream.close(); + } + catch(InterruptedException e) {} + catch(IOException e) {} + + } + }.start(); + + MockGraphEventHandler handler = new MockGraphEventHandler(); + + GraphEventContainer container = new GraphEventContainer(handler); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(resource); + StreamReader streamReader = factory.createStreamReader(streamType, container, eventBuilder); + Report report = new Report(); + streamReader.setReport(report); + + streamReader.processStream(inputStream); + + container.waitForDispatchAllEvents(); + container.stop(); + System.out.println(report.getText()); + } + + @Test + public void testAll() throws IOException { + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + projectController.newProject(); + + URL url = this.getClass().getResource(resource); + + final AtomicInteger nodeCount = new AtomicInteger(); + final AtomicInteger edgeCount = new AtomicInteger(); + + GraphEventHandler handler = new GraphEventHandler() { + @Override + public void handleGraphEvent(GraphEvent event) { + if (event.getElementType() == ElementType.NODE && event.getEventType() == EventType.ADD) + nodeCount.incrementAndGet(); + if (event.getElementType() == ElementType.EDGE && event.getEventType() == EventType.ADD) + edgeCount.incrementAndGet(); + } + }; + + // get the event operation support + GraphEventContainer container = new GraphEventContainer(handler); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(resource); + StreamReader streamReader = factory.createStreamReader(streamType, container, eventBuilder); + assertNotNull(streamReader); + + StreamingEndpoint endpoint = new StreamingEndpoint(); + endpoint.setUrl(url); + + StreamingConnection connection = new StreamingConnectionImpl(endpoint, streamReader, new Report()); + + final AtomicBoolean processing = new AtomicBoolean(true); + connection.addStatusListener( + new StreamingConnection.StatusListener() { + @Override + public void onConnectionClosed(StreamingConnection connection) { + processing.set(false); + synchronized (processing) { + processing.notifyAll(); + } + } + @Override + public void onDataReceived(StreamingConnection connection) { } + @Override + public void onError(StreamingConnection connection) { } + }); + connection.asynchProcess(); + + synchronized (processing) { + try { + while (processing.get()) { + processing.wait(); + } + } catch (InterruptedException e) {} + } + + container.waitForDispatchAllEvents(); + container.stop(); + + assertEquals(402, nodeCount.get()); + assertEquals(788, edgeCount.get()); + } + + @Test + public void testSynchFire() throws IOException { + + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + projectController.newProject(); + Workspace workspace = projectController.newWorkspace(projectController.getCurrentProject()); + + GraphController graphController = Lookup.getDefault().lookup(GraphController.class); + GraphModel graphModel = graphController.getModel(); + Graph graph = graphModel.getHierarchicalMixedGraph(); + + GraphUpdaterEventHandler graphUpdaterHandler = new GraphUpdaterEventHandler(graph); + + URL url = this.getClass().getResource(resource); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(resource); + StreamReader streamReader = factory.createStreamReader(streamType, graphUpdaterHandler, eventBuilder); + assertNotNull(streamReader); + + StreamingEndpoint endpoint = new StreamingEndpoint(); + endpoint.setUrl(url); + StreamingConnection connection = new StreamingConnectionImpl(endpoint, streamReader, new Report()); + + final AtomicBoolean processing = new AtomicBoolean(true); + connection.addStatusListener( + new StreamingConnection.StatusListener() { + @Override + public void onConnectionClosed(StreamingConnection connection) { + processing.set(false); + synchronized (processing) { + processing.notifyAll(); + } + } + @Override + public void onDataReceived(StreamingConnection connection) { } + @Override + public void onError(StreamingConnection connection) { } + }); + connection.asynchProcess(); + + synchronized (processing) { + try { + while (processing.get()) { + processing.wait(); + } + } catch (InterruptedException e) {} + } + + assertEquals(402, graph.getNodeCount()); + assertEquals(788, graph.getEdgeCount()); + } + + @Test + public void testAsynchFire() throws IOException { + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + projectController.newProject(); + Workspace workspace = projectController.newWorkspace(projectController.getCurrentProject()); + + GraphController graphController = Lookup.getDefault().lookup(GraphController.class); + GraphModel graphModel = graphController.getModel(); + Graph graph = graphModel.getHierarchicalMixedGraph(); + + GraphUpdaterEventHandler graphUpdaterHandler = new GraphUpdaterEventHandler(graph); + + URL url = this.getClass().getResource(resource); + + // get the event operation support + GraphEventContainer container = new GraphEventContainer(graphUpdaterHandler); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(resource); + StreamReader streamReader = factory.createStreamReader(streamType, container, eventBuilder); + assertNotNull(streamReader); + + StreamingEndpoint endpoint = new StreamingEndpoint(); + endpoint.setUrl(url); + StreamingConnection connection = new StreamingConnectionImpl(endpoint, streamReader, new Report()); + + final AtomicBoolean processing = new AtomicBoolean(true); + connection.addStatusListener( + new StreamingConnection.StatusListener() { + @Override + public void onConnectionClosed(StreamingConnection connection) { + processing.set(false); + synchronized (processing) { + processing.notifyAll(); + } + } + @Override + public void onDataReceived(StreamingConnection connection) { } + @Override + public void onError(StreamingConnection connection) { } + }); + connection.asynchProcess(); + + synchronized (processing) { + try { + while (processing.get()) { + processing.wait(); + } + } catch (InterruptedException e) {} + } + + container.waitForDispatchAllEvents(); + container.stop(); + + assertEquals(402, graph.getNodeCount()); + assertEquals(788, graph.getEdgeCount()); + } + +// @Test + public void getTimesSynchFire() throws IOException { + for (int i=0; i<30; i++) { + testSynchFire(); + } + } + +// @Test + public void getTimesAsynchFire() throws IOException { + for (int i=0; i<30; i++) { + testAsynchFire(); + } + } + + @Test + public void testCompositeOperationSupport() throws IOException { + StreamWriterFactory writerFactory = Lookup.getDefault().lookup(StreamWriterFactory.class); + + ByteArrayOutputStream out1 = new ByteArrayOutputStream(); + StreamWriter streamWriter1 = writerFactory.createStreamWriter(streamType, out1); + + ByteArrayOutputStream out2 = new ByteArrayOutputStream(); + StreamWriter streamWriter2 = writerFactory.createStreamWriter(streamType, out2); + + CompositeGraphEventHandler composite = new CompositeGraphEventHandler(); + composite.addHandler(streamWriter1); + composite.addHandler(streamWriter2); + + InputStream inputStream = this.getClass().getResourceAsStream(resource); + + StreamReaderFactory readerfactory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(resource); + StreamReader streamReader = readerfactory.createStreamReader(streamType, composite, eventBuilder); + + streamWriter1.startStream(); + streamWriter2.startStream(); + streamReader.processStream(inputStream); + streamWriter1.endStream(); + streamWriter2.endStream(); + + assertTrue(out1.toByteArray().length>0); + assertTrue(out2.toByteArray().length>0); + assertTrue(out1.toByteArray().length==out2.toByteArray().length); + } + +} diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/DGSStreamProcessorTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/DGSStreamProcessorTest.java new file mode 100644 index 0000000000..54fd84e520 --- /dev/null +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/DGSStreamProcessorTest.java @@ -0,0 +1,85 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.test; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.gephi.streaming.api.StreamReader; +import org.gephi.streaming.api.StreamReaderFactory; +import org.gephi.streaming.api.StreamWriter; +import org.gephi.streaming.api.StreamWriterFactory; +import org.gephi.streaming.api.event.GraphEventBuilder; +import org.junit.Test; +import org.openide.util.Lookup; + +/** + * Test cases for the GraphStream DSG file format. + * + * @author panisson + * + */ +public class DGSStreamProcessorTest extends AbstractStreamProcessorTest { + + @Test + public void testDGS2JSON() throws IOException { + + OutputStream out = new FileOutputStream("/tmp/a.json"); + + StreamWriterFactory writerFactory = Lookup.getDefault().lookup(StreamWriterFactory.class); +// ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamWriter streamWriter = writerFactory.createStreamWriter("JSON", out); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(resource); + StreamReader processor = factory.createStreamReader(streamType, streamWriter, eventBuilder); + + InputStream inputStream = this.getClass().getResourceAsStream(resource); + processor.processStream(inputStream); + +// System.out.println(new String(out.toByteArray())); + + } + +} diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/DGSStreamWriterTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/DGSStreamWriterTest.java new file mode 100644 index 0000000000..ef2d080a46 --- /dev/null +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/DGSStreamWriterTest.java @@ -0,0 +1,144 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.test; + +import java.io.ByteArrayOutputStream; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.gephi.streaming.api.event.GraphEventBuilder; +import org.gephi.streaming.api.StreamWriter; +import org.gephi.streaming.api.StreamWriterFactory; +import org.gephi.streaming.api.event.ElementType; +import org.gephi.streaming.api.event.EventType; +import org.gephi.streaming.api.event.GraphEvent; +import org.junit.Test; +import org.openide.util.Lookup; + +/** + * Test cases for the JSON Graph Streaming format. + * + * @author panisson + * + */ +public class DGSStreamWriterTest { + + protected String resource = "amazon_0201485419_400.dgs"; + protected String streamType = "DGS"; + + @Test + public void testStreamWriterFactory() throws IOException { + + StreamWriterFactory factory = Lookup.getDefault().lookup(StreamWriterFactory.class); + StreamWriter processor = factory.createStreamWriter(streamType, new ByteArrayOutputStream()); + assertNotNull(processor); + } + + @Test + public void testStreamWriter() throws IOException { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamWriterFactory factory = Lookup.getDefault().lookup(StreamWriterFactory.class); + StreamWriter streamWriter = factory.createStreamWriter(streamType, out); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + + // write triangle + streamWriter.startStream(); + streamWriter.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "A", null)); + streamWriter.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "B", null)); + streamWriter.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "C", null)); + streamWriter.handleGraphEvent(eventBuilder.edgeAddedEvent("AB", "A", "B", false, null)); + streamWriter.handleGraphEvent(eventBuilder.edgeAddedEvent("BC", "B", "C", false, null)); + streamWriter.handleGraphEvent(eventBuilder.edgeAddedEvent("CA", "C", "A", false, null)); + streamWriter.endStream(); + + } + + @Test + public void testWriteEvents() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamWriterFactory factory = Lookup.getDefault().lookup(StreamWriterFactory.class); + StreamWriter streamWriter = factory.createStreamWriter(streamType, out); + + streamWriter.startStream(); + for (GraphEvent event: new EventFactory().getGraphEvents()) { + streamWriter.handleGraphEvent(event); + } + streamWriter.endStream(); + } + + private class EventFactory { + private List events = new ArrayList(); + + public EventFactory() { + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + Map attributes = new HashMap(); + attributes.put("size", 2); + GraphEvent event; + event = eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "A", attributes); + events.add(event); + event = eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "B", attributes); + events.add(event); + event = eventBuilder.edgeAddedEvent("AB", "A", "B", false, attributes); + events.add(event); + event = eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, "A", attributes); + events.add(event); + event = eventBuilder.graphEvent(ElementType.NODE, EventType.REMOVE, "A", attributes); + events.add(event); + event = eventBuilder.graphEvent(ElementType.EDGE, EventType.CHANGE, "AB", attributes); + events.add(event); + event = eventBuilder.graphEvent(ElementType.EDGE, EventType.REMOVE, "AB", attributes); + events.add(event); + event = eventBuilder.graphEvent(ElementType.GRAPH, EventType.CHANGE, null, attributes); + events.add(event); + } + + public List getGraphEvents() { + return events; + } + } + +} diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/EventComparisonTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/EventComparisonTest.java new file mode 100644 index 0000000000..bb4af59cc8 --- /dev/null +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/EventComparisonTest.java @@ -0,0 +1,82 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.test; + +import java.util.HashSet; +import java.util.Set; +import org.gephi.streaming.api.event.ElementEvent; + +import org.gephi.streaming.api.event.ElementType; +import org.gephi.streaming.api.event.EventType; +import org.gephi.streaming.api.event.GraphEvent; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * @author panisson + * + */ +public class EventComparisonTest { + + @Test + public void compareEventTest() { + ElementEvent event = new ElementEvent(this, EventType.ADD, ElementType.NODE, "0321356985", null); + assertTrue(event.equals(event)); + assertTrue(event.hashCode() == event.hashCode()); + + +// cn "0321356985" "full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)" +// cn "0521780195" "label":"An Introduction..." +// cn "0521780195" "full-label":"An Introduction to Support Vector Machines and Other Kernel-based Learning Methods" +// cn "0521813972" "label":"Kernel Methods ..." + + } + + @Test + public void hashSetTest() { + Set set = new HashSet(); + ElementEvent event = new ElementEvent(this, EventType.ADD, ElementType.NODE, "0321356985", null); + set.add(event); + assertTrue(set.contains(event)); + } + +} diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/Graph2EventListenerTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/Graph2EventListenerTest.java new file mode 100644 index 0000000000..580832de04 --- /dev/null +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/Graph2EventListenerTest.java @@ -0,0 +1,98 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ + +package org.gephi.streaming.test; + +import static org.junit.Assert.*; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphController; +import org.gephi.graph.api.GraphFactory; +import org.gephi.graph.api.GraphModel; +import org.gephi.graph.api.Node; +import org.gephi.project.api.ProjectController; +import org.gephi.project.api.Workspace; +import org.gephi.streaming.api.Graph2EventListener; +import org.junit.Test; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; + +/** + * + * @author panisson + */ +public class Graph2EventListenerTest { + + @Test + public void test() { + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + projectController.newProject(); + projectController.newWorkspace(projectController.getCurrentProject()); + + GraphController graphController = Lookup.getDefault().lookup(GraphController.class); + GraphModel graphModel = graphController.getModel(); + Graph graph = graphModel.getHierarchicalMixedGraph(); + GraphFactory factory = graphModel.factory(); + + MockGraphEventHandler handler = new MockGraphEventHandler(); + final Graph2EventListener graph2EventListener = new Graph2EventListener(graph, handler); + graph.getGraphModel().addGraphListener(graph2EventListener); + + Node node1 = factory.newNode(); + graph.addNode(node1); + Node node2 = factory.newNode(); + graph.addNode(node2); + + Edge edge = factory.newEdge(node1, node2); + graph.addEdge(edge); + + graph.removeEdge(edge); + graph.removeNode(node1); + graph.removeNode(node2); + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { } + + assertEquals(6, handler.getEventCount()); + } + +} diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/GraphUpdaterTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/GraphUpdaterTest.java new file mode 100644 index 0000000000..3e586363cf --- /dev/null +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/GraphUpdaterTest.java @@ -0,0 +1,189 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.test; + +import java.util.HashMap; +import java.util.Map; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphController; +import org.gephi.graph.api.GraphModel; +import org.gephi.graph.api.Node; +import org.gephi.project.api.ProjectController; +import org.gephi.streaming.api.event.GraphEventBuilder; +import org.gephi.streaming.api.GraphUpdaterEventHandler; +import org.gephi.streaming.api.event.ElementType; +import org.gephi.streaming.api.event.EventType; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; +import org.openide.util.Lookup; + +public class GraphUpdaterTest { + + private GraphModel graphModel; + + public GraphUpdaterTest() { + } + + @Before + public void setUp() { + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + projectController.newProject(); + + GraphController graphController = Lookup.getDefault().lookup(GraphController.class); + graphModel = graphController.getModel(); + } + + @After + public void tearDown() { + } + + @Test + public void testUpdateGraph() { + Graph graph = graphModel.getMixedGraph(); + GraphUpdaterEventHandler operationSupport = new GraphUpdaterEventHandler(graph); + Map attributes = new HashMap(); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + + attributes.clear(); + attributes.put("label", "1"); + operationSupport.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "1", attributes)); + Node node1 = graph.getNode("1"); + assertNotNull(node1); + assertEquals("1", node1.getNodeData().getAttributes().getValue("label")); + + attributes.clear(); + attributes.put("label", "2"); + operationSupport.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "2", attributes)); + Node node2 = graph.getNode("2"); + assertNotNull(node2); + assertEquals("2", node2.getNodeData().getAttributes().getValue("label")); + + attributes.clear(); + attributes.put("weight", 0.5f); + operationSupport.handleGraphEvent(eventBuilder.edgeAddedEvent("1_2", "1", "2", false, attributes)); + Edge edge1_2 = graph.getEdge("1_2"); + assertNotNull(edge1_2); + assertEquals(0.5f, edge1_2.getWeight(), 1.0e-5); + + operationSupport.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.REMOVE, "1", attributes)); + node1 = graph.getNode("1"); + assertNull(node1); + + edge1_2 = graph.getEdge("1_2"); + assertNull(edge1_2); + + attributes.clear(); + attributes.put("label", "1"); + operationSupport.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "1", attributes)); + node1 = graph.getNode("1"); + assertNotNull(node1); + + attributes.clear(); + attributes.put("weight", 0.1f); + operationSupport.handleGraphEvent(eventBuilder.edgeAddedEvent("1_2", "1", "2", false, attributes)); + edge1_2 = graph.getEdge("1_2"); + assertNotNull(edge1_2); + assertEquals(0.1f, edge1_2.getWeight(), 1.0e-5); + + attributes.clear(); + attributes.put("label", "Node 1"); + operationSupport.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, "1", attributes)); + node1 = graph.getNode("1"); + assertNotNull(node1); + assertEquals("Node 1", node1.getNodeData().getAttributes().getValue("label")); + + attributes.clear(); + attributes.put("label", null); + operationSupport.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, "1", attributes)); + node1 = graph.getNode("1"); + assertNotNull(node1); + assertNull("Label should be null but was "+ + node1.getNodeData().getAttributes().getValue("label"), + node1.getNodeData().getAttributes().getValue("label")); + + attributes.clear(); + attributes.put("label", "Node 1"); + operationSupport.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, "1", attributes)); + + attributes.clear(); + attributes.put("myattribute", "myattributevalue"); + operationSupport.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, "1", attributes)); + node1 = graph.getNode("1"); + assertNotNull(node1); + assertEquals("Node 1", node1.getNodeData().getAttributes().getValue("label")); + assertEquals("myattributevalue", node1.getNodeData().getAttributes().getValue("myattribute")); + + attributes.clear(); + attributes.put("myattribute", null); + operationSupport.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, "1", attributes)); + node1 = graph.getNode("1"); + assertNotNull(node1); + assertEquals("Node 1", node1.getNodeData().getAttributes().getValue("label")); + assertNull(node1.getNodeData().getAttributes().getValue("myattribute")); + + attributes.clear(); + attributes.put("label", "Node 1"); + attributes.put("size", 5); + attributes.put("r", 0.5); + attributes.put("g", 0.6); + attributes.put("b", 0.7); + attributes.put("x", 1); + attributes.put("y", 2); + attributes.put("z", 3); + operationSupport.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, "1", attributes)); + node1 = graph.getNode("1"); + assertNotNull(node1); + assertEquals("Node 1", node1.getNodeData().getAttributes().getValue("label")); + assertEquals(5, node1.getNodeData().getSize(), 1.0e-5); + assertEquals(0.5, node1.getNodeData().r(), 1.0e-5); + assertEquals(0.6, node1.getNodeData().g(), 1.0e-5); + assertEquals(0.7, node1.getNodeData().b(), 1.0e-5); + assertEquals(1, node1.getNodeData().x(), 1.0e-5); + assertEquals(2, node1.getNodeData().y(), 1.0e-5); + assertEquals(3, node1.getNodeData().z(), 1.0e-5); + + } + +} diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONParsingTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONParsingTest.java new file mode 100644 index 0000000000..66f1696ed6 --- /dev/null +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONParsingTest.java @@ -0,0 +1,737 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.test; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.io.StringWriter; +import org.gephi.streaming.impl.json.parser.CDL; +import org.gephi.streaming.impl.json.parser.Cookie; +import org.gephi.streaming.impl.json.parser.CookieList; +import org.gephi.streaming.impl.json.parser.HTTP; +import org.gephi.streaming.impl.json.parser.JSONArray; +import org.gephi.streaming.impl.json.parser.JSONML; +import org.gephi.streaming.impl.json.parser.JSONObject; +import org.gephi.streaming.impl.json.parser.JSONString; +import org.gephi.streaming.impl.json.parser.JSONStringer; +import org.gephi.streaming.impl.json.parser.JSONTokener; +import org.gephi.streaming.impl.json.parser.XML; +import org.junit.Test; + +/** + * JSONParsingTest class. This file is not formally a member of the org.json library. + * It is just a casual test tool. + */ +public class JSONParsingTest { + + public static void main(String args[]) { + JSONParsingTest test = new JSONParsingTest(); + test.run(); + } + + /** + * Entry point. + * @param args + */ + @Test + public void run() { + Iterator it; + JSONArray a; + JSONObject j; + JSONStringer jj; + Object o; + String s; + +/** + * Obj is a typical class that implements JSONString. It also + * provides some beanie methods that can be used to + * construct a JSONObject. It also demonstrates constructing + * a JSONObject with an array of names. + */ + class Obj implements JSONString { + public String aString; + public double aNumber; + public boolean aBoolean; + + public Obj(String string, double n, boolean b) { + this.aString = string; + this.aNumber = n; + this.aBoolean = b; + } + + public double getNumber() { + return this.aNumber; + } + + public String getString() { + return this.aString; + } + + public boolean isBoolean() { + return this.aBoolean; + } + + public String getBENT() { + return "All uppercase key"; + } + + public String getX() { + return "x"; + } + + public String toJSONString() { + return "{" + JSONObject.quote(this.aString) + ":" + + JSONObject.doubleToString(this.aNumber) + "}"; + } + public String toString() { + return this.getString() + " " + this.getNumber() + " " + + this.isBoolean() + "." + this.getBENT() + " " + this.getX(); + } + } + + + Obj obj = new Obj("A beany object", 42, true); + + try { + s = "[0.1]"; + a = new JSONArray(s); + System.out.println(a.toString()); + System.out.println(""); + + j = XML.toJSONObject(" Ignore the stuff past the end. "); + System.out.println(j.toString()); + System.out.println(""); + + j = new JSONObject(); + o = null; + j.put("booga", o); + j.put("wooga", JSONObject.NULL); + System.out.println(j.toString()); + System.out.println(""); + + j = new JSONObject(); + j.increment("two"); + j.increment("two"); + System.out.println(j.toString()); + System.out.println(""); + + + s = ""; + j = XML.toJSONObject(s); + System.out.println(j.toString(2)); + System.out.println(XML.toString(j)); + System.out.println(""); + + s = "{ \"list of lists\" : [ [1, 2, 3], [4, 5, 6], ] }"; + j = new JSONObject(s); + System.out.println(j.toString(4)); + System.out.println(XML.toString(j)); + + s = " Basic bread Flour Yeast Water Salt Mix all ingredients together. Knead thoroughly. Cover with a cloth, and leave for one hour in warm room. Knead again. Place in a bread baking tin. Cover with a cloth, and leave for one hour in warm room. Bake in the oven at 180(degrees)C for 30 minutes. "; + j = XML.toJSONObject(s); + System.out.println(j.toString(4)); + System.out.println(); + + j = JSONML.toJSONObject(s); + System.out.println(j.toString()); + System.out.println(JSONML.toString(j)); + System.out.println(); + + a = JSONML.toJSONArray(s); + System.out.println(a.toString(4)); + System.out.println(JSONML.toString(a)); + System.out.println(); + + s = "

JSONML is a transformation between JSON and XML that preserves ordering of document features.

JSONML can work with JSON arrays or JSON objects.

Three
little
words

"; + j = JSONML.toJSONObject(s); + System.out.println(j.toString(4)); + System.out.println(JSONML.toString(j)); + System.out.println(); + + a = JSONML.toJSONArray(s); + System.out.println(a.toString(4)); + System.out.println(JSONML.toString(a)); + System.out.println(); + + s = "\n Robert\n Smith\n
\n 12345 Sixth Ave\n Anytown\n CA\n 98765-4321\n
\n
"; + j = XML.toJSONObject(s); + System.out.println(j.toString(4)); + + j = new JSONObject(obj); + System.out.println(j.toString()); + + s = "{ \"entity\": { \"imageURL\": \"\", \"name\": \"IXXXXXXXXXXXXX\", \"id\": 12336, \"ratingCount\": null, \"averageRating\": null } }"; + j = new JSONObject(s); + System.out.println(j.toString(2)); + + jj = new JSONStringer(); + s = jj + .object() + .key("single") + .value("MARIE HAA'S") + .key("Johnny") + .value("MARIE HAA\\'S") + .key("foo") + .value("bar") + .key("baz") + .array() + .object() + .key("quux") + .value("Thanks, Josh!") + .endObject() + .endArray() + .key("obj keys") + .value(JSONObject.getNames(obj)) + .endObject() + .toString(); + System.out.println(s); + + System.out.println(new JSONStringer() + .object() + .key("a") + .array() + .array() + .array() + .value("b") + .endArray() + .endArray() + .endArray() + .endObject() + .toString()); + + jj = new JSONStringer(); + jj.array(); + jj.value(1); + jj.array(); + jj.value(null); + jj.array(); + jj.object(); + jj.key("empty-array").array().endArray(); + jj.key("answer").value(42); + jj.key("null").value(null); + jj.key("false").value(false); + jj.key("true").value(true); + jj.key("big").value(123456789e+88); + jj.key("small").value(123456789e-88); + jj.key("empty-object").object().endObject(); + jj.key("long"); + jj.value(9223372036854775807L); + jj.endObject(); + jj.value("two"); + jj.endArray(); + jj.value(true); + jj.endArray(); + jj.value(98.6); + jj.value(-100.0); + jj.object(); + jj.endObject(); + jj.object(); + jj.key("one"); + jj.value(1.00); + jj.endObject(); + jj.value(obj); + jj.endArray(); + System.out.println(jj.toString()); + + System.out.println(new JSONArray(jj.toString()).toString(4)); + + int ar[] = {1, 2, 3}; + JSONArray ja = new JSONArray(ar); + System.out.println(ja.toString()); + + String sa[] = {"aString", "aNumber", "aBoolean"}; + j = new JSONObject(obj, sa); + j.put("Testing JSONString interface", obj); + System.out.println(j.toString(4)); + + j = new JSONObject("{slashes: '///', closetag: '', backslash:'\\\\', ei: {quotes: '\"\\''},eo: {a: '\"quoted\"', b:\"don't\"}, quotes: [\"'\", '\"']}"); + System.out.println(j.toString(2)); + System.out.println(XML.toString(j)); + System.out.println(""); + + j = new JSONObject( + "{foo: [true, false,9876543210, 0.0, 1.00000001, 1.000000000001, 1.00000000000000001," + + " .00000000000000001, 2.00, 0.1, 2e100, -32,[],{}, \"string\"], " + + " to : null, op : 'Good'," + + "ten:10} postfix comment"); + j.put("String", "98.6"); + j.put("JSONObject", new JSONObject()); + j.put("JSONArray", new JSONArray()); + j.put("int", 57); + j.put("double", 123456789012345678901234567890.); + j.put("true", true); + j.put("false", false); + j.put("null", JSONObject.NULL); + j.put("bool", "true"); + j.put("zero", -0.0); + j.put("\\u2028", "\u2028"); + j.put("\\u2029", "\u2029"); + a = j.getJSONArray("foo"); + a.put(666); + a.put(2001.99); + a.put("so \"fine\"."); + a.put("so ."); + a.put(true); + a.put(false); + a.put(new JSONArray()); + a.put(new JSONObject()); + j.put("keys", JSONObject.getNames(j)); + System.out.println(j.toString(4)); + System.out.println(XML.toString(j)); + + System.out.println("String: " + j.getDouble("String")); + System.out.println(" bool: " + j.getBoolean("bool")); + System.out.println(" to: " + j.getString("to")); + System.out.println(" true: " + j.getString("true")); + System.out.println(" foo: " + j.getJSONArray("foo")); + System.out.println(" op: " + j.getString("op")); + System.out.println(" ten: " + j.getInt("ten")); + System.out.println(" oops: " + j.optBoolean("oops")); + + s = "First \u0009<content> This is \"content\". 3 JSON does not preserve the sequencing of elements and contents. III T H R E EContent text is an implied structure in XML. JSON does not have implied structure:7everything is explicit.!]]>"; + j = XML.toJSONObject(s); + System.out.println(j.toString(2)); + System.out.println(XML.toString(j)); + System.out.println(""); + + ja = JSONML.toJSONArray(s); + System.out.println(ja.toString(4)); + System.out.println(JSONML.toString(ja)); + System.out.println(""); + + s = "unodostrestruequatrocinqoseis"; + ja = JSONML.toJSONArray(s); + System.out.println(ja.toString(4)); + System.out.println(JSONML.toString(ja)); + System.out.println(""); + + s = " "; + j = XML.toJSONObject(s); + + System.out.println(j.toString(2)); + System.out.println(XML.toString(j)); + System.out.println(""); + ja = JSONML.toJSONArray(s); + System.out.println(ja.toString(4)); + System.out.println(JSONML.toString(ja)); + System.out.println(""); + + j = XML.toJSONObject("Sample BookThis is chapter 1. It is not very long or interesting.This is chapter 2. Although it is longer than chapter 1, it is not any more interesting."); + System.out.println(j.toString(2)); + System.out.println(XML.toString(j)); + System.out.println(""); + + j = XML.toJSONObject(""); + System.out.println(j.toString(2)); + System.out.println(XML.toString(j)); + System.out.println(""); + + j = XML.toJSONObject(" Fred fbs0001 Scerbo B "); + System.out.println(j.toString(2)); + System.out.println(XML.toString(j)); + System.out.println(""); + + j = XML.toJSONObject("Repository Address Special Collections LibraryABC UniversityMain Library, 40 Circle DriveOurtown, Pennsylvania17654 USA"); + System.out.println(j.toString()); + System.out.println(XML.toString(j)); + System.out.println(""); + + j = XML.toJSONObject("deluxe&"toot"&toot;Aeksbonusbonus2"); + System.out.println(j.toString(2)); + System.out.println(XML.toString(j)); + System.out.println(""); + + j = HTTP.toJSONObject("GET / HTTP/1.0\nAccept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*\nAccept-Language: en-us\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; Win 9x 4.90; T312461; Q312461)\nHost: www.nokko.com\nConnection: keep-alive\nAccept-encoding: gzip, deflate\n"); + System.out.println(j.toString(2)); + System.out.println(HTTP.toString(j)); + System.out.println(""); + + j = HTTP.toJSONObject("HTTP/1.1 200 Oki Doki\nDate: Sun, 26 May 2002 17:38:52 GMT\nServer: Apache/1.3.23 (Unix) mod_perl/1.26\nKeep-Alive: timeout=15, max=100\nConnection: Keep-Alive\nTransfer-Encoding: chunked\nContent-Type: text/html\n"); + System.out.println(j.toString(2)); + System.out.println(HTTP.toString(j)); + System.out.println(""); + + j = new JSONObject("{nix: null, nux: false, null: 'null', 'Request-URI': '/', Method: 'GET', 'HTTP-Version': 'HTTP/1.0'}"); + System.out.println(j.toString(2)); + System.out.println("isNull: " + j.isNull("nix")); + System.out.println(" has: " + j.has("nix")); + System.out.println(XML.toString(j)); + System.out.println(HTTP.toString(j)); + System.out.println(""); + + j = XML.toJSONObject(""+"\n\n"+""+ + ""+ + "GOOGLEKEY '+search+' 0 10 true false latin1 latin1"+ + ""+ + ""); + System.out.println(j.toString(2)); + System.out.println(XML.toString(j)); + System.out.println(""); + + j = new JSONObject("{Envelope: {Body: {\"ns1:doGoogleSearch\": {oe: \"latin1\", filter: true, q: \"'+search+'\", key: \"GOOGLEKEY\", maxResults: 10, \"SOAP-ENV:encodingStyle\": \"http://schemas.xmlsoap.org/soap/encoding/\", start: 0, ie: \"latin1\", safeSearch:false, \"xmlns:ns1\": \"urn:GoogleSearch\"}}}}"); + System.out.println(j.toString(2)); + System.out.println(XML.toString(j)); + System.out.println(""); + + j = CookieList.toJSONObject(" f%oo = b+l=ah ; o;n%40e = t.wo "); + System.out.println(j.toString(2)); + System.out.println(CookieList.toString(j)); + System.out.println(""); + + j = Cookie.toJSONObject("f%oo=blah; secure ;expires = April 24, 2002"); + System.out.println(j.toString(2)); + System.out.println(Cookie.toString(j)); + System.out.println(""); + + j = new JSONObject("{script: 'It is not allowed in HTML to send a close script tag in a stringso we insert a backslash before the /'}"); + System.out.println(j.toString()); + System.out.println(""); + + JSONTokener jt = new JSONTokener("{op:'test', to:'session', pre:1}{op:'test', to:'session', pre:2}"); + j = new JSONObject(jt); + System.out.println(j.toString()); + System.out.println("pre: " + j.optInt("pre")); + int i = jt.skipTo('{'); + System.out.println(i); + j = new JSONObject(jt); + System.out.println(j.toString()); + System.out.println(""); + + a = CDL.toJSONArray("Comma delimited list test, '\"Strip\"Quotes', 'quote, comma', No quotes, 'Single Quotes', \"Double Quotes\"\n1,'2',\"3\"\n,'It is \"good,\"', \"It works.\"\n\n"); + + s = CDL.toString(a); + System.out.println(s); + System.out.println(""); + System.out.println(a.toString(4)); + System.out.println(""); + a = CDL.toJSONArray(s); + System.out.println(a.toString(4)); + System.out.println(""); + + a = new JSONArray(" [\"\", next is an implied null , , ok,] "); + System.out.println(a.toString()); + System.out.println(""); + System.out.println(XML.toString(a)); + System.out.println(""); + + j = new JSONObject("{ fun => with non-standard forms ; forgiving => This package can be used to parse formats that are similar to but not stricting conforming to JSON; why=To make it easier to migrate existing data to JSON,one = [[1.00]]; uno=[[{1=>1}]];'+':+6e66 ;pluses=+++;empty = '' , 'double':0.666,true: TRUE, false: FALSE, null=NULL;[true] = [[!,@;*]]; string=> o. k. ; \r oct=0666; hex=0x666; dec=666; o=0999; noh=0x0x}"); + System.out.println(j.toString(4)); + System.out.println(""); + if (j.getBoolean("true") && !j.getBoolean("false")) { + System.out.println("It's all good"); + } + + System.out.println(""); + j = new JSONObject(j, new String[]{"dec", "oct", "hex", "missing"}); + System.out.println(j.toString(4)); + + System.out.println(""); + System.out.println(new JSONStringer().array().value(a).value(j).endArray()); + + j = new JSONObject("{string: \"98.6\", long: 2147483648, int: 2147483647, longer: 9223372036854775807, double: 9223372036854775808}"); + System.out.println(j.toString(4)); + + System.out.println("\ngetInt"); + System.out.println("int " + j.getInt("int")); + System.out.println("long " + j.getInt("long")); + System.out.println("longer " + j.getInt("longer")); + //System.out.println("double " + j.getInt("double")); + //System.out.println("string " + j.getInt("string")); + + System.out.println("\ngetLong"); + System.out.println("int " + j.getLong("int")); + System.out.println("long " + j.getLong("long")); + System.out.println("longer " + j.getLong("longer")); + //System.out.println("double " + j.getLong("double")); + //System.out.println("string " + j.getLong("string")); + + System.out.println("\ngetDouble"); + System.out.println("int " + j.getDouble("int")); + System.out.println("long " + j.getDouble("long")); + System.out.println("longer " + j.getDouble("longer")); + System.out.println("double " + j.getDouble("double")); + System.out.println("string " + j.getDouble("string")); + + j.put("good sized", 9223372036854775807L); + System.out.println(j.toString(4)); + + a = new JSONArray("[2147483647, 2147483648, 9223372036854775807, 9223372036854775808]"); + System.out.println(a.toString(4)); + + System.out.println("\nKeys: "); + it = j.keys(); + while (it.hasNext()) { + s = (String)it.next(); + System.out.println(s + ": " + j.getString(s)); + } + + + System.out.println("\naccumulate: "); + j = new JSONObject(); + j.accumulate("stooge", "Curly"); + j.accumulate("stooge", "Larry"); + j.accumulate("stooge", "Moe"); + a = j.getJSONArray("stooge"); + a.put(5, "Shemp"); + System.out.println(j.toString(4)); + + System.out.println("\nwrite:"); + System.out.println(j.write(new StringWriter())); + + s = "122333"; + j = XML.toJSONObject(s); + System.out.println(j.toString(4)); + System.out.println(XML.toString(j)); + + s = "Content of the first chapterContent of the second chapter Content of the first subchapter Content of the second subchapterThird Chapter"; + j = XML.toJSONObject(s); + System.out.println(j.toString(4)); + System.out.println(XML.toString(j)); + + a = JSONML.toJSONArray(s); + System.out.println(a.toString(4)); + System.out.println(JSONML.toString(a)); + + Collection c = null; + Map m = null; + + j = new JSONObject(m); + a = new JSONArray(c); + j.append("stooge", "Joe DeRita"); + j.append("stooge", "Shemp"); + j.accumulate("stooges", "Curly"); + j.accumulate("stooges", "Larry"); + j.accumulate("stooges", "Moe"); + j.accumulate("stoogearray", j.get("stooges")); + j.put("map", m); + j.put("collection", c); + j.put("array", a); + a.put(m); + a.put(c); + System.out.println(j.toString(4)); + + s = "{plist=Apple; AnimalSmells = { pig = piggish; lamb = lambish; worm = wormy; }; AnimalSounds = { pig = oink; lamb = baa; worm = baa; Lisa = \"Why is the worm talking like a lamb?\" } ; AnimalColors = { pig = pink; lamb = black; worm = pink; } } "; + j = new JSONObject(s); + System.out.println(j.toString(4)); + + s = " (\"San Francisco\", \"New York\", \"Seoul\", \"London\", \"Seattle\", \"Shanghai\")"; + a = new JSONArray(s); + System.out.println(a.toString()); + + s = "The content of b and The content of cdoremi"; + j = XML.toJSONObject(s); + + System.out.println(j.toString(2)); + System.out.println(XML.toString(j)); + System.out.println(""); + ja = JSONML.toJSONArray(s); + System.out.println(ja.toString(4)); + System.out.println(JSONML.toString(ja)); + System.out.println(""); + + s = "111111111111111"; + j = JSONML.toJSONObject(s); + System.out.println(j); + ja = JSONML.toJSONArray(s); + System.out.println(ja); + + + System.out.println("\nTesting Exceptions: "); + + System.out.print("Exception: "); + try { + a = new JSONArray("[\n\r\n\r}"); + System.out.println(a.toString()); + } catch (Exception e) { + System.out.println(e); + } + + System.out.print("Exception: "); + try { + a = new JSONArray("<\n\r\n\r "); + System.out.println(a.toString()); + } catch (Exception e) { + System.out.println(e); + } + + System.out.print("Exception: "); + try { + a = new JSONArray(); + a.put(Double.NEGATIVE_INFINITY); + a.put(Double.NaN); + System.out.println(a.toString()); + } catch (Exception e) { + System.out.println(e); + } + System.out.print("Exception: "); + try { + System.out.println(j.getDouble("stooge")); + } catch (Exception e) { + System.out.println(e); + } + System.out.print("Exception: "); + try { + System.out.println(j.getDouble("howard")); + } catch (Exception e) { + System.out.println(e); + } + System.out.print("Exception: "); + try { + System.out.println(j.put(null, "howard")); + } catch (Exception e) { + System.out.println(e); + } + System.out.print("Exception: "); + try { + System.out.println(a.getDouble(0)); + } catch (Exception e) { + System.out.println(e); + } + System.out.print("Exception: "); + try { + System.out.println(a.get(-1)); + } catch (Exception e) { + System.out.println(e); + } + System.out.print("Exception: "); + try { + System.out.println(a.put(Double.NaN)); + } catch (Exception e) { + System.out.println(e); + } + System.out.print("Exception: "); + try { + j = XML.toJSONObject(" "); + } catch (Exception e) { + System.out.println(e); + } + System.out.print("Exception: "); + try { + j = XML.toJSONObject(" "); + } catch (Exception e) { + System.out.println(e); + } + System.out.print("Exception: "); + try { + j = XML.toJSONObject(", Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.test; + +import java.io.ByteArrayInputStream; +import org.gephi.streaming.api.event.GraphEvent; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.LinkedList; +import org.gephi.filters.spi.EdgeFilter; +import org.gephi.filters.spi.NodeFilter; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphController; +import org.gephi.graph.api.GraphModel; +import org.gephi.graph.api.Node; +import org.gephi.project.api.Project; +import org.gephi.project.api.ProjectController; +import org.gephi.project.api.Workspace; +import org.gephi.streaming.api.GraphEventHandler; +import org.gephi.streaming.api.GraphUpdaterEventHandler; +import org.gephi.streaming.api.event.GraphEventBuilder; +import org.gephi.streaming.api.StreamReader; +import org.gephi.streaming.api.StreamReaderFactory; +import org.gephi.streaming.api.event.ElementEvent; +import org.gephi.streaming.api.event.ElementType; +import org.gephi.streaming.api.event.EventType; +import org.gephi.streaming.api.event.FilterEvent; +import org.junit.Test; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; + +/** + * Test cases for the JSON Graph Streaming format. + * + * @author panisson + * + */ +public class JSONStreamReaderTest { + + protected String resource = "amazon.json"; + protected String streamType = "JSON"; + + @Test + public void testStreamReaderFactory() throws IOException { + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + StreamReader processor = factory.createStreamReader(streamType, new MockGraphEventHandler(), eventBuilder); + assertNotNull(processor); + } + + @Test + public void testStreamReader() throws IOException { + + URL url = this.getClass().getResource(resource); + url.openConnection(); + InputStream inputStream = url.openStream(); + + MockGraphEventHandler handler = new MockGraphEventHandler(); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + StreamReader streamReader = factory.createStreamReader(streamType, handler, eventBuilder); + + streamReader.processStream(inputStream); +// assertEquals(2422, count.get()); + assertTrue(handler.getEventCount()>=1405); +// assertEquals(1405, handler.getEventCount()); +// System.out.println(count.get() + " Events"); + } + + @Test + public void testReadEvents() throws IOException { + HeapEventHandler handler = new HeapEventHandler(); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + final StreamReader streamReader = factory.createStreamReader(streamType, handler, eventBuilder); + + final StringBufferedInputStream inputStream = new StringBufferedInputStream(); + + Thread t = new Thread() { + @Override + public void run() { + try { + streamReader.processStream(inputStream); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + }; + t.start(); + + GraphEvent event; + + inputStream.offer("{\"an\":{\"A\":{\"label\":\"Streaming Node A\",\"size\":2}}}\n\r"); + event = handler.getGraphEvent(); + assertNotNull(event); + assertEquals(EventType.ADD, event.getEventType()); + assertEquals(ElementType.NODE, event.getElementType()); + assertEquals(ElementEvent.class, event.getClass()); + assertEquals("A", ((ElementEvent)event).getElementId()); + + inputStream.offer("{\"ae\":{\"AB\":{\"source\":\"A\",\"target\":\"B\",\"directed\":false,\"label\":\"Edge AB\",\"size\":2}}}\n\r"); + event = handler.getGraphEvent(); + assertNotNull(event); + assertEquals(EventType.ADD, event.getEventType()); + assertEquals(ElementType.EDGE, event.getElementType()); + assertTrue(event instanceof ElementEvent); + assertEquals("AB", ((ElementEvent)event).getElementId()); + + inputStream.offer("{\"cn\":{\"A\":{\"label\":\"Streaming Node A\",\"size\":2}}}\n\r"); + event = handler.getGraphEvent(); + assertNotNull(event); + assertEquals(EventType.CHANGE, event.getEventType()); + assertEquals(ElementType.NODE, event.getElementType()); + assertTrue(event instanceof ElementEvent); + assertEquals("A", ((ElementEvent)event).getElementId()); + + inputStream.offer("{\"ce\":{\"AB\":{\"size\":2}}}\n\r"); + event = handler.getGraphEvent(); + assertNotNull(event); + assertEquals(EventType.CHANGE, event.getEventType()); + assertEquals(ElementType.EDGE, event.getElementType()); + assertTrue(event instanceof ElementEvent); + assertEquals("AB", ((ElementEvent)event).getElementId()); + + inputStream.offer("{\"dn\":{\"A\":{}}}\n\r"); + event = handler.getGraphEvent(); + assertNotNull(event); + assertEquals(EventType.REMOVE, event.getEventType()); + assertEquals(ElementType.NODE, event.getElementType()); + assertTrue(event instanceof ElementEvent); + assertEquals("A", ((ElementEvent)event).getElementId()); + + inputStream.offer("{\"de\":{\"AB\":{}}}\n\r"); + event = handler.getGraphEvent(); + assertNotNull(event); + assertEquals(EventType.REMOVE, event.getEventType()); + assertEquals(ElementType.EDGE, event.getElementType()); + assertTrue(event instanceof ElementEvent); + assertEquals("AB", ((ElementEvent)event).getElementId()); + + inputStream.offer("{\"cg\":{\"AB\":{}}}\r"); + event = handler.getGraphEvent(); + assertNotNull(event); + assertEquals(EventType.CHANGE, event.getEventType()); + assertEquals(ElementType.GRAPH, event.getElementType()); + + inputStream.offer("{\"dn\":{\"filter\":\"ALL\"}}\r"); + event = handler.getGraphEvent(); + assertNotNull(event); + assertEquals(EventType.REMOVE, event.getEventType()); + assertEquals(ElementType.NODE, event.getElementType()); + assertTrue(event instanceof FilterEvent); + FilterEvent filterEvent = (FilterEvent)event; + assertTrue(filterEvent.getFilter() instanceof NodeFilter); + NodeFilter nodeFilter = (NodeFilter)filterEvent.getFilter(); + assertTrue(nodeFilter.evaluate(null, null)); + + inputStream.offer("{\"de\":{\"filter\":\"ALL\"}}\r"); + event = handler.getGraphEvent(); + assertNotNull(event); + assertEquals(EventType.REMOVE, event.getEventType()); + assertEquals(ElementType.EDGE, event.getElementType()); + assertTrue(event instanceof FilterEvent); + assertTrue(((FilterEvent)event).getFilter() instanceof EdgeFilter); + EdgeFilter edgeFilter = (EdgeFilter)((FilterEvent)event).getFilter(); + assertTrue(edgeFilter.evaluate(null, null)); + + inputStream.offer("{\"dn\":{\"filter\":{\"NodeAttribute\":{\"attribute\":\"id\",\"value\":\"A\"}}}}\r"); + event = handler.getGraphEvent(); + assertNotNull(event); + assertEquals(EventType.REMOVE, event.getEventType()); + assertEquals(ElementType.NODE, event.getElementType()); + assertTrue(event instanceof FilterEvent); + filterEvent = (FilterEvent)event; + assertTrue(filterEvent.getFilter() instanceof NodeFilter); + nodeFilter = (NodeFilter)filterEvent.getFilter(); + + // Incomplete event + inputStream.offer("{\"de\":{\"filter\":\"ALL\"}\r"); + event = handler.getGraphEvent(); + assertNull(event); + + // Verify if EOF ends the Thread + inputStream.offer("$"); + try { + t.join(1000); + } catch (InterruptedException ex) { } + assertTrue(t.getState() == t.getState().TERMINATED); + } + + @Test + public void testChangeNodeAttribute() throws IOException { + // Get active graph instance - Project and Graph API + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + Project project = projectController.getCurrentProject(); + if (project==null) + projectController.newProject(); + Workspace workspace = projectController.getCurrentWorkspace(); + if (workspace==null) + workspace = projectController.newWorkspace(projectController.getCurrentProject()); + + GraphController graphController = Lookup.getDefault().lookup(GraphController.class); + GraphModel graphModel = graphController.getModel(); + Graph graph = graphModel.getHierarchicalMixedGraph(); + + GraphEventHandler handler = new GraphUpdaterEventHandler(graph); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + StreamReader streamReader = factory.createStreamReader(streamType, handler, eventBuilder); + + String evstr = "{\"an\":{\"A\":{\"label\":\"Streaming Node A\",\"size\":2}}}\n\r"; + evstr += "{\"cn\":{\"A\":{\"label\":\"Streaming Node A changed\",\"size\":3, \"key\":\"value\"}}}\n\r"; + ByteArrayInputStream bais = new ByteArrayInputStream(evstr.getBytes()); + + streamReader.processStream(bais); + try { + Thread.sleep(50); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } + + Node a = graph.getNode("A"); + + assertEquals(3.0, a.getNodeData().getSize(), 0.0); + assertEquals(a.getNodeData().getAttributes().getValue("key"), "value"); + } + + @Test + public void testChangeGraphAttribute() throws IOException { + // Get active graph instance - Project and Graph API + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + Project project = projectController.getCurrentProject(); + if (project==null) + projectController.newProject(); + Workspace workspace = projectController.getCurrentWorkspace(); + if (workspace==null) + workspace = projectController.newWorkspace(projectController.getCurrentProject()); + + GraphController graphController = Lookup.getDefault().lookup(GraphController.class); + GraphModel graphModel = graphController.getModel(); + Graph graph = graphModel.getHierarchicalMixedGraph(); + + GraphEventHandler handler = new GraphUpdaterEventHandler(graph); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + StreamReader streamReader = factory.createStreamReader(streamType, handler, eventBuilder); + + String evstr = "{\"cg\":{\"label\":\"Graph Label\"}}\n\r"; + ByteArrayInputStream bais = new ByteArrayInputStream(evstr.getBytes()); + + streamReader.processStream(bais); + try { + Thread.sleep(50); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } + + assertEquals("Graph Label", graph.getAttributes().getValue("label")); + + } + + @Test + public void testChangeEdgeAttribute() throws IOException { + // Get active graph instance - Project and Graph API + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + Project project = projectController.getCurrentProject(); + if (project==null) + projectController.newProject(); + Workspace workspace = projectController.getCurrentWorkspace(); + if (workspace==null) + workspace = projectController.newWorkspace(projectController.getCurrentProject()); + + GraphController graphController = Lookup.getDefault().lookup(GraphController.class); + GraphModel graphModel = graphController.getModel(); + Graph graph = graphModel.getHierarchicalMixedGraph(); + + GraphEventHandler handler = new GraphUpdaterEventHandler(graph); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + StreamReader streamReader = factory.createStreamReader(streamType, handler, eventBuilder); + + String evstr = "{\"an\":{\"A\":{\"label\":\"Streaming Node A\",\"size\":2}}}\n\r"; + evstr += "{\"an\":{\"B\":{\"label\":\"Streaming Node B\",\"size\":2}}}\n\r"; + evstr += "{\"ae\":{\"AB\":{\"source\":\"A\",\"target\":\"B\",\"directed\":false,\"label\":\"Edge AB\",\"size\":2}}}\n\r"; + evstr += "{\"ce\":{\"AB\":{\"label\":\"Edge AB changed\",\"size\":3, \"key\":\"value\"}}}\n\r"; + ByteArrayInputStream bais = new ByteArrayInputStream(evstr.getBytes()); + + streamReader.processStream(bais); + try { + Thread.sleep(50); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } + + Edge ab = graph.getEdge("AB"); + + assertEquals(3.0, ab.getEdgeData().getSize(), 0.0); + assertEquals(ab.getEdgeData().getAttributes().getValue("key"), "value"); + + } + + @Test + public void testIgnoreInvalidAttrType() throws IOException { + HeapEventHandler handler = new HeapEventHandler(); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + StreamReader streamReader = factory.createStreamReader(streamType, handler, eventBuilder); + + String evstr = "{\"ivalid\":0,\"an\":{\"A\":{\"label\":\"Streaming Node A\"}}}\n\r"; + ByteArrayInputStream bais = new ByteArrayInputStream(evstr.getBytes()); + + boolean raisedException = false; + try { + streamReader.processStream(bais); + } catch (IllegalArgumentException e) { + raisedException = true; + } + assertTrue(raisedException); + +// streamReader.processStream(bais); +// GraphEvent event = handler.getGraphEvent(); +// assertNotNull(event); +// assertEquals(EventType.ADD, event.getEventType()); +// assertEquals(ElementType.NODE, event.getElementType()); +// assertEquals(ElementEvent.class, event.getClass()); +// assertEquals("A", ((ElementEvent)event).getElementId()); + } + + @Test + public void testIgnoreInvalidAttrName() throws IOException { + HeapEventHandler handler = new HeapEventHandler(); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + StreamReader streamReader = factory.createStreamReader(streamType, handler, eventBuilder); + + String evstr = "{\"ivalid\":{\"A\":{\"label\":\"Streaming Node A\"}},\"an\":{\"A\":{\"label\":\"Streaming Node A\"}}}\n\r"; + ByteArrayInputStream bais = new ByteArrayInputStream(evstr.getBytes()); + + boolean raisedException = false; + try { + streamReader.processStream(bais); + } catch (IllegalArgumentException e) { + raisedException = true; + } + assertTrue(raisedException); + } + + @Test + public void testReadEventId() throws IOException { + HeapEventHandler handler = new HeapEventHandler(); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + StreamReader streamReader = factory.createStreamReader(streamType, handler, eventBuilder); + + String evstr = "{\"id\":0,\"an\":{\"A\":{\"label\":\"Streaming Node A\"}}}\n\r"; + ByteArrayInputStream bais = new ByteArrayInputStream(evstr.getBytes()); + + streamReader.processStream(bais); + GraphEvent event = handler.getGraphEvent(); + assertNotNull(event); + assertEquals(EventType.ADD, event.getEventType()); + assertEquals(ElementType.NODE, event.getElementType()); + assertEquals(ElementEvent.class, event.getClass()); + assertEquals("A", ((ElementEvent)event).getElementId()); + } + + @Test + public void testReadEventTimestamp() throws IOException { + HeapEventHandler handler = new HeapEventHandler(); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + StreamReader streamReader = factory.createStreamReader(streamType, handler, eventBuilder); + + String evstr = "{\"t\":999,\"an\":{\"A\":{\"label\":\"Streaming Node A\"}}}\n\r"; + ByteArrayInputStream bais = new ByteArrayInputStream(evstr.getBytes()); + + streamReader.processStream(bais); + GraphEvent event = handler.getGraphEvent(); + assertNotNull(event); + assertEquals(Double.valueOf(999.), event.getTimestamp()); + } + + @Test + public void testUnicodeEvent() throws IOException { + HeapEventHandler handler = new HeapEventHandler(); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + StreamReader streamReader = factory.createStreamReader(streamType, handler, eventBuilder); + + String evstr = "{\"an\":{\"Unicode1\":{\"label\":\"Съешь же ещё Ñтих мÑгких французÑких булок да выпей чаю.\"}}}\n\r"; + ByteArrayInputStream bais = new ByteArrayInputStream(evstr.getBytes()); + + streamReader.processStream(bais); + GraphEvent event = handler.getGraphEvent(); + assertNotNull(event); + assertEquals(EventType.ADD, event.getEventType()); + assertEquals(ElementType.NODE, event.getElementType()); + assertEquals(ElementEvent.class, event.getClass()); + assertEquals("Unicode1", ((ElementEvent)event).getElementId()); + //System.out.println(((ElementEvent)event).getAttributes().get("label")); + assertEquals("Съешь же ещё Ñтих мÑгких французÑких булок да выпей чаю.", + ((ElementEvent)event).getAttributes().get("label")); + } + + @Test + public void testEscapedUnicodeEvent() throws IOException { + HeapEventHandler handler = new HeapEventHandler(); + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + StreamReader streamReader = factory.createStreamReader(streamType, handler, eventBuilder); + + String evstr = "{\"an\":{\"Unicode2\":{\"label\":\"\\u0421\\u044a\\u0435\\u0448\\u044c \\u0436\\u0435 \\u0435\\u0449\\u0451 \\u044d\\u0442\\u0438\\u0445 \\u043c\\u044f\\u0433\\u043a\\u0438\\u0445 \\u0444\\u0440\\u0430\\u043d\\u0446\\u0443\\u0437\\u0441\\u043a\\u0438\\u0445 \\u0431\\u0443\\u043b\\u043e\\u043a \\u0434\\u0430 \\u0432\\u044b\\u043f\\u0435\\u0439 \\u0447\\u0430\\u044e.\"}}}\n\r"; + ByteArrayInputStream bais = new ByteArrayInputStream(evstr.getBytes()); + + streamReader.processStream(bais); + GraphEvent event = handler.getGraphEvent(); + assertNotNull(event); + assertEquals(EventType.ADD, event.getEventType()); + assertEquals(ElementType.NODE, event.getElementType()); + assertEquals(ElementEvent.class, event.getClass()); + assertEquals("Unicode2", ((ElementEvent)event).getElementId()); + //System.out.println(((ElementEvent)event).getAttributes().get("label")); + assertEquals("Съешь же ещё Ñтих мÑгких французÑких булок да выпей чаю.", + ((ElementEvent)event).getAttributes().get("label")); + } + + private class HeapEventHandler implements GraphEventHandler { + + private LinkedList eventHeap = new LinkedList(); + + @Override + public void handleGraphEvent(GraphEvent event) { + eventHeap.offer(event); + } + + public GraphEvent getGraphEvent() { + if (eventHeap.isEmpty()) { + return null; + } else { + return eventHeap.pop(); + } + } + + } + + private class StringBufferedInputStream extends InputStream { + private final StringBuffer buffer = new StringBuffer(); + + @Override + public int read(byte b[], int off, int len) throws IOException { + if (b == null) { + throw new NullPointerException(); + } else if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + + int c = read(); + if (c == -1) { + return -1; + } + b[off] = (byte)c; + return 1; + } + + @Override + public int read() throws IOException { + int read = 0; + while (read == 0) { + if(buffer.length() > 0) { + read = buffer.charAt(0); + buffer.deleteCharAt(0); + if (read=='$') { + synchronized(buffer) { + buffer.notifyAll(); + } + return -1; + } + return read; + } else { + try { + synchronized(buffer) { + buffer.notifyAll(); + buffer.wait(); + } + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } + } + } + return read; + } + + public void offer(String str) { + buffer.append(str); + synchronized(buffer) { + buffer.notifyAll(); + try { + buffer.wait(); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } + } + } + + } +} diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONStreamWriterTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONStreamWriterTest.java new file mode 100644 index 0000000000..7bff9acd9a --- /dev/null +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONStreamWriterTest.java @@ -0,0 +1,329 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.gephi.data.attributes.api.AttributeController; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphController; +import org.gephi.graph.api.GraphModel; +import org.gephi.project.api.Project; +import org.gephi.project.api.ProjectController; +import org.gephi.project.api.Workspace; +import org.gephi.streaming.api.Graph2EventListener; +import org.gephi.streaming.api.event.GraphEventBuilder; +import org.gephi.streaming.api.StreamReader; +import org.gephi.streaming.api.StreamReaderFactory; +import org.gephi.streaming.api.StreamWriter; +import org.gephi.streaming.api.StreamWriterFactory; +import org.gephi.streaming.api.event.ElementType; +import org.gephi.streaming.api.event.EventType; +import org.gephi.streaming.api.event.GraphEvent; +import org.gephi.streaming.impl.json.parser.JSONConstants; +import org.gephi.streaming.impl.json.parser.JSONException; +import org.gephi.streaming.impl.json.parser.JSONObject; +import org.junit.Test; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; + +/** + * Test cases for the JSON Graph Streaming format. + * + * @author panisson + * + */ +public class JSONStreamWriterTest { + + protected String resource = "amazon.json"; + protected String streamType = "JSON"; + + @Test + public void testStreamWriterFactory() throws IOException { + + StreamWriterFactory factory = Lookup.getDefault().lookup(StreamWriterFactory.class); + StreamWriter processor = factory.createStreamWriter(streamType, new ByteArrayOutputStream()); + assertNotNull(processor); + } + + private String writeEvent(GraphEvent event) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamWriterFactory factory = Lookup.getDefault().lookup(StreamWriterFactory.class); + StreamWriter streamWriter = factory.createStreamWriter(streamType, out); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + + streamWriter.startStream(); + streamWriter.handleGraphEvent(event); + streamWriter.endStream(); + + return new String(out.toByteArray()); + } + + @Test + public void testWriteSimpleEvent() { + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + Map attributes = new HashMap(); + attributes.put("size", 2); + GraphEvent event = eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "A", attributes); + String result = writeEvent(event); + + try { + JSONObject jo = new JSONObject(result); + assertTrue(jo.has(JSONConstants.Types.AN.value())); + Object o = jo.get(JSONConstants.Types.AN.value()); + assertTrue(o instanceof JSONObject); + jo = (JSONObject)o; + assertTrue(jo.has("A")); + o = jo.get("A"); + assertTrue(o instanceof JSONObject); + jo = (JSONObject)o; + assertTrue(jo.has("size")); + o = jo.get("size"); + assertTrue(o.equals(2)); + + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + + @Test + public void testWriteAN() { + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + String result = writeEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "A", null)); + + try { + JSONObject jo = new JSONObject(result); + assertTrue(jo.has(JSONConstants.Types.AN.value())); + Object o = jo.get(JSONConstants.Types.AN.value()); + assertTrue(o instanceof JSONObject); + jo = (JSONObject)o; + assertTrue(jo.has("A")); + o = jo.get("A"); + assertTrue(o instanceof JSONObject); + + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + + @Test + public void testWriteCN() { + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + String result = writeEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, "A", null)); + + try { + JSONObject jo = new JSONObject(result); + assertTrue(jo.has(JSONConstants.Types.CN.value())); + Object o = jo.get(JSONConstants.Types.CN.value()); + assertTrue(o instanceof JSONObject); + jo = (JSONObject)o; + assertTrue(jo.has("A")); + o = jo.get("A"); + assertTrue(o instanceof JSONObject); + + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + + @Test + public void testWriteDN() { + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + String result = writeEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.REMOVE, "A", null)); + + try { + JSONObject jo = new JSONObject(result); + assertTrue(jo.has(JSONConstants.Types.DN.value())); + Object o = jo.get(JSONConstants.Types.DN.value()); + assertTrue(o instanceof JSONObject); + jo = (JSONObject)o; + assertTrue(jo.has("A")); + o = jo.get("A"); + assertTrue(o instanceof JSONObject); + + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + + @Test + public void testStreamWriter() throws IOException { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamWriterFactory factory = Lookup.getDefault().lookup(StreamWriterFactory.class); + StreamWriter streamWriter = factory.createStreamWriter(streamType, out); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + + // write triangle + streamWriter.startStream(); + streamWriter.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "A", null)); + streamWriter.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "B", null)); + streamWriter.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "C", null)); + streamWriter.handleGraphEvent(eventBuilder.edgeAddedEvent("AB", "A", "B", false, null)); + streamWriter.handleGraphEvent(eventBuilder.edgeAddedEvent("BC", "B", "C", false, null)); + streamWriter.handleGraphEvent(eventBuilder.edgeAddedEvent("CA", "C", "A", false, null)); + streamWriter.endStream(); + + } + + @Test + public void testWriteEvents() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamWriterFactory factory = Lookup.getDefault().lookup(StreamWriterFactory.class); + StreamWriter streamWriter = factory.createStreamWriter(streamType, out); + + streamWriter.startStream(); + for (GraphEvent event: new EventFactory().getGraphEvents()) { + streamWriter.handleGraphEvent(event); + } + streamWriter.endStream(); + } + + @Test + public void testWriteEventTimestamp() { + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + GraphEvent event = eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "A", null); + event.setTimestamp(999.87); + String result = writeEvent(event); + + try { + JSONObject jo = new JSONObject(result); + assertTrue(jo.has(JSONConstants.Fields.T.value())); + Object t = jo.get(JSONConstants.Fields.T.value()); + assertTrue(t.equals(999.87)); + + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + + @Test + public void testCheckNullTimestamp() { + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + GraphEvent event = eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "A", null); + String result = writeEvent(event); + + try { + JSONObject jo = new JSONObject(result); + assertTrue(!jo.has(JSONConstants.Fields.T.value())); + + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + + private class EventFactory { + private List events = new ArrayList(); + + public EventFactory() { + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + Map attributes = new HashMap(); + attributes.put("size", 2); + GraphEvent event; + event = eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "A", attributes); + events.add(event); + event = eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "B", attributes); + events.add(event); + event = eventBuilder.edgeAddedEvent("AB", "A", "B", false, attributes); + events.add(event); + event = eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, "A", attributes); + events.add(event); + event = eventBuilder.graphEvent(ElementType.NODE, EventType.REMOVE, "A", attributes); + events.add(event); + event = eventBuilder.graphEvent(ElementType.EDGE, EventType.CHANGE, "AB", attributes); + events.add(event); + event = eventBuilder.graphEvent(ElementType.EDGE, EventType.REMOVE, "AB", attributes); + events.add(event); + event = eventBuilder.graphEvent(ElementType.GRAPH, EventType.CHANGE, null, attributes); + events.add(event); + } + + public List getGraphEvents() { + return events; + } + } + + @Test + public void testChangeGraphAttribute() throws IOException { + // Get active graph instance - Project and Graph API + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + Project project = projectController.getCurrentProject(); + if (project==null) + projectController.newProject(); + Workspace workspace = projectController.getCurrentWorkspace(); + if (workspace==null) + workspace = projectController.newWorkspace(projectController.getCurrentProject()); + + GraphController graphController = Lookup.getDefault().lookup(GraphController.class); + GraphModel graphModel = graphController.getModel(); + Graph graph = graphModel.getHierarchicalMixedGraph(); + + AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); + ac.getModel(); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamWriterFactory factory = Lookup.getDefault().lookup(StreamWriterFactory.class); + StreamWriter streamWriter = factory.createStreamWriter(streamType, out); + + Graph2EventListener listener = new Graph2EventListener(graph, streamWriter); + graphModel.addGraphListener(listener); + ac.getModel().addAttributeListener(listener); + + graph.getAttributes().setValue("Label", "Graph Label"); + + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } + + assertEquals("{\"cg\":{\"Label\":\"Graph Label\"}}\r\n", new String(out.toByteArray())); + } + +} diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JsonStreamProcessorTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JsonStreamProcessorTest.java new file mode 100644 index 0000000000..2896211bae --- /dev/null +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JsonStreamProcessorTest.java @@ -0,0 +1,53 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.test; + +public class JsonStreamProcessorTest extends AbstractStreamProcessorTest { + + private static final String JSON_RESOURCE = "amazon.json"; + + public JsonStreamProcessorTest() { + this.streamType = "JSON"; + this.resource = JSON_RESOURCE; + } + +} diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/ListenersTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/ListenersTest.java new file mode 100644 index 0000000000..2d1d1b3397 --- /dev/null +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/ListenersTest.java @@ -0,0 +1,111 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ + +package org.gephi.streaming.test; + +import org.gephi.data.attributes.api.AttributeController; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphController; +import org.gephi.graph.api.GraphModel; +import org.gephi.project.api.Project; +import org.gephi.project.api.ProjectController; +import org.gephi.project.api.Workspace; +import org.gephi.streaming.api.Graph2EventListener; +import org.gephi.streaming.api.GraphEventHandler; +import org.gephi.streaming.api.GraphUpdaterEventHandler; +import org.gephi.streaming.api.event.ElementType; +import org.gephi.streaming.api.event.EventType; +import org.gephi.streaming.api.event.GraphEventBuilder; +import org.junit.Test; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; + +/** + * + * @author panisson + */ +public class ListenersTest { + + @Test + public void runTest() { + // Get active graph instance - Project and Graph API + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + Project project = projectController.getCurrentProject(); + if (project==null) + projectController.newProject(); + Workspace workspace = projectController.getCurrentWorkspace(); + if (workspace==null) + workspace = projectController.newWorkspace(projectController.getCurrentProject()); +// projectController.openWorkspace(workspace); + + GraphController graphController = Lookup.getDefault().lookup(GraphController.class); + GraphModel graphModel = graphController.getModel(); + Graph graph = graphModel.getHierarchicalMixedGraph(); + + GraphEventHandler printerHandler = new GraphEventHandler() { + + @Override + public void handleGraphEvent(org.gephi.streaming.api.event.GraphEvent event) { + System.out.println(event); + } + }; + + Graph2EventListener listener = new Graph2EventListener(graph, printerHandler); + graph.getGraphModel().addGraphListener(listener); + AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); + ac.getModel().addAttributeListener(listener); + + GraphUpdaterEventHandler graphUpdaterHandler = new GraphUpdaterEventHandler(graph); + GraphEventBuilder eventBuilder = new GraphEventBuilder(this); + + graphUpdaterHandler.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "A", null)); + graphUpdaterHandler.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "B", null)); + graphUpdaterHandler.handleGraphEvent(eventBuilder.edgeAddedEvent("AB", "A", "B", false, null)); + + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } + } + +} diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/MockGraphEventHandler.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/MockGraphEventHandler.java new file mode 100644 index 0000000000..095427ae5a --- /dev/null +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/MockGraphEventHandler.java @@ -0,0 +1,59 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.test; + +import org.gephi.streaming.api.GraphEventHandler; +import org.gephi.streaming.api.event.GraphEvent; + +class MockGraphEventHandler implements GraphEventHandler { + + private int eventCount = 0; + + @Override + public void handleGraphEvent(GraphEvent event) { + eventCount++; + } + + public int getEventCount() { + return eventCount; + } +} diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/StreamControllerTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/StreamControllerTest.java new file mode 100644 index 0000000000..1421614067 --- /dev/null +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/StreamControllerTest.java @@ -0,0 +1,94 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ + +package org.gephi.streaming.test; + +import java.io.IOException; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphController; +import org.gephi.graph.api.GraphModel; +import org.gephi.project.api.Project; +import org.gephi.project.api.ProjectController; +import org.gephi.project.api.Workspace; +import org.gephi.streaming.api.StreamingConnection; +import org.gephi.streaming.api.StreamingController; +import org.gephi.streaming.api.StreamingEndpoint; +import org.junit.Test; +import org.openide.util.Lookup; + +/** + * + * @author panisson + */ +public class StreamControllerTest { + + @Test + public void connectToStream() throws IOException { + String resource = "amazon.json"; + + StreamingController controller = Lookup.getDefault().lookup(StreamingController.class); + StreamingEndpoint endpoint = new StreamingEndpoint(); + endpoint.setStreamType(controller.getStreamType("JSON")); + endpoint.setUrl(this.getClass().getResource(resource)); + + + // Get active graph instance - Project and Graph API + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + Project project = projectController.getCurrentProject(); + if (project==null) + projectController.newProject(); + Workspace workspace = projectController.getCurrentWorkspace(); + if (workspace==null) + workspace = projectController.newWorkspace(projectController.getCurrentProject()); +// projectController.openWorkspace(workspace); + + GraphController graphController = Lookup.getDefault().lookup(GraphController.class); + GraphModel graphModel = graphController.getModel(); + Graph graph = graphModel.getHierarchicalMixedGraph(); + + // Connect to stream - Streaming API + + StreamingConnection connection = controller.connect(endpoint, graph); + connection.process(); + } + +} diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/amazon.json b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/amazon.json new file mode 100644 index 0000000000..ce9a7a5a9a --- /dev/null +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/amazon.json @@ -0,0 +1,1405 @@ +{"an":{"0201485419":{}}} +{"cn":{"0201485419":{"label":"Art of Computer...","full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)"}}} +{"an":{"0321335708":{"label":"Art of Computer...","full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)"}}} +{"ae":{"0201485419_0321335708":{"source":"0201485419","directed":false,"target":"0321335708"}}} +{"an":{"0201558025":{"label":"Art of Computer...","full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)"}}} +{"ae":{"0201485419_0201558025":{"source":"0201485419","directed":false,"target":"0201558025"}}} +{"an":{"0262032937":{"label":"Art of Computer...","full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)"}}} +{"ae":{"0201485419_0262032937":{"source":"0201485419","directed":false,"target":"0262032937"}}} +{"an":{"0201853930":{"label":"Art of Computer...","full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)"}}} +{"ae":{"0201485419_0201853930":{"source":"0201485419","directed":false,"target":"0201853930"}}} +{"an":{"0201853949":{"label":"Art of Computer...","full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)"}}} +{"ae":{"0201485419_0201853949":{"source":"0201485419","directed":false,"target":"0201853949"}}} +{"cn":{"0321335708":{"label":"Art of Computer...","full-label":"Art of Computer Programming, Volume 4, Fascicle 4,The: Generating All Trees--History of Combinatorial Generation (Art of Computer Programming)"}}} +{"ae":{"0201853949_0321335708":{"source":"0201853949","directed":false,"target":"0321335708"}}} +{"ae":{"0201853930_0321335708":{"source":"0201853930","directed":false,"target":"0321335708"}}} +{"an":{"0201853922":{"label":"Art of Computer...","full-label":"Art of Computer Programming, Volume 4, Fascicle 4,The: Generating All Trees--History of Combinatorial Generation (Art of Computer Programming)"}}} +{"ae":{"0321335708_0201853922":{"source":"0321335708","directed":false,"target":"0201853922"}}} +{"an":{"0321534964":{"label":"Art of Computer...","full-label":"Art of Computer Programming, Volume 4, Fascicle 4,The: Generating All Trees--History of Combinatorial Generation (Art of Computer Programming)"}}} +{"ae":{"0321335708_0321534964":{"source":"0321335708","directed":false,"target":"0321534964"}}} +{"cn":{"0201558025":{"label":"Concrete Mathem...","full-label":"Concrete Mathematics: A Foundation for Computer Science (2nd Edition)"}}} +{"ae":{"0262032937_0201558025":{"source":"0262032937","directed":false,"target":"0201558025"}}} +{"an":{"0387948600":{"label":"Concrete Mathem...","full-label":"Concrete Mathematics: A Foundation for Computer Science (2nd Edition)"}}} +{"ae":{"0201558025_0387948600":{"source":"0201558025","directed":false,"target":"0387948600"}}} +{"an":{"0201657880":{"label":"Concrete Mathem...","full-label":"Concrete Mathematics: A Foundation for Computer Science (2nd Edition)"}}} +{"ae":{"0201558025_0201657880":{"source":"0201558025","directed":false,"target":"0201657880"}}} +{"ae":{"0321335708_0201558025":{"source":"0321335708","directed":false,"target":"0201558025"}}} +{"cn":{"0262032937":{"label":"Introduction to...","full-label":"Introduction to Algorithms"}}} +{"an":{"0137903952":{"label":"Introduction to...","full-label":"Introduction to Algorithms"}}} +{"ae":{"0262032937_0137903952":{"source":"0262032937","directed":false,"target":"0137903952"}}} +{"an":{"0534950973":{"label":"Introduction to...","full-label":"Introduction to Algorithms"}}} +{"ae":{"0262032937_0534950973":{"source":"0262032937","directed":false,"target":"0534950973"}}} +{"an":{"0471694665":{"label":"Introduction to...","full-label":"Introduction to Algorithms"}}} +{"ae":{"0262032937_0471694665":{"source":"0262032937","directed":false,"target":"0471694665"}}} +{"an":{"0123706068":{"label":"Introduction to...","full-label":"Introduction to Algorithms"}}} +{"ae":{"0262032937_0123706068":{"source":"0262032937","directed":false,"target":"0123706068"}}} +{"an":{"0201633612":{"label":"Introduction to...","full-label":"Introduction to Algorithms"}}} +{"ae":{"0262032937_0201633612":{"source":"0262032937","directed":false,"target":"0201633612"}}} +{"cn":{"0201853930":{"label":"The Art of Comp...","full-label":"The Art of Computer Programming, Volume 4, Fascicle 2: Generating All Tuples and Permutations (Art of Computer Programming)"}}} +{"ae":{"0201853949_0201853930":{"source":"0201853949","directed":false,"target":"0201853930"}}} +{"ae":{"0201853922_0201853930":{"source":"0201853922","directed":false,"target":"0201853930"}}} +{"ae":{"0321534964_0201853930":{"source":"0321534964","directed":false,"target":"0201853930"}}} +{"cn":{"0201853949":{"label":"The Art of Comp...","full-label":"The Art of Computer Programming, Volume 4, Fascicle 3: Generating All Combinations and Partitions (Art of Computer Programming)"}}} +{"ae":{"0201853922_0201853949":{"source":"0201853922","directed":false,"target":"0201853949"}}} +{"ae":{"0321534964_0201853949":{"source":"0321534964","directed":false,"target":"0201853949"}}} +{"cn":{"0201853922":{"label":"The Art of Comp...","full-label":"The Art of Computer Programming, Volume 1, Fascicle 1: MMIX -- A RISC Computer for the New Millennium (Art of Computer Programming)"}}} +{"ae":{"0321534964_0201853922":{"source":"0321534964","directed":false,"target":"0201853922"}}} +{"ae":{"0201485419_0201853922":{"source":"0201485419","directed":false,"target":"0201853922"}}} +{"cn":{"0321534964":{"label":"The Art of Comp...","full-label":"The Art of Computer Programming, Volume 4, Fascicle 0: Introduction to Combinatorial Algorithms and Boolean Functions (Art of Computer Programming)"}}} +{"ae":{"0201485419_0321534964":{"source":"0201485419","directed":false,"target":"0321534964"}}} +{"cn":{"0387948600":{"label":"The Algorithm D...","full-label":"The Algorithm Design Manual"}}} +{"an":{"0387001638":{"label":"The Algorithm D...","full-label":"The Algorithm Design Manual"}}} +{"ae":{"0387948600_0387001638":{"source":"0387948600","directed":false,"target":"0387001638"}}} +{"ae":{"0262032937_0387948600":{"source":"0262032937","directed":false,"target":"0387948600"}}} +{"an":{"0596529325":{"label":"The Algorithm D...","full-label":"The Algorithm Design Manual"}}} +{"ae":{"0387948600_0596529325":{"source":"0387948600","directed":false,"target":"0596529325"}}} +{"ae":{"0201657880_0387948600":{"source":"0201657880","directed":false,"target":"0387948600"}}} +{"an":{"0596510047":{"label":"The Algorithm D...","full-label":"The Algorithm Design Manual"}}} +{"ae":{"0387948600_0596510047":{"source":"0387948600","directed":false,"target":"0596510047"}}} +{"cn":{"0201657880":{"label":"Programming Pea...","full-label":"Programming Pearls (2nd Edition) (ACM Press)"}}} +{"an":{"047012167X":{"label":"Programming Pea...","full-label":"Programming Pearls (2nd Edition) (ACM Press)"}}} +{"ae":{"0201657880_047012167X":{"source":"0201657880","directed":false,"target":"047012167X"}}} +{"an":{"0735619670":{"label":"Programming Pea...","full-label":"Programming Pearls (2nd Edition) (ACM Press)"}}} +{"ae":{"0201657880_0735619670":{"source":"0201657880","directed":false,"target":"0735619670"}}} +{"an":{"020161586X":{"label":"Programming Pea...","full-label":"Programming Pearls (2nd Edition) (ACM Press)"}}} +{"ae":{"0201657880_020161586X":{"source":"0201657880","directed":false,"target":"020161586X"}}} +{"ae":{"0201633612_0201657880":{"source":"0201633612","directed":false,"target":"0201657880"}}} +{"an":{"020161622X":{"label":"Programming Pea...","full-label":"Programming Pearls (2nd Edition) (ACM Press)"}}} +{"ae":{"0201657880_020161622X":{"source":"0201657880","directed":false,"target":"020161622X"}}} +{"cn":{"0137903952":{"label":"Artificial Inte...","full-label":"Artificial Intelligence: A Modern Approach (2nd Edition) (Prentice Hall Series in Artificial Intelligence)"}}} +{"an":{"0387310738":{"label":"Artificial Inte...","full-label":"Artificial Intelligence: A Modern Approach (2nd Edition) (Prentice Hall Series in Artificial Intelligence)"}}} +{"ae":{"0137903952_0387310738":{"source":"0137903952","directed":false,"target":"0387310738"}}} +{"ae":{"0471694665_0137903952":{"source":"0471694665","directed":false,"target":"0137903952"}}} +{"an":{"0071154671":{"label":"Artificial Inte...","full-label":"Artificial Intelligence: A Modern Approach (2nd Edition) (Prentice Hall Series in Artificial Intelligence)"}}} +{"ae":{"0137903952_0071154671":{"source":"0137903952","directed":false,"target":"0071154671"}}} +{"ae":{"0534950973_0137903952":{"source":"0534950973","directed":false,"target":"0137903952"}}} +{"cn":{"0534950973":{"label":"Introduction to...","full-label":"Introduction to the Theory of Computation, Second Edition"}}} +{"ae":{"0471694665_0534950973":{"source":"0471694665","directed":false,"target":"0534950973"}}} +{"an":{"0716710455":{"label":"Introduction to...","full-label":"Introduction to the Theory of Computation, Second Edition"}}} +{"ae":{"0534950973_0716710455":{"source":"0534950973","directed":false,"target":"0716710455"}}} +{"an":{"0321486811":{"label":"Introduction to...","full-label":"Introduction to the Theory of Computation, Second Edition"}}} +{"ae":{"0534950973_0321486811":{"source":"0534950973","directed":false,"target":"0321486811"}}} +{"cn":{"0471694665":{"label":"Operating Syste...","full-label":"Operating System Concepts (7th Edition)"}}} +{"ae":{"0123706068_0471694665":{"source":"0123706068","directed":false,"target":"0471694665"}}} +{"an":{"0321497708":{"label":"Operating Syste...","full-label":"Operating System Concepts (7th Edition)"}}} +{"ae":{"0471694665_0321497708":{"source":"0471694665","directed":false,"target":"0321497708"}}} +{"cn":{"0123706068":{"label":"Computer Organi...","full-label":"Computer Organization and Design: The Hardware/Software Interface. Third Edition, Revised"}}} +{"an":{"0123704901":{"label":"Computer Organi...","full-label":"Computer Organization and Design: The Hardware/Software Interface. Third Edition, Revised"}}} +{"ae":{"0123706068_0123704901":{"source":"0123706068","directed":false,"target":"0123704901"}}} +{"an":{"0131103628":{"label":"Computer Organi...","full-label":"Computer Organization and Design: The Hardware/Software Interface. Third Edition, Revised"}}} +{"ae":{"0123706068_0131103628":{"source":"0123706068","directed":false,"target":"0131103628"}}} +{"ae":{"0137903952_0123706068":{"source":"0137903952","directed":false,"target":"0123706068"}}} +{"cn":{"0201633612":{"label":"Design Patterns...","full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)"}}} +{"an":{"0201485672":{"label":"Design Patterns...","full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201633612_0201485672":{"source":"0201633612","directed":false,"target":"0201485672"}}} +{"ae":{"0735619670_0201633612":{"source":"0735619670","directed":false,"target":"0201633612"}}} +{"an":{"0596007124":{"label":"Design Patterns...","full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201633612_0596007124":{"source":"0201633612","directed":false,"target":"0596007124"}}} +{"an":{"0321334876":{"label":"Design Patterns...","full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201633612_0321334876":{"source":"0201633612","directed":false,"target":"0321334876"}}} +{"an":{"0321193687":{"label":"Design Patterns...","full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201633612_0321193687":{"source":"0201633612","directed":false,"target":"0321193687"}}} +{"cn":{"0387001638":{"label":"Programming Cha...","full-label":"Programming Challenges"}}} +{"ae":{"047012167X_0387001638":{"source":"047012167X","directed":false,"target":"0387001638"}}} +{"ae":{"0201657880_0387001638":{"source":"0201657880","directed":false,"target":"0387001638"}}} +{"an":{"0470121688":{"label":"Programming Cha...","full-label":"Programming Challenges"}}} +{"ae":{"0387001638_0470121688":{"source":"0387001638","directed":false,"target":"0470121688"}}} +{"ae":{"0262032937_0387001638":{"source":"0262032937","directed":false,"target":"0387001638"}}} +{"cn":{"0596529325":{"label":"Programming Col...","full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications"}}} +{"an":{"0596529309":{"label":"Programming Col...","full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications"}}} +{"ae":{"0596529325_0596529309":{"source":"0596529325","directed":false,"target":"0596529309"}}} +{"an":{"0596529260":{"label":"Programming Col...","full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications"}}} +{"ae":{"0596529325_0596529260":{"source":"0596529325","directed":false,"target":"0596529260"}}} +{"an":{"0596102356":{"label":"Programming Col...","full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications"}}} +{"ae":{"0596529325_0596102356":{"source":"0596529325","directed":false,"target":"0596102356"}}} +{"ae":{"0596510047_0596529325":{"source":"0596510047","directed":false,"target":"0596529325"}}} +{"an":{"0596514557":{"label":"Programming Col...","full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications"}}} +{"ae":{"0596529325_0596514557":{"source":"0596529325","directed":false,"target":"0596514557"}}} +{"cn":{"0596510047":{"label":"Beautiful Code:...","full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))"}}} +{"an":{"1400082471":{"label":"Beautiful Code:...","full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))"}}} +{"ae":{"0596510047_1400082471":{"source":"0596510047","directed":false,"target":"1400082471"}}} +{"ae":{"0596529260_0596510047":{"source":"0596529260","directed":false,"target":"0596510047"}}} +{"an":{"193435600X":{"label":"Beautiful Code:...","full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))"}}} +{"ae":{"0596510047_193435600X":{"source":"0596510047","directed":false,"target":"193435600X"}}} +{"an":{"159059844X":{"label":"Beautiful Code:...","full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))"}}} +{"ae":{"0596510047_159059844X":{"source":"0596510047","directed":false,"target":"159059844X"}}} +{"cn":{"047012167X":{"label":"Programming Int...","full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)"}}} +{"an":{"B000JBY0RY":{"label":"Programming Int...","full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)"}}} +{"ae":{"047012167X_B000JBY0RY":{"source":"047012167X","directed":false,"target":"B000JBY0RY"}}} +{"an":{"B000ESSSN4":{"label":"Programming Int...","full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)"}}} +{"ae":{"047012167X_B000ESSSN4":{"source":"047012167X","directed":false,"target":"B000ESSSN4"}}} +{"ae":{"0470121688_047012167X":{"source":"0470121688","directed":false,"target":"047012167X"}}} +{"an":{"0071495789":{"label":"Programming Int...","full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)"}}} +{"ae":{"047012167X_0071495789":{"source":"047012167X","directed":false,"target":"0071495789"}}} +{"cn":{"0735619670":{"label":"Code Complete: ...","full-label":"Code Complete: A Practical Handbook of Software Construction"}}} +{"ae":{"020161622X_0735619670":{"source":"020161622X","directed":false,"target":"0735619670"}}} +{"an":{"1556159005":{"label":"Code Complete: ...","full-label":"Code Complete: A Practical Handbook of Software Construction"}}} +{"ae":{"0735619670_1556159005":{"source":"0735619670","directed":false,"target":"1556159005"}}} +{"an":{"0735605351":{"label":"Code Complete: ...","full-label":"Code Complete: A Practical Handbook of Software Construction"}}} +{"ae":{"0735619670_0735605351":{"source":"0735619670","directed":false,"target":"0735605351"}}} +{"an":{"0201835959":{"label":"Code Complete: ...","full-label":"Code Complete: A Practical Handbook of Software Construction"}}} +{"ae":{"0735619670_0201835959":{"source":"0735619670","directed":false,"target":"0201835959"}}} +{"cn":{"020161586X":{"label":"The Practice of...","full-label":"The Practice of Programming (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0735619670_020161586X":{"source":"0735619670","directed":false,"target":"020161586X"}}} +{"ae":{"0131103628_020161586X":{"source":"0131103628","directed":false,"target":"020161586X"}}} +{"ae":{"0201633612_020161586X":{"source":"0201633612","directed":false,"target":"020161586X"}}} +{"an":{"013937681X":{"label":"The Practice of...","full-label":"The Practice of Programming (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"020161586X_013937681X":{"source":"020161586X","directed":false,"target":"013937681X"}}} +{"cn":{"020161622X":{"label":"The Pragmatic P...","full-label":"The Pragmatic Programmer: From Journeyman to Master"}}} +{"ae":{"0201835959_020161622X":{"source":"0201835959","directed":false,"target":"020161622X"}}} +{"ae":{"0201485672_020161622X":{"source":"0201485672","directed":false,"target":"020161622X"}}} +{"ae":{"0201633612_020161622X":{"source":"0201633612","directed":false,"target":"020161622X"}}} +{"an":{"0932633439":{"label":"The Pragmatic P...","full-label":"The Pragmatic Programmer: From Journeyman to Master"}}} +{"ae":{"020161622X_0932633439":{"source":"020161622X","directed":false,"target":"0932633439"}}} +{"cn":{"0387310738":{"label":"Pattern Recogni...","full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)"}}} +{"an":{"0387952845":{"label":"Pattern Recogni...","full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)"}}} +{"ae":{"0387310738_0387952845":{"source":"0387310738","directed":false,"target":"0387952845"}}} +{"an":{"0471056693":{"label":"Pattern Recogni...","full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)"}}} +{"ae":{"0387310738_0471056693":{"source":"0387310738","directed":false,"target":"0471056693"}}} +{"an":{"0120884070":{"label":"Pattern Recogni...","full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)"}}} +{"ae":{"0387310738_0120884070":{"source":"0387310738","directed":false,"target":"0120884070"}}} +{"an":{"026218253X":{"label":"Pattern Recogni...","full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)"}}} +{"ae":{"0387310738_026218253X":{"source":"0387310738","directed":false,"target":"026218253X"}}} +{"ae":{"0596529325_0387310738":{"source":"0596529325","directed":false,"target":"0387310738"}}} +{"cn":{"0071154671":{"label":"Machine Learnin...","full-label":"Machine Learning (Mcgraw-Hill International Edit)"}}} +{"ae":{"0387310738_0071154671":{"source":"0387310738","directed":false,"target":"0071154671"}}} +{"ae":{"0120884070_0071154671":{"source":"0120884070","directed":false,"target":"0071154671"}}} +{"ae":{"0387952845_0071154671":{"source":"0387952845","directed":false,"target":"0071154671"}}} +{"ae":{"0471056693_0071154671":{"source":"0471056693","directed":false,"target":"0071154671"}}} +{"cn":{"0716710455":{"label":"Computers and I...","full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)"}}} +{"an":{"0486402584":{"label":"Computers and I...","full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)"}}} +{"ae":{"0716710455_0486402584":{"source":"0716710455","directed":false,"target":"0486402584"}}} +{"an":{"3540653678":{"label":"Computers and I...","full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)"}}} +{"ae":{"0716710455_3540653678":{"source":"0716710455","directed":false,"target":"3540653678"}}} +{"ae":{"0262032937_0716710455":{"source":"0262032937","directed":false,"target":"0716710455"}}} +{"an":{"0201530821":{"label":"Computers and I...","full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)"}}} +{"ae":{"0716710455_0201530821":{"source":"0716710455","directed":false,"target":"0201530821"}}} +{"cn":{"0321486811":{"label":"Compilers: Prin...","full-label":"Compilers: Principles, Techniques, and Tools (2nd Edition)"}}} +{"an":{"B00007FYCY":{"label":"Compilers: Prin...","full-label":"Compilers: Principles, Techniques, and Tools (2nd Edition)"}}} +{"ae":{"0321486811_B00007FYCY":{"source":"0321486811","directed":false,"target":"B00007FYCY"}}} +{"an":{"0126339511":{"label":"Compilers: Prin...","full-label":"Compilers: Principles, Techniques, and Tools (2nd Edition)"}}} +{"ae":{"0321486811_0126339511":{"source":"0321486811","directed":false,"target":"0126339511"}}} +{"ae":{"0262032937_0321486811":{"source":"0262032937","directed":false,"target":"0321486811"}}} +{"ae":{"0137903952_0321486811":{"source":"0137903952","directed":false,"target":"0321486811"}}} +{"ae":{"0596510047_0321486811":{"source":"0596510047","directed":false,"target":"0321486811"}}} +{"cn":{"0321497708":{"label":"Computer Networ...","full-label":"Computer Networking: A Top-Down Approach (4th Edition)"}}} +{"ae":{"0262032937_0321497708":{"source":"0262032937","directed":false,"target":"0321497708"}}} +{"ae":{"0123706068_0321497708":{"source":"0123706068","directed":false,"target":"0321497708"}}} +{"ae":{"0137903952_0321497708":{"source":"0137903952","directed":false,"target":"0321497708"}}} +{"an":{"0321369572":{"label":"Computer Networ...","full-label":"Computer Networking: A Top-Down Approach (4th Edition)"}}} +{"ae":{"0321497708_0321369572":{"source":"0321497708","directed":false,"target":"0321369572"}}} +{"cn":{"0123704901":{"label":"Computer Archit...","full-label":"Computer Architecture, Fourth Edition: A Quantitative Approach"}}} +{"ae":{"0262032937_0123704901":{"source":"0262032937","directed":false,"target":"0123704901"}}} +{"ae":{"0471694665_0123704901":{"source":"0471694665","directed":false,"target":"0123704901"}}} +{"an":{"1593271042":{"label":"Computer Archit...","full-label":"Computer Architecture, Fourth Edition: A Quantitative Approach"}}} +{"ae":{"0123704901_1593271042":{"source":"0123704901","directed":false,"target":"1593271042"}}} +{"ae":{"0321486811_0123704901":{"source":"0321486811","directed":false,"target":"0123704901"}}} +{"cn":{"0131103628":{"label":"C Programming L...","full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)"}}} +{"an":{"0201700735":{"label":"C Programming L...","full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)"}}} +{"ae":{"0131103628_0201700735":{"source":"0131103628","directed":false,"target":"0201700735"}}} +{"an":{"013089592X":{"label":"C Programming L...","full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)"}}} +{"ae":{"0131103628_013089592X":{"source":"0131103628","directed":false,"target":"013089592X"}}} +{"an":{"0672305100":{"label":"C Programming L...","full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)"}}} +{"ae":{"0131103628_0672305100":{"source":"0131103628","directed":false,"target":"0672305100"}}} +{"an":{"0201433079":{"label":"C Programming L...","full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)"}}} +{"ae":{"0131103628_0201433079":{"source":"0131103628","directed":false,"target":"0201433079"}}} +{"ae":{"013937681X_0131103628":{"source":"013937681X","directed":false,"target":"0131103628"}}} +{"cn":{"0201485672":{"label":"Refactoring: Im...","full-label":"Refactoring: Improving the Design of Existing Code (The Addison-Wesley Object Technology Series)"}}} +{"ae":{"0735619670_0201485672":{"source":"0735619670","directed":false,"target":"0201485672"}}} +{"an":{"0321146530":{"label":"Refactoring: Im...","full-label":"Refactoring: Improving the Design of Existing Code (The Addison-Wesley Object Technology Series)"}}} +{"ae":{"0201485672_0321146530":{"source":"0201485672","directed":false,"target":"0321146530"}}} +{"an":{"0321127420":{"label":"Refactoring: Im...","full-label":"Refactoring: Improving the Design of Existing Code (The Addison-Wesley Object Technology Series)"}}} +{"ae":{"0201485672_0321127420":{"source":"0201485672","directed":false,"target":"0321127420"}}} +{"cn":{"0596007124":{"label":"Head First Desi...","full-label":"Head First Design Patterns (Head First)"}}} +{"an":{"0596008678":{"label":"Head First Desi...","full-label":"Head First Design Patterns (Head First)"}}} +{"ae":{"0596007124_0596008678":{"source":"0596007124","directed":false,"target":"0596008678"}}} +{"an":{"0596009208":{"label":"Head First Desi...","full-label":"Head First Design Patterns (Head First)"}}} +{"ae":{"0596007124_0596009208":{"source":"0596007124","directed":false,"target":"0596009208"}}} +{"an":{"0596516681":{"label":"Head First Desi...","full-label":"Head First Design Patterns (Head First)"}}} +{"ae":{"0596007124_0596516681":{"source":"0596007124","directed":false,"target":"0596516681"}}} +{"an":{"0596102143":{"label":"Head First Desi...","full-label":"Head First Design Patterns (Head First)"}}} +{"ae":{"0596007124_0596102143":{"source":"0596007124","directed":false,"target":"0596102143"}}} +{"cn":{"0321334876":{"label":"Effective C++: ...","full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"an":{"020163371X":{"label":"Effective C++: ...","full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0321334876_020163371X":{"source":"0321334876","directed":false,"target":"020163371X"}}} +{"an":{"0201749629":{"label":"Effective C++: ...","full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0321334876_0201749629":{"source":"0321334876","directed":false,"target":"0201749629"}}} +{"ae":{"0201700735_0321334876":{"source":"0201700735","directed":false,"target":"0321334876"}}} +{"an":{"0201379260":{"label":"Effective C++: ...","full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0321334876_0201379260":{"source":"0321334876","directed":false,"target":"0201379260"}}} +{"cn":{"0321193687":{"label":"UML Distilled: ...","full-label":"UML Distilled: A Brief Guide to the Standard Object Modeling Language (3rd Edition) (The Addison-Wesley Object Technology Series)"}}} +{"an":{"0131489062":{"label":"UML Distilled: ...","full-label":"UML Distilled: A Brief Guide to the Standard Object Modeling Language (3rd Edition) (The Addison-Wesley Object Technology Series)"}}} +{"ae":{"0321193687_0131489062":{"source":"0321193687","directed":false,"target":"0131489062"}}} +{"ae":{"0201485672_0321193687":{"source":"0201485672","directed":false,"target":"0321193687"}}} +{"ae":{"0596007124_0321193687":{"source":"0596007124","directed":false,"target":"0321193687"}}} +{"ae":{"0735619670_0321193687":{"source":"0735619670","directed":false,"target":"0321193687"}}} +{"cn":{"0470121688":{"label":"Puzzles for Pro...","full-label":"Puzzles for Programmers and Pros"}}} +{"ae":{"0201657880_0470121688":{"source":"0201657880","directed":false,"target":"0470121688"}}} +{"ae":{"B000JBY0RY_0470121688":{"source":"B000JBY0RY","directed":false,"target":"0470121688"}}} +{"ae":{"B000ESSSN4_0470121688":{"source":"B000ESSSN4","directed":false,"target":"0470121688"}}} +{"an":{"0486281523":{"label":"Puzzles for Pro...","full-label":"Puzzles for Programmers and Pros"}}} +{"ae":{"0470121688_0486281523":{"source":"0470121688","directed":false,"target":"0486281523"}}} +{"cn":{"0596529309":{"label":"High Performanc...","full-label":"High Performance Web Sites: Essential Knowledge for Front-End Engineers"}}} +{"ae":{"0596102356_0596529309":{"source":"0596102356","directed":false,"target":"0596529309"}}} +{"ae":{"0596529260_0596529309":{"source":"0596529260","directed":false,"target":"0596529309"}}} +{"an":{"0596528108":{"label":"High Performanc...","full-label":"High Performance Web Sites: Essential Knowledge for Front-End Engineers"}}} +{"ae":{"0596529309_0596528108":{"source":"0596529309","directed":false,"target":"0596528108"}}} +{"ae":{"0596510047_0596529309":{"source":"0596510047","directed":false,"target":"0596529309"}}} +{"cn":{"0596529260":{"label":"RESTful Web Ser...","full-label":"RESTful Web Services"}}} +{"ae":{"0596102356_0596529260":{"source":"0596102356","directed":false,"target":"0596529260"}}} +{"an":{"0977616630":{"label":"RESTful Web Ser...","full-label":"RESTful Web Services"}}} +{"ae":{"0596529260_0977616630":{"source":"0596529260","directed":false,"target":"0977616630"}}} +{"cn":{"0596102356":{"label":"Building Scalab...","full-label":"Building Scalable Web Sites: Building, scaling, and optimizing the next generation of web applications"}}} +{"an":{"067232699X":{"label":"Building Scalab...","full-label":"Building Scalable Web Sites: Building, scaling, and optimizing the next generation of web applications"}}} +{"ae":{"0596102356_067232699X":{"source":"0596102356","directed":false,"target":"067232699X"}}} +{"an":{"0596003064":{"label":"Building Scalab...","full-label":"Building Scalable Web Sites: Building, scaling, and optimizing the next generation of web applications"}}} +{"ae":{"0596102356_0596003064":{"source":"0596102356","directed":false,"target":"0596003064"}}} +{"cn":{"0596514557":{"label":"Visualizing Dat...","full-label":"Visualizing Data"}}} +{"an":{"0262182629":{"label":"Visualizing Dat...","full-label":"Visualizing Data"}}} +{"ae":{"0596514557_0262182629":{"source":"0596514557","directed":false,"target":"0262182629"}}} +{"an":{"159059617X":{"label":"Visualizing Dat...","full-label":"Visualizing Data"}}} +{"ae":{"0596514557_159059617X":{"source":"0596514557","directed":false,"target":"159059617X"}}} +{"an":{"0596510519":{"label":"Visualizing Dat...","full-label":"Visualizing Data"}}} +{"ae":{"0596514557_0596510519":{"source":"0596514557","directed":false,"target":"0596510519"}}} +{"an":{"0321525655":{"label":"Visualizing Dat...","full-label":"Visualizing Data"}}} +{"ae":{"0596514557_0321525655":{"source":"0596514557","directed":false,"target":"0321525655"}}} +{"cn":{"1400082471":{"label":"Dreaming in Cod...","full-label":"Dreaming in Code: Two Dozen Programmers, Three Years, 4,732 Bugs, and One Quest for Transcendent Software"}}} +{"an":{"1590597141":{"label":"Dreaming in Cod...","full-label":"Dreaming in Code: Two Dozen Programmers, Three Years, 4,732 Bugs, and One Quest for Transcendent Software"}}} +{"ae":{"1400082471_1590597141":{"source":"1400082471","directed":false,"target":"1590597141"}}} +{"ae":{"0201835959_1400082471":{"source":"0201835959","directed":false,"target":"1400082471"}}} +{"ae":{"159059844X_1400082471":{"source":"159059844X","directed":false,"target":"1400082471"}}} +{"an":{"1590598385":{"label":"Dreaming in Cod...","full-label":"Dreaming in Code: Two Dozen Programmers, Three Years, 4,732 Bugs, and One Quest for Transcendent Software"}}} +{"ae":{"1400082471_1590598385":{"source":"1400082471","directed":false,"target":"1590598385"}}} +{"cn":{"193435600X":{"label":"Programming Erl...","full-label":"Programming Erlang: Software for a Concurrent World"}}} +{"ae":{"0596529325_193435600X":{"source":"0596529325","directed":false,"target":"193435600X"}}} +{"ae":{"0596529260_193435600X":{"source":"0596529260","directed":false,"target":"193435600X"}}} +{"an":{"0978739256":{"label":"Programming Erl...","full-label":"Programming Erlang: Software for a Concurrent World"}}} +{"ae":{"193435600X_0978739256":{"source":"193435600X","directed":false,"target":"0978739256"}}} +{"an":{"0521692695":{"label":"Programming Erl...","full-label":"Programming Erlang: Software for a Concurrent World"}}} +{"ae":{"193435600X_0521692695":{"source":"193435600X","directed":false,"target":"0521692695"}}} +{"cn":{"159059844X":{"label":"Managing Humans...","full-label":"Managing Humans: Biting and Humorous Tales of a Software Engineering Manager"}}} +{"ae":{"1590598385_159059844X":{"source":"1590598385","directed":false,"target":"159059844X"}}} +{"an":{"0596527055":{"label":"Managing Humans...","full-label":"Managing Humans: Biting and Humorous Tales of a Software Engineering Manager"}}} +{"ae":{"159059844X_0596527055":{"source":"159059844X","directed":false,"target":"0596527055"}}} +{"ae":{"1590597141_159059844X":{"source":"1590597141","directed":false,"target":"159059844X"}}} +{"cn":{"B000JBY0RY":{"label":"How Would You M...","full-label":"How Would You Move Mount Fuji? Microsoft's Cult of the Puzzle - How the World's Smartest Company Selects the Most Creative Thinkers"}}} +{"ae":{"0201657880_B000JBY0RY":{"source":"0201657880","directed":false,"target":"B000JBY0RY"}}} +{"an":{"0071440011":{"label":"How Would You M...","full-label":"How Would You Move Mount Fuji? Microsoft's Cult of the Puzzle - How the World's Smartest Company Selects the Most Creative Thinkers"}}} +{"ae":{"B000JBY0RY_0071440011":{"source":"B000JBY0RY","directed":false,"target":"0071440011"}}} +{"an":{"0385242719":{"label":"How Would You M...","full-label":"How Would You Move Mount Fuji? Microsoft's Cult of the Puzzle - How the World's Smartest Company Selects the Most Creative Thinkers"}}} +{"ae":{"B000JBY0RY_0385242719":{"source":"B000JBY0RY","directed":false,"target":"0385242719"}}} +{"cn":{"B000ESSSN4":{"label":"How Would You M...","full-label":"How Would You Move Mount Fuji? : Microsoft's Cult of the Puzzle -- How the World's Smartest Companies Select the Most Creative Thinkers"}}} +{"ae":{"0201657880_B000ESSSN4":{"source":"0201657880","directed":false,"target":"B000ESSSN4"}}} +{"ae":{"0071440011_B000ESSSN4":{"source":"0071440011","directed":false,"target":"B000ESSSN4"}}} +{"ae":{"0385242719_B000ESSSN4":{"source":"0385242719","directed":false,"target":"B000ESSSN4"}}} +{"an":{"0970055269":{"label":"How Would You M...","full-label":"How Would You Move Mount Fuji? : Microsoft's Cult of the Puzzle -- How the World's Smartest Companies Select the Most Creative Thinkers"}}} +{"ae":{"B000ESSSN4_0970055269":{"source":"B000ESSSN4","directed":false,"target":"0970055269"}}} +{"cn":{"0071495789":{"label":"Ace the IT Inte...","full-label":"Ace the IT Interview (Ace the It Job Interview)"}}} +{"an":{"0071492747":{"label":"Ace the IT Inte...","full-label":"Ace the IT Interview (Ace the It Job Interview)"}}} +{"ae":{"0071495789_0071492747":{"source":"0071495789","directed":false,"target":"0071492747"}}} +{"an":{"0071425470":{"label":"Ace the IT Inte...","full-label":"Ace the IT Interview (Ace the It Job Interview)"}}} +{"ae":{"0071495789_0071425470":{"source":"0071495789","directed":false,"target":"0071425470"}}} +{"an":{"0071387730":{"label":"Ace the IT Inte...","full-label":"Ace the IT Interview (Ace the It Job Interview)"}}} +{"ae":{"0071495789_0071387730":{"source":"0071495789","directed":false,"target":"0071387730"}}} +{"an":{"1402203853":{"label":"Ace the IT Inte...","full-label":"Ace the IT Interview (Ace the It Job Interview)"}}} +{"ae":{"0071495789_1402203853":{"source":"0071495789","directed":false,"target":"1402203853"}}} +{"cn":{"1556159005":{"label":"Rapid Developme...","full-label":"Rapid Development: Taming Wild Software Schedules"}}} +{"ae":{"0735605351_1556159005":{"source":"0735605351","directed":false,"target":"1556159005"}}} +{"an":{"1572316217":{"label":"Rapid Developme...","full-label":"Rapid Development: Taming Wild Software Schedules"}}} +{"ae":{"1556159005_1572316217":{"source":"1556159005","directed":false,"target":"1572316217"}}} +{"ae":{"0201835959_1556159005":{"source":"0201835959","directed":false,"target":"1556159005"}}} +{"ae":{"0932633439_1556159005":{"source":"0932633439","directed":false,"target":"1556159005"}}} +{"cn":{"0735605351":{"label":"Software Estima...","full-label":"Software Estimation: Demystifying the Black Art (Best Practices (Microsoft))"}}} +{"an":{"0735618798":{"label":"Software Estima...","full-label":"Software Estimation: Demystifying the Black Art (Best Practices (Microsoft))"}}} +{"ae":{"0735605351_0735618798":{"source":"0735605351","directed":false,"target":"0735618798"}}} +{"ae":{"0932633439_0735605351":{"source":"0932633439","directed":false,"target":"0735605351"}}} +{"an":{"0131479415":{"label":"Software Estima...","full-label":"Software Estimation: Demystifying the Black Art (Best Practices (Microsoft))"}}} +{"ae":{"0735605351_0131479415":{"source":"0735605351","directed":false,"target":"0131479415"}}} +{"cn":{"0201835959":{"label":"The Mythical Ma...","full-label":"The Mythical Man-Month: Essays on Software Engineering, Anniversary Edition (2nd Edition)"}}} +{"ae":{"0932633439_0201835959":{"source":"0932633439","directed":false,"target":"0201835959"}}} +{"ae":{"0201633612_0201835959":{"source":"0201633612","directed":false,"target":"0201835959"}}} +{"cn":{"013937681X":{"label":"Unix Programmin...","full-label":"Unix Programming Environment (Prentice-Hall Software Series)"}}} +{"an":{"0132017997":{"label":"Unix Programmin...","full-label":"Unix Programming Environment (Prentice-Hall Software Series)"}}} +{"ae":{"013937681X_0132017997":{"source":"013937681X","directed":false,"target":"0132017997"}}} +{"an":{"020103669X":{"label":"Unix Programmin...","full-label":"Unix Programming Environment (Prentice-Hall Software Series)"}}} +{"ae":{"013937681X_020103669X":{"source":"013937681X","directed":false,"target":"020103669X"}}} +{"ae":{"0201433079_013937681X":{"source":"0201433079","directed":false,"target":"013937681X"}}} +{"cn":{"0932633439":{"label":"Peopleware: Pro...","full-label":"Peopleware: Productive Projects and Teams (Second Edition)"}}} +{"ae":{"0735619670_0932633439":{"source":"0735619670","directed":false,"target":"0932633439"}}} +{"an":{"1590593898":{"label":"Peopleware: Pro...","full-label":"Peopleware: Productive Projects and Teams (Second Edition)"}}} +{"ae":{"0932633439_1590593898":{"source":"0932633439","directed":false,"target":"1590593898"}}} +{"cn":{"0387952845":{"label":"The Elements of...","full-label":"The Elements of Statistical Learning"}}} +{"ae":{"0471056693_0387952845":{"source":"0471056693","directed":false,"target":"0387952845"}}} +{"ae":{"0120884070_0387952845":{"source":"0120884070","directed":false,"target":"0387952845"}}} +{"an":{"0387402721":{"label":"The Elements of...","full-label":"The Elements of Statistical Learning"}}} +{"ae":{"0387952845_0387402721":{"source":"0387952845","directed":false,"target":"0387402721"}}} +{"an":{"158488388X":{"label":"The Elements of...","full-label":"The Elements of Statistical Learning"}}} +{"ae":{"0387952845_158488388X":{"source":"0387952845","directed":false,"target":"158488388X"}}} +{"cn":{"0471056693":{"label":"Pattern Classif...","full-label":"Pattern Classification (2nd Edition)"}}} +{"an":{"0471429775":{"label":"Pattern Classif...","full-label":"Pattern Classification (2nd Edition)"}}} +{"ae":{"0471056693_0471429775":{"source":"0471056693","directed":false,"target":"0471429775"}}} +{"ae":{"0120884070_0471056693":{"source":"0120884070","directed":false,"target":"0471056693"}}} +{"cn":{"0120884070":{"label":"Data Mining: Pr...","full-label":"Data Mining: Practical Machine Learning Tools and Techniques, Second Edition (Morgan Kaufmann Series in Data Management Systems)"}}} +{"an":{"1558609016":{"label":"Data Mining: Pr...","full-label":"Data Mining: Practical Machine Learning Tools and Techniques, Second Edition (Morgan Kaufmann Series in Data Management Systems)"}}} +{"ae":{"0120884070_1558609016":{"source":"0120884070","directed":false,"target":"1558609016"}}} +{"ae":{"0596529325_0120884070":{"source":"0596529325","directed":false,"target":"0120884070"}}} +{"an":{"0262012111":{"label":"Data Mining: Pr...","full-label":"Data Mining: Practical Machine Learning Tools and Techniques, Second Edition (Morgan Kaufmann Series in Data Management Systems)"}}} +{"ae":{"0120884070_0262012111":{"source":"0120884070","directed":false,"target":"0262012111"}}} +{"cn":{"026218253X":{"label":"Gaussian Proces...","full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)"}}} +{"an":{"0262194759":{"label":"Gaussian Proces...","full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)"}}} +{"ae":{"026218253X_0262194759":{"source":"026218253X","directed":false,"target":"0262194759"}}} +{"an":{"0262033585":{"label":"Gaussian Proces...","full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)"}}} +{"ae":{"026218253X_0262033585":{"source":"026218253X","directed":false,"target":"0262033585"}}} +{"ae":{"0387952845_026218253X":{"source":"0387952845","directed":false,"target":"026218253X"}}} +{"an":{"0521642981":{"label":"Gaussian Proces...","full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)"}}} +{"ae":{"026218253X_0521642981":{"source":"026218253X","directed":false,"target":"0521642981"}}} +{"cn":{"0486402584":{"label":"Combinatorial O...","full-label":"Combinatorial Optimization: Algorithms and Complexity"}}} +{"ae":{"3540653678_0486402584":{"source":"3540653678","directed":false,"target":"0486402584"}}} +{"an":{"0486247759":{"label":"Combinatorial O...","full-label":"Combinatorial Optimization: Algorithms and Complexity"}}} +{"ae":{"0486402584_0486247759":{"source":"0486402584","directed":false,"target":"0486247759"}}} +{"ae":{"0262032937_0486402584":{"source":"0262032937","directed":false,"target":"0486402584"}}} +{"an":{"0486678709":{"label":"Combinatorial O...","full-label":"Combinatorial Optimization: Algorithms and Complexity"}}} +{"ae":{"0486402584_0486678709":{"source":"0486402584","directed":false,"target":"0486678709"}}} +{"cn":{"3540653678":{"label":"Approximation A...","full-label":"Approximation Algorithms"}}} +{"an":{"0521474655":{"label":"Approximation A...","full-label":"Approximation Algorithms"}}} +{"ae":{"3540653678_0521474655":{"source":"3540653678","directed":false,"target":"0521474655"}}} +{"an":{"0521872820":{"label":"Approximation A...","full-label":"Approximation Algorithms"}}} +{"ae":{"3540653678_0521872820":{"source":"3540653678","directed":false,"target":"0521872820"}}} +{"an":{"0521835402":{"label":"Approximation A...","full-label":"Approximation Algorithms"}}} +{"ae":{"3540653678_0521835402":{"source":"3540653678","directed":false,"target":"0521835402"}}} +{"cn":{"0201530821":{"label":"Computational C...","full-label":"Computational Complexity"}}} +{"ae":{"0534950973_0201530821":{"source":"0534950973","directed":false,"target":"0201530821"}}} +{"ae":{"0486402584_0201530821":{"source":"0486402584","directed":false,"target":"0201530821"}}} +{"ae":{"3540653678_0201530821":{"source":"3540653678","directed":false,"target":"0201530821"}}} +{"ae":{"0262032937_0201530821":{"source":"0262032937","directed":false,"target":"0201530821"}}} +{"cn":{"B00007FYCY":{"label":"Lex & Yacc","full-label":"Lex & Yacc"}}} +{"an":{"0534939724":{"label":"Lex & Yacc","full-label":"Lex & Yacc"}}} +{"ae":{"B00007FYCY_0534939724":{"source":"B00007FYCY","directed":false,"target":"0534939724"}}} +{"ae":{"0262032937_B00007FYCY":{"source":"0262032937","directed":false,"target":"B00007FYCY"}}} +{"an":{"B00007FYIJ":{"label":"Lex & Yacc","full-label":"Lex & Yacc"}}} +{"ae":{"B00007FYCY_B00007FYIJ":{"source":"B00007FYCY","directed":false,"target":"B00007FYIJ"}}} +{"ae":{"013089592X_B00007FYCY":{"source":"013089592X","directed":false,"target":"B00007FYCY"}}} +{"cn":{"0126339511":{"label":"Programming Lan...","full-label":"Programming Language Pragmatics, Second Edition"}}} +{"ae":{"0262032937_0126339511":{"source":"0262032937","directed":false,"target":"0126339511"}}} +{"ae":{"0596510047_0126339511":{"source":"0596510047","directed":false,"target":"0126339511"}}} +{"an":{"155860698X":{"label":"Programming Lan...","full-label":"Programming Language Pragmatics, Second Edition"}}} +{"ae":{"0126339511_155860698X":{"source":"0126339511","directed":false,"target":"155860698X"}}} +{"ae":{"0534950973_0126339511":{"source":"0534950973","directed":false,"target":"0126339511"}}} +{"cn":{"0321369572":{"label":"Fundamentals of...","full-label":"Fundamentals of Database Systems (5th Edition)"}}} +{"an":{"0072253517":{"label":"Fundamentals of...","full-label":"Fundamentals of Database Systems (5th Edition)"}}} +{"ae":{"0321369572_0072253517":{"source":"0321369572","directed":false,"target":"0072253517"}}} +{"ae":{"0471694665_0321369572":{"source":"0471694665","directed":false,"target":"0321369572"}}} +{"ae":{"0262032937_0321369572":{"source":"0262032937","directed":false,"target":"0321369572"}}} +{"ae":{"0137903952_0321369572":{"source":"0137903952","directed":false,"target":"0321369572"}}} +{"cn":{"1593271042":{"label":"Inside the Mach...","full-label":"Inside the Machine: An Illustrated Introduction to Microprocessors and Computer Architecture"}}} +{"an":{"1593270038":{"label":"Inside the Mach...","full-label":"Inside the Machine: An Illustrated Introduction to Microprocessors and Computer Architecture"}}} +{"ae":{"1593271042_1593270038":{"source":"1593271042","directed":false,"target":"1593270038"}}} +{"ae":{"0596510047_1593271042":{"source":"0596510047","directed":false,"target":"1593271042"}}} +{"an":{"1593270658":{"label":"Inside the Mach...","full-label":"Inside the Machine: An Illustrated Introduction to Microprocessors and Computer Architecture"}}} +{"ae":{"1593271042_1593270658":{"source":"1593271042","directed":false,"target":"1593270658"}}} +{"ae":{"0123706068_1593271042":{"source":"0123706068","directed":false,"target":"1593271042"}}} +{"cn":{"0201700735":{"label":"The C++ Program...","full-label":"The C++ Programming Language: Special Edition (3rd Edition)"}}} +{"ae":{"0201379260_0201700735":{"source":"0201379260","directed":false,"target":"0201700735"}}} +{"ae":{"0201633612_0201700735":{"source":"0201633612","directed":false,"target":"0201700735"}}} +{"ae":{"020163371X_0201700735":{"source":"020163371X","directed":false,"target":"0201700735"}}} +{"cn":{"013089592X":{"label":"C: A Reference ...","full-label":"C: A Reference Manual (5th Edition)"}}} +{"an":{"0672326663":{"label":"C: A Reference ...","full-label":"C: A Reference Manual (5th Edition)"}}} +{"ae":{"013089592X_0672326663":{"source":"013089592X","directed":false,"target":"0672326663"}}} +{"an":{"0201179288":{"label":"C: A Reference ...","full-label":"C: A Reference Manual (5th Edition)"}}} +{"ae":{"013089592X_0201179288":{"source":"013089592X","directed":false,"target":"0201179288"}}} +{"an":{"0131774298":{"label":"C: A Reference ...","full-label":"C: A Reference Manual (5th Edition)"}}} +{"ae":{"013089592X_0131774298":{"source":"013089592X","directed":false,"target":"0131774298"}}} +{"ae":{"0201433079_013089592X":{"source":"0201433079","directed":false,"target":"013089592X"}}} +{"cn":{"0672305100":{"label":"Absolute Beginn...","full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)"}}} +{"ae":{"0672326663_0672305100":{"source":"0672326663","directed":false,"target":"0672305100"}}} +{"an":{"0764570684":{"label":"Absolute Beginn...","full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)"}}} +{"ae":{"0672305100_0764570684":{"source":"0672305100","directed":false,"target":"0764570684"}}} +{"an":{"0672326965":{"label":"Absolute Beginn...","full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)"}}} +{"ae":{"0672305100_0672326965":{"source":"0672305100","directed":false,"target":"0672326965"}}} +{"an":{"0764570692":{"label":"Absolute Beginn...","full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)"}}} +{"ae":{"0672305100_0764570692":{"source":"0672305100","directed":false,"target":"0764570692"}}} +{"cn":{"0201433079":{"label":"Advanced Progra...","full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"an":{"0131411551":{"label":"Advanced Progra...","full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201433079_0131411551":{"source":"0201433079","directed":false,"target":"0131411551"}}} +{"an":{"0130810819":{"label":"Advanced Progra...","full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201433079_0130810819":{"source":"0201433079","directed":false,"target":"0130810819"}}} +{"an":{"0131411543":{"label":"Advanced Progra...","full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201433079_0131411543":{"source":"0201433079","directed":false,"target":"0131411543"}}} +{"an":{"0596005652":{"label":"Advanced Progra...","full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201433079_0596005652":{"source":"0201433079","directed":false,"target":"0596005652"}}} +{"cn":{"0321146530":{"label":"Test Driven Dev...","full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)"}}} +{"an":{"0321278658":{"label":"Test Driven Dev...","full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)"}}} +{"ae":{"0321146530_0321278658":{"source":"0321146530","directed":false,"target":"0321278658"}}} +{"an":{"0321205685":{"label":"Test Driven Dev...","full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)"}}} +{"ae":{"0321146530_0321205685":{"source":"0321146530","directed":false,"target":"0321205685"}}} +{"an":{"0131177052":{"label":"Test Driven Dev...","full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)"}}} +{"ae":{"0321146530_0131177052":{"source":"0321146530","directed":false,"target":"0131177052"}}} +{"ae":{"0131479415_0321146530":{"source":"0131479415","directed":false,"target":"0321146530"}}} +{"cn":{"0321127420":{"label":"Patterns of Ent...","full-label":"Patterns of Enterprise Application Architecture (The Addison-Wesley Signature Series)"}}} +{"an":{"0321200683":{"label":"Patterns of Ent...","full-label":"Patterns of Enterprise Application Architecture (The Addison-Wesley Signature Series)"}}} +{"ae":{"0321127420_0321200683":{"source":"0321127420","directed":false,"target":"0321200683"}}} +{"an":{"0321125215":{"label":"Patterns of Ent...","full-label":"Patterns of Enterprise Application Architecture (The Addison-Wesley Signature Series)"}}} +{"ae":{"0321127420_0321125215":{"source":"0321127420","directed":false,"target":"0321125215"}}} +{"ae":{"0201633612_0321127420":{"source":"0201633612","directed":false,"target":"0321127420"}}} +{"ae":{"0596007124_0321127420":{"source":"0596007124","directed":false,"target":"0321127420"}}} +{"cn":{"0596008678":{"label":"Head First Obje...","full-label":"Head First Object-Oriented Analysis and Design (Head First)"}}} +{"ae":{"0596009208_0596008678":{"source":"0596009208","directed":false,"target":"0596008678"}}} +{"an":{"0596527357":{"label":"Head First Obje...","full-label":"Head First Object-Oriented Analysis and Design (Head First)"}}} +{"ae":{"0596008678_0596527357":{"source":"0596008678","directed":false,"target":"0596527357"}}} +{"ae":{"0596102143_0596008678":{"source":"0596102143","directed":false,"target":"0596008678"}}} +{"an":{"059610197X":{"label":"Head First Obje...","full-label":"Head First Object-Oriented Analysis and Design (Head First)"}}} +{"ae":{"0596008678_059610197X":{"source":"0596008678","directed":false,"target":"059610197X"}}} +{"cn":{"0596009208":{"label":"Head First Java...","full-label":"Head First Java, 2nd Edition"}}} +{"ae":{"0596516681_0596009208":{"source":"0596516681","directed":false,"target":"0596009208"}}} +{"ae":{"059610197X_0596009208":{"source":"059610197X","directed":false,"target":"0596009208"}}} +{"an":{"0596005717":{"label":"Head First Java...","full-label":"Head First Java, 2nd Edition"}}} +{"ae":{"0596009208_0596005717":{"source":"0596009208","directed":false,"target":"0596005717"}}} +{"cn":{"0596516681":{"label":"Head First Serv...","full-label":"Head First Servlets and JSP: Passing the Sun Certified Web Component Developer Exam (Brain-Friendly Guides)"}}} +{"ae":{"0596005717_0596516681":{"source":"0596005717","directed":false,"target":"0596516681"}}} +{"an":{"0072253606":{"label":"Head First Serv...","full-label":"Head First Servlets and JSP: Passing the Sun Certified Web Component Developer Exam (Brain-Friendly Guides)"}}} +{"ae":{"0596516681_0072253606":{"source":"0596516681","directed":false,"target":"0072253606"}}} +{"ae":{"059610197X_0596516681":{"source":"059610197X","directed":false,"target":"0596516681"}}} +{"cn":{"0596102143":{"label":"Head First Desi...","full-label":"Head First Design Patterns Poster (Head First)"}}} +{"ae":{"0596009208_0596102143":{"source":"0596009208","directed":false,"target":"0596102143"}}} +{"ae":{"0596527357_0596102143":{"source":"0596527357","directed":false,"target":"0596102143"}}} +{"ae":{"059610197X_0596102143":{"source":"059610197X","directed":false,"target":"0596102143"}}} +{"cn":{"020163371X":{"label":"More Effective ...","full-label":"More Effective C++: 35 New Ways to Improve Your Programs and Designs (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201749629_020163371X":{"source":"0201749629","directed":false,"target":"020163371X"}}} +{"ae":{"0201633612_020163371X":{"source":"0201633612","directed":false,"target":"020163371X"}}} +{"ae":{"0201379260_020163371X":{"source":"0201379260","directed":false,"target":"020163371X"}}} +{"cn":{"0201749629":{"label":"Effective STL: ...","full-label":"Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201379260_0201749629":{"source":"0201379260","directed":false,"target":"0201749629"}}} +{"ae":{"0201633612_0201749629":{"source":"0201633612","directed":false,"target":"0201749629"}}} +{"ae":{"0201700735_0201749629":{"source":"0201700735","directed":false,"target":"0201749629"}}} +{"cn":{"0201379260":{"label":"The C++ Standar...","full-label":"The C++ Standard Library: A Tutorial and Reference"}}} +{"an":{"0201734842":{"label":"The C++ Standar...","full-label":"The C++ Standard Library: A Tutorial and Reference"}}} +{"ae":{"0201379260_0201734842":{"source":"0201379260","directed":false,"target":"0201734842"}}} +{"ae":{"0201633612_0201379260":{"source":"0201633612","directed":false,"target":"0201379260"}}} +{"cn":{"0131489062":{"label":"Applying UML an...","full-label":"Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development (3rd Edition)"}}} +{"ae":{"0596007124_0131489062":{"source":"0596007124","directed":false,"target":"0131489062"}}} +{"ae":{"0201633612_0131489062":{"source":"0201633612","directed":false,"target":"0131489062"}}} +{"an":{"0201702258":{"label":"Applying UML an...","full-label":"Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development (3rd Edition)"}}} +{"ae":{"0131489062_0201702258":{"source":"0131489062","directed":false,"target":"0201702258"}}} +{"ae":{"0201485672_0131489062":{"source":"0201485672","directed":false,"target":"0131489062"}}} +{"cn":{"0486281523":{"label":"My Best Mathema...","full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)"}}} +{"an":{"0486252116":{"label":"My Best Mathema...","full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)"}}} +{"ae":{"0486281523_0486252116":{"source":"0486281523","directed":false,"target":"0486252116"}}} +{"an":{"0486270785":{"label":"My Best Mathema...","full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)"}}} +{"ae":{"0486281523_0486270785":{"source":"0486281523","directed":false,"target":"0486270785"}}} +{"an":{"0486256375":{"label":"My Best Mathema...","full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)"}}} +{"ae":{"0486281523_0486256375":{"source":"0486281523","directed":false,"target":"0486256375"}}} +{"an":{"0486427552":{"label":"My Best Mathema...","full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)"}}} +{"ae":{"0486281523_0486427552":{"source":"0486281523","directed":false,"target":"0486427552"}}} +{"an":{"0970825315":{"label":"My Best Mathema...","full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)"}}} +{"ae":{"0486281523_0970825315":{"source":"0486281523","directed":false,"target":"0970825315"}}} +{"cn":{"0596528108":{"label":"Designing Web N...","full-label":"Designing Web Navigation: Optimizing the User Experience"}}} +{"an":{"0596527349":{"label":"Designing Web N...","full-label":"Designing Web Navigation: Optimizing the User Experience"}}} +{"ae":{"0596528108_0596527349":{"source":"0596528108","directed":false,"target":"0596527349"}}} +{"ae":{"0596529325_0596528108":{"source":"0596529325","directed":false,"target":"0596528108"}}} +{"an":{"0321344758":{"label":"Designing Web N...","full-label":"Designing Web Navigation: Optimizing the User Experience"}}} +{"ae":{"0596528108_0321344758":{"source":"0596528108","directed":false,"target":"0321344758"}}} +{"an":{"0596008031":{"label":"Designing Web N...","full-label":"Designing Web Navigation: Optimizing the User Experience"}}} +{"ae":{"0596528108_0596008031":{"source":"0596528108","directed":false,"target":"0596008031"}}} +{"cn":{"0977616630":{"label":"Agile Web Devel...","full-label":"Agile Web Development with Rails, 2nd Edition"}}} +{"an":{"0974514055":{"label":"Agile Web Devel...","full-label":"Agile Web Development with Rails, 2nd Edition"}}} +{"ae":{"0977616630_0974514055":{"source":"0977616630","directed":false,"target":"0974514055"}}} +{"an":{"0977616606":{"label":"Agile Web Devel...","full-label":"Agile Web Development with Rails, 2nd Edition"}}} +{"ae":{"0977616630_0977616606":{"source":"0977616630","directed":false,"target":"0977616606"}}} +{"an":{"0596527446":{"label":"Agile Web Devel...","full-label":"Agile Web Development with Rails, 2nd Edition"}}} +{"ae":{"0977616630_0596527446":{"source":"0977616630","directed":false,"target":"0596527446"}}} +{"an":{"0596523696":{"label":"Agile Web Devel...","full-label":"Agile Web Development with Rails, 2nd Edition"}}} +{"ae":{"0977616630_0596523696":{"source":"0977616630","directed":false,"target":"0596523696"}}} +{"an":{"1932394699":{"label":"Agile Web Devel...","full-label":"Agile Web Development with Rails, 2nd Edition"}}} +{"ae":{"0977616630_1932394699":{"source":"0977616630","directed":false,"target":"1932394699"}}} +{"cn":{"067232699X":{"label":"Scalable Intern...","full-label":"Scalable Internet Architectures (Developer's Library)"}}} +{"ae":{"0596529309_067232699X":{"source":"0596529309","directed":false,"target":"067232699X"}}} +{"ae":{"0596529325_067232699X":{"source":"0596529325","directed":false,"target":"067232699X"}}} +{"ae":{"0596529260_067232699X":{"source":"0596529260","directed":false,"target":"067232699X"}}} +{"ae":{"0596003064_067232699X":{"source":"0596003064","directed":false,"target":"067232699X"}}} +{"cn":{"0596003064":{"label":"High Performanc...","full-label":"High Performance MySQL"}}} +{"an":{"059652708X":{"label":"High Performanc...","full-label":"High Performance MySQL"}}} +{"ae":{"0596003064_059652708X":{"source":"0596003064","directed":false,"target":"059652708X"}}} +{"an":{"0596100892":{"label":"High Performanc...","full-label":"High Performance MySQL"}}} +{"ae":{"0596003064_0596100892":{"source":"0596003064","directed":false,"target":"0596100892"}}} +{"ae":{"0596529309_0596003064":{"source":"0596529309","directed":false,"target":"0596003064"}}} +{"an":{"159059505X":{"label":"High Performanc...","full-label":"High Performance MySQL"}}} +{"ae":{"0596003064_159059505X":{"source":"0596003064","directed":false,"target":"159059505X"}}} +{"cn":{"0262182629":{"label":"Processing: A P...","full-label":"Processing: A Programming Handbook for Visual Designers and Artists"}}} +{"ae":{"159059617X_0262182629":{"source":"159059617X","directed":false,"target":"0262182629"}}} +{"ae":{"0596510519_0262182629":{"source":"0596510519","directed":false,"target":"0262182629"}}} +{"an":{"159200346X":{"label":"Processing: A P...","full-label":"Processing: A Programming Handbook for Visual Designers and Artists"}}} +{"ae":{"0262182629_159200346X":{"source":"0262182629","directed":false,"target":"159200346X"}}} +{"ae":{"0596529325_0262182629":{"source":"0596529325","directed":false,"target":"0262182629"}}} +{"cn":{"159059617X":{"label":"Processing: Cre...","full-label":"Processing: Creative Coding and Computational Art (Foundation)"}}} +{"ae":{"0596510519_159059617X":{"source":"0596510519","directed":false,"target":"159059617X"}}} +{"ae":{"159200346X_159059617X":{"source":"159200346X","directed":false,"target":"159059617X"}}} +{"an":{"0500285179":{"label":"Processing: Cre...","full-label":"Processing: Creative Coding and Computational Art (Foundation)"}}} +{"ae":{"159059617X_0500285179":{"source":"159059617X","directed":false,"target":"0500285179"}}} +{"cn":{"0596510519":{"label":"Making Things T...","full-label":"Making Things Talk: Practical Methods for Connecting Physical Objects"}}} +{"ae":{"159200346X_0596510519":{"source":"159200346X","directed":false,"target":"0596510519"}}} +{"an":{"059651428X":{"label":"Making Things T...","full-label":"Making Things Talk: Practical Methods for Connecting Physical Objects"}}} +{"ae":{"0596510519_059651428X":{"source":"0596510519","directed":false,"target":"059651428X"}}} +{"cn":{"0321525655":{"label":"Presentation Ze...","full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)"}}} +{"an":{"1591841992":{"label":"Presentation Ze...","full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)"}}} +{"ae":{"0321525655_1591841992":{"source":"0321525655","directed":false,"target":"1591841992"}}} +{"an":{"0735623872":{"label":"Presentation Ze...","full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)"}}} +{"ae":{"0321525655_0735623872":{"source":"0321525655","directed":false,"target":"0735623872"}}} +{"an":{"0195320697":{"label":"Presentation Ze...","full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)"}}} +{"ae":{"0321525655_0195320697":{"source":"0321525655","directed":false,"target":"0195320697"}}} +{"an":{"0979777704":{"label":"Presentation Ze...","full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)"}}} +{"ae":{"0321525655_0979777704":{"source":"0321525655","directed":false,"target":"0979777704"}}} +{"an":{"1400064287":{"label":"Presentation Ze...","full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)"}}} +{"ae":{"0321525655_1400064287":{"source":"0321525655","directed":false,"target":"1400064287"}}} +{"cn":{"1590597141":{"label":"Founders at Wor...","full-label":"Founders at Work: Stories of Startups' Early Days"}}} +{"an":{"1591840562":{"label":"Founders at Wor...","full-label":"Founders at Work: Stories of Startups' Early Days"}}} +{"ae":{"1590597141_1591840562":{"source":"1590597141","directed":false,"target":"1591840562"}}} +{"ae":{"0596527055_1590597141":{"source":"0596527055","directed":false,"target":"1590597141"}}} +{"ae":{"0596510047_1590597141":{"source":"0596510047","directed":false,"target":"1590597141"}}} +{"ae":{"0596529325_1590597141":{"source":"0596529325","directed":false,"target":"1590597141"}}} +{"cn":{"1590598385":{"label":"Smart and Gets ...","full-label":"Smart and Gets Things Done: Joel Spolsky's Concise Guide to Finding the Best Technical Talent"}}} +{"ae":{"1590593898_1590598385":{"source":"1590593898","directed":false,"target":"1590598385"}}} +{"ae":{"0596510047_1590598385":{"source":"0596510047","directed":false,"target":"1590598385"}}} +{"ae":{"1590597141_1590598385":{"source":"1590597141","directed":false,"target":"1590598385"}}} +{"ae":{"0932633439_1590598385":{"source":"0932633439","directed":false,"target":"1590598385"}}} +{"cn":{"0978739256":{"label":"The Definitive ...","full-label":"The Definitive ANTLR Reference: Building Domain-Specific Languages (Pragmatic Programmers)"}}} +{"ae":{"0596510047_0978739256":{"source":"0596510047","directed":false,"target":"0978739256"}}} +{"ae":{"0596529325_0978739256":{"source":"0596529325","directed":false,"target":"0978739256"}}} +{"ae":{"0596529260_0978739256":{"source":"0596529260","directed":false,"target":"0978739256"}}} +{"an":{"0321349601":{"label":"The Definitive ...","full-label":"The Definitive ANTLR Reference: Building Domain-Specific Languages (Pragmatic Programmers)"}}} +{"ae":{"0978739256_0321349601":{"source":"0978739256","directed":false,"target":"0321349601"}}} +{"cn":{"0521692695":{"label":"Programming in ...","full-label":"Programming in Haskell"}}} +{"an":{"0521644089":{"label":"Programming in ...","full-label":"Programming in Haskell"}}} +{"ae":{"0521692695_0521644089":{"source":"0521692695","directed":false,"target":"0521644089"}}} +{"an":{"0954300696":{"label":"Programming in ...","full-label":"Programming in Haskell"}}} +{"ae":{"0521692695_0954300696":{"source":"0521692695","directed":false,"target":"0954300696"}}} +{"ae":{"0596510047_0521692695":{"source":"0596510047","directed":false,"target":"0521692695"}}} +{"an":{"0521663504":{"label":"Programming in ...","full-label":"Programming in Haskell"}}} +{"ae":{"0521692695_0521663504":{"source":"0521692695","directed":false,"target":"0521663504"}}} +{"cn":{"0596527055":{"label":"The Myths of In...","full-label":"The Myths of Innovation"}}} +{"ae":{"0596510047_0596527055":{"source":"0596510047","directed":false,"target":"0596527055"}}} +{"ae":{"0596529325_0596527055":{"source":"0596529325","directed":false,"target":"0596527055"}}} +{"ae":{"0321525655_0596527055":{"source":"0321525655","directed":false,"target":"0596527055"}}} +{"cn":{"0071440011":{"label":"How to Ace the ...","full-label":"How to Ace the Brainteaser Interview"}}} +{"ae":{"047012167X_0071440011":{"source":"047012167X","directed":false,"target":"0071440011"}}} +{"ae":{"0071387730_0071440011":{"source":"0071387730","directed":false,"target":"0071440011"}}} +{"an":{"0971015848":{"label":"How to Ace the ...","full-label":"How to Ace the Brainteaser Interview"}}} +{"ae":{"0071440011_0971015848":{"source":"0071440011","directed":false,"target":"0971015848"}}} +{"cn":{"0385242719":{"label":"Labyrinths of R...","full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge"}}} +{"an":{"038541580X":{"label":"Labyrinths of R...","full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge"}}} +{"ae":{"0385242719_038541580X":{"source":"0385242719","directed":false,"target":"038541580X"}}} +{"an":{"0809045990":{"label":"Labyrinths of R...","full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge"}}} +{"ae":{"0385242719_0809045990":{"source":"0385242719","directed":false,"target":"0809045990"}}} +{"an":{"0521483476":{"label":"Labyrinths of R...","full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge"}}} +{"ae":{"0385242719_0521483476":{"source":"0385242719","directed":false,"target":"0521483476"}}} +{"cn":{"0970055269":{"label":"Heard on the St...","full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews"}}} +{"an":{"0131499084":{"label":"Heard on the St...","full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews"}}} +{"ae":{"0970055269_0131499084":{"source":"0970055269","directed":false,"target":"0131499084"}}} +{"an":{"1581311729":{"label":"Heard on the St...","full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews"}}} +{"ae":{"0970055269_1581311729":{"source":"0970055269","directed":false,"target":"1581311729"}}} +{"an":{"0387401016":{"label":"Heard on the St...","full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews"}}} +{"ae":{"0970055269_0387401016":{"source":"0970055269","directed":false,"target":"0387401016"}}} +{"an":{"0470192739":{"label":"Heard on the St...","full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews"}}} +{"ae":{"0970055269_0470192739":{"source":"0970055269","directed":false,"target":"0470192739"}}} +{"an":{"1432706810":{"label":"Heard on the St...","full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews"}}} +{"ae":{"0970055269_1432706810":{"source":"0970055269","directed":false,"target":"1432706810"}}} +{"cn":{"0071492747":{"label":"ACE the IT Resu...","full-label":"ACE the IT Resume (Ace the It Resume)"}}} +{"ae":{"047012167X_0071492747":{"source":"047012167X","directed":false,"target":"0071492747"}}} +{"an":{"1593573111":{"label":"ACE the IT Resu...","full-label":"ACE the IT Resume (Ace the It Resume)"}}} +{"ae":{"0071492747_1593573111":{"source":"0071492747","directed":false,"target":"1593573111"}}} +{"an":{"1593571275":{"label":"ACE the IT Resu...","full-label":"ACE the IT Resume (Ace the It Resume)"}}} +{"ae":{"0071492747_1593571275":{"source":"0071492747","directed":false,"target":"1593571275"}}} +{"an":{"1587131560":{"label":"ACE the IT Resu...","full-label":"ACE the IT Resume (Ace the It Resume)"}}} +{"ae":{"0071492747_1587131560":{"source":"0071492747","directed":false,"target":"1587131560"}}} +{"cn":{"0071425470":{"label":"Boost Your Inte...","full-label":"Boost Your Interview IQ"}}} +{"an":{"0071449825":{"label":"Boost Your Inte...","full-label":"Boost Your Interview IQ"}}} +{"ae":{"0071425470_0071449825":{"source":"0071425470","directed":false,"target":"0071449825"}}} +{"an":{"0970901224":{"label":"Boost Your Inte...","full-label":"Boost Your Interview IQ"}}} +{"ae":{"0071425470_0970901224":{"source":"0071425470","directed":false,"target":"0970901224"}}} +{"ae":{"1402203853_0071425470":{"source":"1402203853","directed":false,"target":"0071425470"}}} +{"an":{"1564148696":{"label":"Boost Your Inte...","full-label":"Boost Your Interview IQ"}}} +{"ae":{"0071425470_1564148696":{"source":"0071425470","directed":false,"target":"1564148696"}}} +{"cn":{"0071387730":{"label":"201 Best Questi...","full-label":"201 Best Questions To Ask On Your Interview"}}} +{"an":{"007016357X":{"label":"201 Best Questi...","full-label":"201 Best Questions To Ask On Your Interview"}}} +{"ae":{"0071387730_007016357X":{"source":"0071387730","directed":false,"target":"007016357X"}}} +{"ae":{"1402203853_0071387730":{"source":"1402203853","directed":false,"target":"0071387730"}}} +{"ae":{"1593573111_0071387730":{"source":"1593573111","directed":false,"target":"0071387730"}}} +{"an":{"007141827X":{"label":"201 Best Questi...","full-label":"201 Best Questions To Ask On Your Interview"}}} +{"ae":{"0071387730_007141827X":{"source":"0071387730","directed":false,"target":"007141827X"}}} +{"an":{"1418040002":{"label":"201 Best Questi...","full-label":"201 Best Questions To Ask On Your Interview"}}} +{"ae":{"0071387730_1418040002":{"source":"0071387730","directed":false,"target":"1418040002"}}} +{"cn":{"1402203853":{"label":"301 Smart Answe...","full-label":"301 Smart Answers to Tough Interview Questions"}}} +{"ae":{"007016357X_1402203853":{"source":"007016357X","directed":false,"target":"1402203853"}}} +{"an":{"1564147789":{"label":"301 Smart Answe...","full-label":"301 Smart Answers to Tough Interview Questions"}}} +{"ae":{"1402203853_1564147789":{"source":"1402203853","directed":false,"target":"1564147789"}}} +{"ae":{"1564148696_1402203853":{"source":"1564148696","directed":false,"target":"1402203853"}}} +{"ae":{"007141827X_1402203853":{"source":"007141827X","directed":false,"target":"1402203853"}}} +{"cn":{"1572316217":{"label":"Software Projec...","full-label":"Software Project Survival Guide (Pro -- Best Practices)"}}} +{"ae":{"0735605351_1572316217":{"source":"0735605351","directed":false,"target":"1572316217"}}} +{"ae":{"0735619670_1572316217":{"source":"0735619670","directed":false,"target":"1572316217"}}} +{"ae":{"0201835959_1572316217":{"source":"0201835959","directed":false,"target":"1572316217"}}} +{"ae":{"0735618798_1572316217":{"source":"0735618798","directed":false,"target":"1572316217"}}} +{"cn":{"0735618798":{"label":"Software Requir...","full-label":"Software Requirements, Second Edition (Pro-Best Practices)"}}} +{"an":{"0735622671":{"label":"Software Requir...","full-label":"Software Requirements, Second Edition (Pro-Best Practices)"}}} +{"ae":{"0735618798_0735622671":{"source":"0735618798","directed":false,"target":"0735622671"}}} +{"ae":{"0201702258_0735618798":{"source":"0201702258","directed":false,"target":"0735618798"}}} +{"an":{"1576810607":{"label":"Software Requir...","full-label":"Software Requirements, Second Edition (Pro-Best Practices)"}}} +{"ae":{"0735618798_1576810607":{"source":"0735618798","directed":false,"target":"1576810607"}}} +{"ae":{"0735619670_0735618798":{"source":"0735619670","directed":false,"target":"0735618798"}}} +{"cn":{"0131479415":{"label":"Agile Estimatin...","full-label":"Agile Estimating and Planning (Robert C. Martin Series)"}}} +{"ae":{"0321205685_0131479415":{"source":"0321205685","directed":false,"target":"0131479415"}}} +{"an":{"073561993X":{"label":"Agile Estimatin...","full-label":"Agile Estimating and Planning (Robert C. Martin Series)"}}} +{"ae":{"0131479415_073561993X":{"source":"0131479415","directed":false,"target":"073561993X"}}} +{"an":{"0130676349":{"label":"Agile Estimatin...","full-label":"Agile Estimating and Planning (Robert C. Martin Series)"}}} +{"ae":{"0131479415_0130676349":{"source":"0131479415","directed":false,"target":"0130676349"}}} +{"an":{"0131111558":{"label":"Agile Estimatin...","full-label":"Agile Estimating and Planning (Robert C. Martin Series)"}}} +{"ae":{"0131479415_0131111558":{"source":"0131479415","directed":false,"target":"0131111558"}}} +{"an":{"0977616649":{"label":"Agile Estimatin...","full-label":"Agile Estimating and Planning (Robert C. Martin Series)"}}} +{"ae":{"0131479415_0977616649":{"source":"0131479415","directed":false,"target":"0977616649"}}} +{"cn":{"0132017997":{"label":"Design of the U...","full-label":"Design of the UNIX Operating System (Prentice Hall Software Series)"}}} +{"ae":{"0131103628_0132017997":{"source":"0131103628","directed":false,"target":"0132017997"}}} +{"an":{"0131019082":{"label":"Design of the U...","full-label":"Design of the UNIX Operating System (Prentice Hall Software Series)"}}} +{"ae":{"0132017997_0131019082":{"source":"0132017997","directed":false,"target":"0131019082"}}} +{"an":{"0131429388":{"label":"Design of the U...","full-label":"Design of the UNIX Operating System (Prentice Hall Software Series)"}}} +{"ae":{"0132017997_0131429388":{"source":"0132017997","directed":false,"target":"0131429388"}}} +{"ae":{"0201433079_0132017997":{"source":"0201433079","directed":false,"target":"0132017997"}}} +{"cn":{"020103669X":{"label":"Software Tools","full-label":"Software Tools"}}} +{"ae":{"020161586X_020103669X":{"source":"020161586X","directed":false,"target":"020103669X"}}} +{"ae":{"0131103628_020103669X":{"source":"0131103628","directed":false,"target":"020103669X"}}} +{"an":{"020107981X":{"label":"Software Tools","full-label":"Software Tools"}}} +{"ae":{"020103669X_020107981X":{"source":"020103669X","directed":false,"target":"020107981X"}}} +{"ae":{"0132017997_020103669X":{"source":"0132017997","directed":false,"target":"020103669X"}}} +{"cn":{"1590593898":{"label":"Joel on Softwar...","full-label":"Joel on Software: And on Diverse and Occasionally Related Matters That Will Prove of Interest to Software Developers, Designers, and Managers, and to Those Who, Whether by Good Fortune or Ill Luck, Work with Them in Some Capacity"}}} +{"an":{"1590595009":{"label":"Joel on Softwar...","full-label":"Joel on Software: And on Diverse and Occasionally Related Matters That Will Prove of Interest to Software Developers, Designers, and Managers, and to Those Who, Whether by Good Fortune or Ill Luck, Work with Them in Some Capacity"}}} +{"ae":{"1590593898_1590595009":{"source":"1590593898","directed":false,"target":"1590595009"}}} +{"ae":{"0201835959_1590593898":{"source":"0201835959","directed":false,"target":"1590593898"}}} +{"an":{"1893115941":{"label":"Joel on Softwar...","full-label":"Joel on Software: And on Diverse and Occasionally Related Matters That Will Prove of Interest to Software Developers, Designers, and Managers, and to Those Who, Whether by Good Fortune or Ill Luck, Work with Them in Some Capacity"}}} +{"ae":{"1590593898_1893115941":{"source":"1590593898","directed":false,"target":"1893115941"}}} +{"cn":{"0387402721":{"label":"All of Statisti...","full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)"}}} +{"an":{"0387251456":{"label":"All of Statisti...","full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)"}}} +{"ae":{"0387402721_0387251456":{"source":"0387402721","directed":false,"target":"0387251456"}}} +{"ae":{"0387310738_0387402721":{"source":"0387310738","directed":false,"target":"0387402721"}}} +{"an":{"0534243126":{"label":"All of Statisti...","full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)"}}} +{"ae":{"0387402721_0534243126":{"source":"0387402721","directed":false,"target":"0534243126"}}} +{"an":{"052168689X":{"label":"All of Statisti...","full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)"}}} +{"ae":{"0387402721_052168689X":{"source":"0387402721","directed":false,"target":"052168689X"}}} +{"cn":{"158488388X":{"label":"Bayesian Data A...","full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)"}}} +{"ae":{"052168689X_158488388X":{"source":"052168689X","directed":false,"target":"158488388X"}}} +{"an":{"0387212396":{"label":"Bayesian Data A...","full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)"}}} +{"ae":{"158488388X_0387212396":{"source":"158488388X","directed":false,"target":"0387212396"}}} +{"an":{"0387713840":{"label":"Bayesian Data A...","full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)"}}} +{"ae":{"158488388X_0387713840":{"source":"158488388X","directed":false,"target":"0387713840"}}} +{"an":{"0340814055":{"label":"Bayesian Data A...","full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)"}}} +{"ae":{"158488388X_0340814055":{"source":"158488388X","directed":false,"target":"0340814055"}}} +{"cn":{"0471429775":{"label":"Computer Manual...","full-label":"Computer Manual in MATLAB to Accompany Pattern Classification, Second Edition"}}} +{"ae":{"0387310738_0471429775":{"source":"0387310738","directed":false,"target":"0471429775"}}} +{"ae":{"0387952845_0471429775":{"source":"0387952845","directed":false,"target":"0471429775"}}} +{"an":{"0130085197":{"label":"Computer Manual...","full-label":"Computer Manual in MATLAB to Accompany Pattern Classification, Second Edition"}}} +{"ae":{"0471429775_0130085197":{"source":"0471429775","directed":false,"target":"0130085197"}}} +{"an":{"0198538642":{"label":"Computer Manual...","full-label":"Computer Manual in MATLAB to Accompany Pattern Classification, Second Edition"}}} +{"ae":{"0471429775_0198538642":{"source":"0471429775","directed":false,"target":"0198538642"}}} +{"cn":{"1558609016":{"label":"Data Mining: Co...","full-label":"Data Mining: Concepts and Techniques, Second Edition (The Morgan Kaufmann Series in Data Management Systems)"}}} +{"an":{"0321321367":{"label":"Data Mining: Co...","full-label":"Data Mining: Concepts and Techniques, Second Edition (The Morgan Kaufmann Series in Data Management Systems)"}}} +{"ae":{"1558609016_0321321367":{"source":"1558609016","directed":false,"target":"0321321367"}}} +{"an":{"0321356985":{"label":"Data Mining: Co...","full-label":"Data Mining: Concepts and Techniques, Second Edition (The Morgan Kaufmann Series in Data Management Systems)"}}} +{"ae":{"1558609016_0321356985":{"source":"1558609016","directed":false,"target":"0321356985"}}} +{"ae":{"0387952845_1558609016":{"source":"0387952845","directed":false,"target":"1558609016"}}} +{"ae":{"0596529325_1558609016":{"source":"0596529325","directed":false,"target":"1558609016"}}} +{"cn":{"0262012111":{"label":"Introduction to...","full-label":"Introduction to Machine Learning (Adaptive Computation and Machine Learning)"}}} +{"ae":{"0387310738_0262012111":{"source":"0387310738","directed":false,"target":"0262012111"}}} +{"ae":{"0071154671_0262012111":{"source":"0071154671","directed":false,"target":"0262012111"}}} +{"ae":{"0387952845_0262012111":{"source":"0387952845","directed":false,"target":"0262012111"}}} +{"ae":{"0471056693_0262012111":{"source":"0471056693","directed":false,"target":"0262012111"}}} +{"cn":{"0262194759":{"label":"Learning with K...","full-label":"Learning with Kernels: Support Vector Machines, Regularization, Optimization, and Beyond (Adaptive Computation and Machine Learning)"}}} +{"ae":{"0387310738_0262194759":{"source":"0387310738","directed":false,"target":"0262194759"}}} +{"ae":{"0387952845_0262194759":{"source":"0387952845","directed":false,"target":"0262194759"}}} +{"an":{"0521780195":{"label":"Learning with K...","full-label":"Learning with Kernels: Support Vector Machines, Regularization, Optimization, and Beyond (Adaptive Computation and Machine Learning)"}}} +{"ae":{"0262194759_0521780195":{"source":"0262194759","directed":false,"target":"0521780195"}}} +{"an":{"0521813972":{"label":"Learning with K...","full-label":"Learning with Kernels: Support Vector Machines, Regularization, Optimization, and Beyond (Adaptive Computation and Machine Learning)"}}} +{"ae":{"0262194759_0521813972":{"source":"0262194759","directed":false,"target":"0521813972"}}} +{"cn":{"0262033585":{"label":"Semi-Supervised...","full-label":"Semi-Supervised Learning (Adaptive Computation and Machine Learning)"}}} +{"ae":{"0387310738_0262033585":{"source":"0387310738","directed":false,"target":"0262033585"}}} +{"ae":{"0262194759_0262033585":{"source":"0262194759","directed":false,"target":"0262033585"}}} +{"an":{"0262026171":{"label":"Semi-Supervised...","full-label":"Semi-Supervised Learning (Adaptive Computation and Machine Learning)"}}} +{"ae":{"0262033585_0262026171":{"source":"0262033585","directed":false,"target":"0262026171"}}} +{"an":{"0262026252":{"label":"Semi-Supervised...","full-label":"Semi-Supervised Learning (Adaptive Computation and Machine Learning)"}}} +{"ae":{"0262033585_0262026252":{"source":"0262033585","directed":false,"target":"0262026252"}}} +{"cn":{"0521642981":{"label":"Information The...","full-label":"Information Theory, Inference & Learning Algorithms"}}} +{"ae":{"0387310738_0521642981":{"source":"0387310738","directed":false,"target":"0521642981"}}} +{"an":{"0521592712":{"label":"Information The...","full-label":"Information Theory, Inference & Learning Algorithms"}}} +{"ae":{"0521642981_0521592712":{"source":"0521642981","directed":false,"target":"0521592712"}}} +{"an":{"0471241954":{"label":"Information The...","full-label":"Information Theory, Inference & Learning Algorithms"}}} +{"ae":{"0521642981_0471241954":{"source":"0521642981","directed":false,"target":"0471241954"}}} +{"ae":{"0387952845_0521642981":{"source":"0387952845","directed":false,"target":"0521642981"}}} +{"cn":{"0486247759":{"label":"Introductory Gr...","full-label":"Introductory Graph Theory"}}} +{"ae":{"0486678709_0486247759":{"source":"0486678709","directed":false,"target":"0486247759"}}} +{"an":{"0387984887":{"label":"Introductory Gr...","full-label":"Introductory Graph Theory"}}} +{"ae":{"0486247759_0387984887":{"source":"0486247759","directed":false,"target":"0387984887"}}} +{"an":{"0486432327":{"label":"Introductory Gr...","full-label":"Introductory Graph Theory"}}} +{"ae":{"0486247759_0486432327":{"source":"0486247759","directed":false,"target":"0486432327"}}} +{"an":{"0070054894":{"label":"Introductory Gr...","full-label":"Introductory Graph Theory"}}} +{"ae":{"0486247759_0070054894":{"source":"0486247759","directed":false,"target":"0070054894"}}} +{"cn":{"0486678709":{"label":"Introduction to...","full-label":"Introduction to Graph Theory (Dover Books on Advanced Mathematics)"}}} +{"an":{"0199218420":{"label":"Introduction to...","full-label":"Introduction to Graph Theory (Dover Books on Advanced Mathematics)"}}} +{"ae":{"0486678709_0199218420":{"source":"0486678709","directed":false,"target":"0199218420"}}} +{"ae":{"0070054894_0486678709":{"source":"0070054894","directed":false,"target":"0486678709"}}} +{"ae":{"0486432327_0486678709":{"source":"0486432327","directed":false,"target":"0486678709"}}} +{"cn":{"0521474655":{"label":"Randomized Algo...","full-label":"Randomized Algorithms"}}} +{"ae":{"0521835402_0521474655":{"source":"0521835402","directed":false,"target":"0521474655"}}} +{"ae":{"0486402584_0521474655":{"source":"0486402584","directed":false,"target":"0521474655"}}} +{"ae":{"0716710455_0521474655":{"source":"0716710455","directed":false,"target":"0521474655"}}} +{"ae":{"0521872820_0521474655":{"source":"0521872820","directed":false,"target":"0521474655"}}} +{"cn":{"0521872820":{"label":"Algorithmic Gam...","full-label":"Algorithmic Game Theory"}}} +{"an":{"0674341163":{"label":"Algorithmic Gam...","full-label":"Algorithmic Game Theory"}}} +{"ae":{"0521872820_0674341163":{"source":"0521872820","directed":false,"target":"0674341163"}}} +{"an":{"0262033429":{"label":"Algorithmic Gam...","full-label":"Algorithmic Game Theory"}}} +{"ae":{"0521872820_0262033429":{"source":"0521872820","directed":false,"target":"0262033429"}}} +{"ae":{"0486402584_0521872820":{"source":"0486402584","directed":false,"target":"0521872820"}}} +{"an":{"0262650401":{"label":"Algorithmic Gam...","full-label":"Algorithmic Game Theory"}}} +{"ae":{"0521872820_0262650401":{"source":"0521872820","directed":false,"target":"0262650401"}}} +{"cn":{"0521835402":{"label":"Probability and...","full-label":"Probability and Computing: Randomized Algorithms and Probabilistic Analysis"}}} +{"ae":{"0521872820_0521835402":{"source":"0521872820","directed":false,"target":"0521835402"}}} +{"ae":{"0262032937_0521835402":{"source":"0262032937","directed":false,"target":"0521835402"}}} +{"ae":{"0486402584_0521835402":{"source":"0486402584","directed":false,"target":"0521835402"}}} +{"cn":{"0534939724":{"label":"Compiler Constr...","full-label":"Compiler Construction: Principles and Practice"}}} +{"ae":{"0321486811_0534939724":{"source":"0321486811","directed":false,"target":"0534939724"}}} +{"ae":{"0126339511_0534939724":{"source":"0126339511","directed":false,"target":"0534939724"}}} +{"ae":{"0534950973_0534939724":{"source":"0534950973","directed":false,"target":"0534939724"}}} +{"ae":{"013089592X_0534939724":{"source":"013089592X","directed":false,"target":"0534939724"}}} +{"cn":{"B00007FYIJ":{"label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)"}}} +{"an":{"0596005954":{"label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)"}}} +{"ae":{"B00007FYIJ_0596005954":{"source":"B00007FYIJ","directed":false,"target":"0596005954"}}} +{"an":{"0596528124":{"label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)"}}} +{"ae":{"B00007FYIJ_0596528124":{"source":"B00007FYIJ","directed":false,"target":"0596528124"}}} +{"an":{"0596009658":{"label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)"}}} +{"ae":{"B00007FYIJ_0596009658":{"source":"B00007FYIJ","directed":false,"target":"0596009658"}}} +{"an":{"B00007FYGT":{"label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)"}}} +{"ae":{"B00007FYIJ_B00007FYGT":{"source":"B00007FYIJ","directed":false,"target":"B00007FYGT"}}} +{"an":{"0596003528":{"label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)"}}} +{"ae":{"B00007FYIJ_0596003528":{"source":"B00007FYIJ","directed":false,"target":"0596003528"}}} +{"cn":{"155860698X":{"label":"Engineering a C...","full-label":"Engineering a Compiler"}}} +{"ae":{"0321486811_155860698X":{"source":"0321486811","directed":false,"target":"155860698X"}}} +{"an":{"1558603204":{"label":"Engineering a C...","full-label":"Engineering a Compiler"}}} +{"ae":{"155860698X_1558603204":{"source":"155860698X","directed":false,"target":"1558603204"}}} +{"an":{"1558602860":{"label":"Engineering a C...","full-label":"Engineering a Compiler"}}} +{"ae":{"155860698X_1558602860":{"source":"155860698X","directed":false,"target":"1558602860"}}} +{"ae":{"B00007FYCY_155860698X":{"source":"B00007FYCY","directed":false,"target":"155860698X"}}} +{"cn":{"0072253517":{"label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)"}}} +{"an":{"0072231459":{"label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)"}}} +{"ae":{"0072253517_0072231459":{"source":"0072253517","directed":false,"target":"0072231459"}}} +{"an":{"0072230665":{"label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)"}}} +{"ae":{"0072253517_0072230665":{"source":"0072253517","directed":false,"target":"0072230665"}}} +{"an":{"0072263172":{"label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)"}}} +{"ae":{"0072253517_0072263172":{"source":"0072253517","directed":false,"target":"0072263172"}}} +{"an":{"0072263059":{"label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)"}}} +{"ae":{"0072253517_0072263059":{"source":"0072253517","directed":false,"target":"0072263059"}}} +{"an":{"0072230789":{"label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)"}}} +{"ae":{"0072253517_0072230789":{"source":"0072253517","directed":false,"target":"0072230789"}}} +{"cn":{"1593270038":{"label":"Write Great Cod...","full-label":"Write Great Code: Volume 1: Understanding the Machine"}}} +{"ae":{"1593270658_1593270038":{"source":"1593270658","directed":false,"target":"1593270038"}}} +{"an":{"1886411972":{"label":"Write Great Cod...","full-label":"Write Great Code: Volume 1: Understanding the Machine"}}} +{"ae":{"1593270038_1886411972":{"source":"1593270038","directed":false,"target":"1886411972"}}} +{"an":{"0521520436":{"label":"Write Great Cod...","full-label":"Write Great Code: Volume 1: Understanding the Machine"}}} +{"ae":{"1593270038_0521520436":{"source":"1593270038","directed":false,"target":"0521520436"}}} +{"ae":{"0735619670_1593270038":{"source":"0735619670","directed":false,"target":"1593270038"}}} +{"cn":{"1593270658":{"label":"Write Great Cod...","full-label":"Write Great Code, Volume 2: Thinking Low-Level, Writing High-Level"}}} +{"ae":{"1886411972_1593270658":{"source":"1886411972","directed":false,"target":"1593270658"}}} +{"ae":{"0521520436_1593270658":{"source":"0521520436","directed":false,"target":"1593270658"}}} +{"an":{"1593270569":{"label":"Write Great Cod...","full-label":"Write Great Code, Volume 2: Thinking Low-Level, Writing High-Level"}}} +{"ae":{"1593270658_1593270569":{"source":"1593270658","directed":false,"target":"1593270569"}}} +{"cn":{"0672326663":{"label":"Programming in ...","full-label":"Programming in C (3rd Edition) (Developer's Library)"}}} +{"ae":{"0131103628_0672326663":{"source":"0131103628","directed":false,"target":"0672326663"}}} +{"ae":{"0672326965_0672326663":{"source":"0672326965","directed":false,"target":"0672326663"}}} +{"an":{"0672325861":{"label":"Programming in ...","full-label":"Programming in C (3rd Edition) (Developer's Library)"}}} +{"ae":{"0672326663_0672325861":{"source":"0672326663","directed":false,"target":"0672325861"}}} +{"cn":{"0201179288":{"label":"C Traps and Pit...","full-label":"C Traps and Pitfalls"}}} +{"ae":{"0131774298_0201179288":{"source":"0131774298","directed":false,"target":"0201179288"}}} +{"an":{"0201604612":{"label":"C Traps and Pit...","full-label":"C Traps and Pitfalls"}}} +{"ae":{"0201179288_0201604612":{"source":"0201179288","directed":false,"target":"0201604612"}}} +{"an":{"0673999866":{"label":"C Traps and Pit...","full-label":"C Traps and Pitfalls"}}} +{"ae":{"0201179288_0673999866":{"source":"0201179288","directed":false,"target":"0673999866"}}} +{"ae":{"0131103628_0201179288":{"source":"0131103628","directed":false,"target":"0201179288"}}} +{"cn":{"0131774298":{"label":"Expert C Progra...","full-label":"Expert C Programming"}}} +{"ae":{"0131103628_0131774298":{"source":"0131103628","directed":false,"target":"0131774298"}}} +{"ae":{"0201604612_0131774298":{"source":"0201604612","directed":false,"target":"0131774298"}}} +{"ae":{"0673999866_0131774298":{"source":"0673999866","directed":false,"target":"0131774298"}}} +{"cn":{"0764570684":{"label":"C For Dummies, ...","full-label":"C For Dummies, 2nd Edition"}}} +{"ae":{"0764570692_0764570684":{"source":"0764570692","directed":false,"target":"0764570684"}}} +{"an":{"0764568523":{"label":"C For Dummies, ...","full-label":"C For Dummies, 2nd Edition"}}} +{"ae":{"0764570684_0764568523":{"source":"0764570684","directed":false,"target":"0764568523"}}} +{"ae":{"0131103628_0764570684":{"source":"0131103628","directed":false,"target":"0764570684"}}} +{"an":{"0470088702":{"label":"C For Dummies, ...","full-label":"C For Dummies, 2nd Edition"}}} +{"ae":{"0764570684_0470088702":{"source":"0764570684","directed":false,"target":"0470088702"}}} +{"cn":{"0672326965":{"label":"C Primer Plus (...","full-label":"C Primer Plus (5th Edition)"}}} +{"an":{"0672326973":{"label":"C Primer Plus (...","full-label":"C Primer Plus (5th Edition)"}}} +{"ae":{"0672326965_0672326973":{"source":"0672326965","directed":false,"target":"0672326973"}}} +{"ae":{"0131103628_0672326965":{"source":"0131103628","directed":false,"target":"0672326965"}}} +{"ae":{"013089592X_0672326965":{"source":"013089592X","directed":false,"target":"0672326965"}}} +{"cn":{"0764570692":{"label":"C All-in-One De...","full-label":"C All-in-One Desk Reference for Dummies"}}} +{"an":{"0764517953":{"label":"C All-in-One De...","full-label":"C All-in-One Desk Reference for Dummies"}}} +{"ae":{"0764570692_0764517953":{"source":"0764570692","directed":false,"target":"0764517953"}}} +{"ae":{"0131103628_0764570692":{"source":"0131103628","directed":false,"target":"0764570692"}}} +{"an":{"0470124512":{"label":"C All-in-One De...","full-label":"C All-in-One Desk Reference for Dummies"}}} +{"ae":{"0764570692_0470124512":{"source":"0764570692","directed":false,"target":"0470124512"}}} +{"cn":{"0131411551":{"label":"Unix Network Pr...","full-label":"Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0130810819_0131411551":{"source":"0130810819","directed":false,"target":"0131411551"}}} +{"an":{"020163354X":{"label":"Unix Network Pr...","full-label":"Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0131411551_020163354X":{"source":"0131411551","directed":false,"target":"020163354X"}}} +{"an":{"0201633469":{"label":"Unix Network Pr...","full-label":"Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0131411551_0201633469":{"source":"0131411551","directed":false,"target":"0201633469"}}} +{"ae":{"0596005652_0131411551":{"source":"0596005652","directed":false,"target":"0131411551"}}} +{"cn":{"0130810819":{"label":"UNIX Network Pr...","full-label":"UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition) (The Unix Networking Reference Series , Vol 2)"}}} +{"ae":{"020163354X_0130810819":{"source":"020163354X","directed":false,"target":"0130810819"}}} +{"an":{"013490012X":{"label":"UNIX Network Pr...","full-label":"UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition) (The Unix Networking Reference Series , Vol 2)"}}} +{"ae":{"0130810819_013490012X":{"source":"0130810819","directed":false,"target":"013490012X"}}} +{"an":{"0201633922":{"label":"UNIX Network Pr...","full-label":"UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition) (The Unix Networking Reference Series , Vol 2)"}}} +{"ae":{"0130810819_0201633922":{"source":"0130810819","directed":false,"target":"0201633922"}}} +{"cn":{"0131411543":{"label":"Advanced UNIX P...","full-label":"Advanced UNIX Programming (2nd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0131411551_0131411543":{"source":"0131411551","directed":false,"target":"0131411543"}}} +{"ae":{"0130810819_0131411543":{"source":"0130810819","directed":false,"target":"0131411543"}}} +{"ae":{"0131103628_0131411543":{"source":"0131103628","directed":false,"target":"0131411543"}}} +{"an":{"0130424110":{"label":"Advanced UNIX P...","full-label":"Advanced UNIX Programming (2nd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0131411543_0130424110":{"source":"0131411543","directed":false,"target":"0130424110"}}} +{"cn":{"0596005652":{"label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition"}}} +{"an":{"B00006AVQ0":{"label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition"}}} +{"ae":{"0596005652_B00006AVQ0":{"source":"0596005652","directed":false,"target":"B00006AVQ0"}}} +{"an":{"0596002556":{"label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition"}}} +{"ae":{"0596005652_0596002556":{"source":"0596005652","directed":false,"target":"0596002556"}}} +{"an":{"0672327201":{"label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition"}}} +{"ae":{"0596005652_0672327201":{"source":"0596005652","directed":false,"target":"0672327201"}}} +{"an":{"0131679848":{"label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition"}}} +{"ae":{"0596005652_0131679848":{"source":"0596005652","directed":false,"target":"0131679848"}}} +{"an":{"059600222X":{"label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition"}}} +{"ae":{"0596005652_059600222X":{"source":"0596005652","directed":false,"target":"059600222X"}}} +{"cn":{"0321278658":{"label":"Extreme Program...","full-label":"Extreme Programming Explained: Embrace Change (2nd Edition) (The XP Series)"}}} +{"ae":{"0130676349_0321278658":{"source":"0130676349","directed":false,"target":"0321278658"}}} +{"an":{"0201710919":{"label":"Extreme Program...","full-label":"Extreme Programming Explained: Embrace Change (2nd Edition) (The XP Series)"}}} +{"ae":{"0321278658_0201710919":{"source":"0321278658","directed":false,"target":"0201710919"}}} +{"ae":{"073561993X_0321278658":{"source":"073561993X","directed":false,"target":"0321278658"}}} +{"ae":{"0201485672_0321278658":{"source":"0201485672","directed":false,"target":"0321278658"}}} +{"cn":{"0321205685":{"label":"User Stories Ap...","full-label":"User Stories Applied: For Agile Software Development (The Addison-Wesley Signature Series)"}}} +{"ae":{"073561993X_0321205685":{"source":"073561993X","directed":false,"target":"0321205685"}}} +{"ae":{"0130676349_0321205685":{"source":"0130676349","directed":false,"target":"0321205685"}}} +{"ae":{"0977616649_0321205685":{"source":"0977616649","directed":false,"target":"0321205685"}}} +{"ae":{"0131111558_0321205685":{"source":"0131111558","directed":false,"target":"0321205685"}}} +{"cn":{"0131177052":{"label":"Working Effecti...","full-label":"Working Effectively with Legacy Code (Robert C. Martin Series)"}}} +{"ae":{"0201485672_0131177052":{"source":"0201485672","directed":false,"target":"0131177052"}}} +{"an":{"0131495054":{"label":"Working Effecti...","full-label":"Working Effectively with Legacy Code (Robert C. Martin Series)"}}} +{"ae":{"0131177052_0131495054":{"source":"0131177052","directed":false,"target":"0131495054"}}} +{"an":{"0321213351":{"label":"Working Effecti...","full-label":"Working Effectively with Legacy Code (Robert C. Martin Series)"}}} +{"ae":{"0131177052_0321213351":{"source":"0131177052","directed":false,"target":"0321213351"}}} +{"ae":{"020161622X_0131177052":{"source":"020161622X","directed":false,"target":"0131177052"}}} +{"cn":{"0321200683":{"label":"Enterprise Inte...","full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)"}}} +{"an":{"0131858580":{"label":"Enterprise Inte...","full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)"}}} +{"ae":{"0321200683_0131858580":{"source":"0321200683","directed":false,"target":"0131858580"}}} +{"an":{"0596006756":{"label":"Enterprise Inte...","full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)"}}} +{"ae":{"0321200683_0596006756":{"source":"0321200683","directed":false,"target":"0596006756"}}} +{"ae":{"0321125215_0321200683":{"source":"0321125215","directed":false,"target":"0321200683"}}} +{"an":{"0132344823":{"label":"Enterprise Inte...","full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)"}}} +{"ae":{"0321200683_0132344823":{"source":"0321200683","directed":false,"target":"0132344823"}}} +{"cn":{"0321125215":{"label":"Domain-Driven D...","full-label":"Domain-Driven Design: Tackling Complexity in the Heart of Software"}}} +{"an":{"0321268202":{"label":"Domain-Driven D...","full-label":"Domain-Driven Design: Tackling Complexity in the Heart of Software"}}} +{"ae":{"0321125215_0321268202":{"source":"0321125215","directed":false,"target":"0321268202"}}} +{"ae":{"0201485672_0321125215":{"source":"0201485672","directed":false,"target":"0321125215"}}} +{"ae":{"0321146530_0321125215":{"source":"0321146530","directed":false,"target":"0321125215"}}} +{"cn":{"0596527357":{"label":"Head First Soft...","full-label":"Head First Software Development (Brain-Friendly Guides)"}}} +{"an":{"0596526849":{"label":"Head First Soft...","full-label":"Head First Software Development (Brain-Friendly Guides)"}}} +{"ae":{"0596527357_0596526849":{"source":"0596527357","directed":false,"target":"0596526849"}}} +{"an":{"0596514824":{"label":"Head First Soft...","full-label":"Head First Software Development (Brain-Friendly Guides)"}}} +{"ae":{"0596527357_0596514824":{"source":"0596527357","directed":false,"target":"0596514824"}}} +{"ae":{"0596007124_0596527357":{"source":"0596007124","directed":false,"target":"0596527357"}}} +{"an":{"0596527748":{"label":"Head First Soft...","full-label":"Head First Software Development (Brain-Friendly Guides)"}}} +{"ae":{"0596527357_0596527748":{"source":"0596527357","directed":false,"target":"0596527748"}}} +{"cn":{"059610197X":{"label":"Head First HTML...","full-label":"Head First HTML with CSS & XHTML"}}} +{"ae":{"0596527748_059610197X":{"source":"0596527748","directed":false,"target":"059610197X"}}} +{"an":{"0596102259":{"label":"Head First HTML...","full-label":"Head First HTML with CSS & XHTML"}}} +{"ae":{"059610197X_0596102259":{"source":"059610197X","directed":false,"target":"0596102259"}}} +{"ae":{"0596526849_059610197X":{"source":"0596526849","directed":false,"target":"059610197X"}}} +{"ae":{"0596007124_059610197X":{"source":"0596007124","directed":false,"target":"059610197X"}}} +{"cn":{"0596005717":{"label":"Head First EJB ...","full-label":"Head First EJB (Brain-Friendly Study Guides; Enterprise JavaBeans)"}}} +{"ae":{"0596007124_0596005717":{"source":"0596007124","directed":false,"target":"0596005717"}}} +{"ae":{"0596008678_0596005717":{"source":"0596008678","directed":false,"target":"0596005717"}}} +{"ae":{"0072253606_0596005717":{"source":"0072253606","directed":false,"target":"0596005717"}}} +{"cn":{"0072253606":{"label":"SCJP Sun Certif...","full-label":"SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press Study Guides)"}}} +{"an":{"1590596978":{"label":"SCJP Sun Certif...","full-label":"SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press Study Guides)"}}} +{"ae":{"0072253606_1590596978":{"source":"0072253606","directed":false,"target":"1590596978"}}} +{"ae":{"0596009208_0072253606":{"source":"0596009208","directed":false,"target":"0072253606"}}} +{"ae":{"0596007124_0072253606":{"source":"0596007124","directed":false,"target":"0072253606"}}} +{"an":{"1933988134":{"label":"SCJP Sun Certif...","full-label":"SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press Study Guides)"}}} +{"ae":{"0072253606_1933988134":{"source":"0072253606","directed":false,"target":"1933988134"}}} +{"cn":{"0201734842":{"label":"C++ Templates: ...","full-label":"C++ Templates: The Complete Guide"}}} +{"an":{"0201704315":{"label":"C++ Templates: ...","full-label":"C++ Templates: The Complete Guide"}}} +{"ae":{"0201734842_0201704315":{"source":"0201734842","directed":false,"target":"0201704315"}}} +{"ae":{"0321334876_0201734842":{"source":"0321334876","directed":false,"target":"0201734842"}}} +{"ae":{"0201749629_0201734842":{"source":"0201749629","directed":false,"target":"0201734842"}}} +{"ae":{"0201700735_0201734842":{"source":"0201700735","directed":false,"target":"0201734842"}}} +{"cn":{"0201702258":{"label":"Writing Effecti...","full-label":"Writing Effective Use Cases (The Agile Software Development Series)"}}} +{"an":{"1592009123":{"label":"Writing Effecti...","full-label":"Writing Effective Use Cases (The Agile Software Development Series)"}}} +{"ae":{"0201702258_1592009123":{"source":"0201702258","directed":false,"target":"1592009123"}}} +{"ae":{"1576810607_0201702258":{"source":"1576810607","directed":false,"target":"0201702258"}}} +{"an":{"0321419499":{"label":"Writing Effecti...","full-label":"Writing Effective Use Cases (The Agile Software Development Series)"}}} +{"ae":{"0201702258_0321419499":{"source":"0201702258","directed":false,"target":"0321419499"}}} +{"ae":{"0735622671_0201702258":{"source":"0735622671","directed":false,"target":"0201702258"}}} +{"cn":{"0486252116":{"label":"Entertaining Ma...","full-label":"Entertaining Mathematical Puzzles"}}} +{"ae":{"0486256375_0486252116":{"source":"0486256375","directed":false,"target":"0486252116"}}} +{"ae":{"0486270785_0486252116":{"source":"0486270785","directed":false,"target":"0486252116"}}} +{"ae":{"0486427552_0486252116":{"source":"0486427552","directed":false,"target":"0486252116"}}} +{"ae":{"0970825315_0486252116":{"source":"0970825315","directed":false,"target":"0486252116"}}} +{"cn":{"0486270785":{"label":"The Moscow Puzz...","full-label":"The Moscow Puzzles: 359 Mathematical Recreations (Math & Logic Puzzles)"}}} +{"ae":{"0486256375_0486270785":{"source":"0486256375","directed":false,"target":"0486270785"}}} +{"an":{"0393061140":{"label":"The Moscow Puzz...","full-label":"The Moscow Puzzles: 359 Mathematical Recreations (Math & Logic Puzzles)"}}} +{"ae":{"0486270785_0393061140":{"source":"0486270785","directed":false,"target":"0393061140"}}} +{"ae":{"0486427552_0486270785":{"source":"0486427552","directed":false,"target":"0486270785"}}} +{"cn":{"0486256375":{"label":"Perplexing Puzz...","full-label":"Perplexing Puzzles and Tantalizing Teasers (Math & Logic Puzzles)"}}} +{"ae":{"0970825315_0486256375":{"source":"0970825315","directed":false,"target":"0486256375"}}} +{"ae":{"0486427552_0486256375":{"source":"0486427552","directed":false,"target":"0486256375"}}} +{"cn":{"0486427552":{"label":"Brain Busters! ...","full-label":"Brain Busters! Mind-Stretching Puzzles in Math and Logic"}}} +{"ae":{"0970825315_0486427552":{"source":"0970825315","directed":false,"target":"0486427552"}}} +{"an":{"0970825307":{"label":"Brain Busters! ...","full-label":"Brain Busters! Mind-Stretching Puzzles in Math and Logic"}}} +{"ae":{"0486427552_0970825307":{"source":"0486427552","directed":false,"target":"0970825307"}}} +{"cn":{"0970825315":{"label":"So You Think Yo...","full-label":"So You Think You're Smart: 150 Fun and Challenging Brain Teasers"}}} +{"ae":{"0970825307_0970825315":{"source":"0970825307","directed":false,"target":"0970825315"}}} +{"an":{"1885003994":{"label":"So You Think Yo...","full-label":"So You Think You're Smart: 150 Fun and Challenging Brain Teasers"}}} +{"ae":{"0970825315_1885003994":{"source":"0970825315","directed":false,"target":"1885003994"}}} +{"an":{"0140318755":{"label":"So You Think Yo...","full-label":"So You Think You're Smart: 150 Fun and Challenging Brain Teasers"}}} +{"ae":{"0970825315_0140318755":{"source":"0970825315","directed":false,"target":"0140318755"}}} +{"cn":{"0596527349":{"label":"Information Arc...","full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites"}}} +{"ae":{"0321344758_0596527349":{"source":"0321344758","directed":false,"target":"0596527349"}}} +{"ae":{"0596008031_0596527349":{"source":"0596008031","directed":false,"target":"0596527349"}}} +{"an":{"0321392353":{"label":"Information Arc...","full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites"}}} +{"ae":{"0596527349_0321392353":{"source":"0596527349","directed":false,"target":"0321392353"}}} +{"an":{"0596007655":{"label":"Information Arc...","full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites"}}} +{"ae":{"0596527349_0596007655":{"source":"0596527349","directed":false,"target":"0596007655"}}} +{"an":{"0735712026":{"label":"Information Arc...","full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites"}}} +{"ae":{"0596527349_0735712026":{"source":"0596527349","directed":false,"target":"0735712026"}}} +{"cn":{"0321344758":{"label":"Don't Make Me T...","full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition"}}} +{"ae":{"0596008031_0321344758":{"source":"0596008031","directed":false,"target":"0321344758"}}} +{"an":{"0321350316":{"label":"Don't Make Me T...","full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition"}}} +{"ae":{"0321344758_0321350316":{"source":"0321344758","directed":false,"target":"0321350316"}}} +{"an":{"0465067107":{"label":"Don't Make Me T...","full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition"}}} +{"ae":{"0321344758_0465067107":{"source":"0321344758","directed":false,"target":"0465067107"}}} +{"an":{"156205810X":{"label":"Don't Make Me T...","full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition"}}} +{"ae":{"0321344758_156205810X":{"source":"0321344758","directed":false,"target":"156205810X"}}} +{"cn":{"0596008031":{"label":"Designing Inter...","full-label":"Designing Interfaces: Patterns for Effective Interaction Design"}}} +{"ae":{"0596007655_0596008031":{"source":"0596007655","directed":false,"target":"0596008031"}}} +{"an":{"0262134748":{"label":"Designing Inter...","full-label":"Designing Interfaces: Patterns for Effective Interaction Design"}}} +{"ae":{"0596008031_0262134748":{"source":"0596008031","directed":false,"target":"0262134748"}}} +{"ae":{"0321392353_0596008031":{"source":"0321392353","directed":false,"target":"0596008031"}}} +{"cn":{"0974514055":{"label":"Programming Rub...","full-label":"Programming Ruby: The Pragmatic Programmers' Guide, Second Edition"}}} +{"ae":{"0596523696_0974514055":{"source":"0596523696","directed":false,"target":"0974514055"}}} +{"ae":{"0977616606_0974514055":{"source":"0977616606","directed":false,"target":"0974514055"}}} +{"an":{"0672328844":{"label":"Programming Rub...","full-label":"Programming Ruby: The Pragmatic Programmers' Guide, Second Edition"}}} +{"ae":{"0974514055_0672328844":{"source":"0974514055","directed":false,"target":"0672328844"}}} +{"ae":{"1932394699_0974514055":{"source":"1932394699","directed":false,"target":"0974514055"}}} +{"cn":{"0977616606":{"label":"Rails Recipes (...","full-label":"Rails Recipes (Pragmatic Programmers)"}}} +{"ae":{"1932394699_0977616606":{"source":"1932394699","directed":false,"target":"0977616606"}}} +{"ae":{"0596523696_0977616606":{"source":"0596523696","directed":false,"target":"0977616606"}}} +{"ae":{"0596527446_0977616606":{"source":"0596527446","directed":false,"target":"0977616606"}}} +{"cn":{"0596527446":{"label":"Ajax on Rails","full-label":"Ajax on Rails"}}} +{"an":{"0596527314":{"label":"Ajax on Rails","full-label":"Ajax on Rails"}}} +{"ae":{"0596527446_0596527314":{"source":"0596527446","directed":false,"target":"0596527314"}}} +{"ae":{"0596523696_0596527446":{"source":"0596523696","directed":false,"target":"0596527446"}}} +{"ae":{"0974514055_0596527446":{"source":"0974514055","directed":false,"target":"0596527446"}}} +{"cn":{"0596523696":{"label":"Ruby Cookbook (...","full-label":"Ruby Cookbook (Cookbooks (O'Reilly))"}}} +{"ae":{"0596527314_0596523696":{"source":"0596527314","directed":false,"target":"0596523696"}}} +{"ae":{"0672328844_0596523696":{"source":"0672328844","directed":false,"target":"0596523696"}}} +{"cn":{"1932394699":{"label":"Ruby for Rails:...","full-label":"Ruby for Rails: Ruby Techniques for Rails Developers"}}} +{"ae":{"0596523696_1932394699":{"source":"0596523696","directed":false,"target":"1932394699"}}} +{"ae":{"0596527446_1932394699":{"source":"0596527446","directed":false,"target":"1932394699"}}} +{"cn":{"059652708X":{"label":"MySQL Cookbook","full-label":"MySQL Cookbook"}}} +{"an":{"0596101015":{"label":"MySQL Cookbook","full-label":"MySQL Cookbook"}}} +{"ae":{"059652708X_0596101015":{"source":"059652708X","directed":false,"target":"0596101015"}}} +{"ae":{"0596100892_059652708X":{"source":"0596100892","directed":false,"target":"059652708X"}}} +{"an":{"0596008643":{"label":"MySQL Cookbook","full-label":"MySQL Cookbook"}}} +{"ae":{"059652708X_0596008643":{"source":"059652708X","directed":false,"target":"0596008643"}}} +{"an":{"0672327120":{"label":"MySQL Cookbook","full-label":"MySQL Cookbook"}}} +{"ae":{"059652708X_0672327120":{"source":"059652708X","directed":false,"target":"0672327120"}}} +{"cn":{"0596100892":{"label":"MySQL Stored Pr...","full-label":"MySQL Stored Procedure Programming"}}} +{"ae":{"159059505X_0596100892":{"source":"159059505X","directed":false,"target":"0596100892"}}} +{"an":{"0672328704":{"label":"MySQL Stored Pr...","full-label":"MySQL Stored Procedure Programming"}}} +{"ae":{"0596100892_0672328704":{"source":"0596100892","directed":false,"target":"0672328704"}}} +{"an":{"1590595351":{"label":"MySQL Stored Pr...","full-label":"MySQL Stored Procedure Programming"}}} +{"ae":{"0596100892_1590595351":{"source":"0596100892","directed":false,"target":"1590595351"}}} +{"cn":{"159059505X":{"label":"Pro MySQL (Expe...","full-label":"Pro MySQL (Expert's Voice in Open Source)"}}} +{"ae":{"059652708X_159059505X":{"source":"059652708X","directed":false,"target":"159059505X"}}} +{"ae":{"1590595351_159059505X":{"source":"1590595351","directed":false,"target":"159059505X"}}} +{"ae":{"0596102356_159059505X":{"source":"0596102356","directed":false,"target":"159059505X"}}} +{"cn":{"159200346X":{"label":"Physical Comput...","full-label":"Physical Computing: Sensing and Controlling the Physical World with Computers"}}} +{"an":{"0071452818":{"label":"Physical Comput...","full-label":"Physical Computing: Sensing and Controlling the Physical World with Computers"}}} +{"ae":{"159200346X_0071452818":{"source":"159200346X","directed":false,"target":"0071452818"}}} +{"an":{"0945053312":{"label":"Physical Comput...","full-label":"Physical Computing: Sensing and Controlling the Physical World with Computers"}}} +{"ae":{"159200346X_0945053312":{"source":"159200346X","directed":false,"target":"0945053312"}}} +{"cn":{"0500285179":{"label":"Creative Code: ...","full-label":"Creative Code: Aesthetics + Computation"}}} +{"an":{"0262134721":{"label":"Creative Code: ...","full-label":"Creative Code: Aesthetics + Computation"}}} +{"ae":{"0500285179_0262134721":{"source":"0500285179","directed":false,"target":"0262134721"}}} +{"ae":{"0262182629_0500285179":{"source":"0262182629","directed":false,"target":"0500285179"}}} +{"an":{"0262632446":{"label":"Creative Code: ...","full-label":"Creative Code: Aesthetics + Computation"}}} +{"ae":{"0500285179_0262632446":{"source":"0500285179","directed":false,"target":"0262632446"}}} +{"an":{"0789305259":{"label":"Creative Code: ...","full-label":"Creative Code: Aesthetics + Computation"}}} +{"ae":{"0500285179_0789305259":{"source":"0500285179","directed":false,"target":"0789305259"}}} +{"cn":{"059651428X":{"label":"The Best of MAK...","full-label":"The Best of MAKE (Make)"}}} +{"an":{"0596514921":{"label":"The Best of MAK...","full-label":"The Best of MAKE (Make)"}}} +{"ae":{"059651428X_0596514921":{"source":"059651428X","directed":false,"target":"0596514921"}}} +{"an":{"0596510543":{"label":"The Best of MAK...","full-label":"The Best of MAKE (Make)"}}} +{"ae":{"059651428X_0596510543":{"source":"059651428X","directed":false,"target":"0596510543"}}} +{"an":{"B0007RNI5K":{"label":"The Best of MAK...","full-label":"The Best of MAKE (Make)"}}} +{"ae":{"059651428X_B0007RNI5K":{"source":"059651428X","directed":false,"target":"B0007RNI5K"}}} +{"ae":{"159200346X_059651428X":{"source":"159200346X","directed":false,"target":"059651428X"}}} +{"cn":{"1591841992":{"label":"The Back of the...","full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures"}}} +{"an":{"0142005207":{"label":"The Back of the...","full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures"}}} +{"ae":{"1591841992_0142005207":{"source":"1591841992","directed":false,"target":"0142005207"}}} +{"ae":{"0979777704_1591841992":{"source":"0979777704","directed":false,"target":"1591841992"}}} +{"an":{"1594482918":{"label":"The Back of the...","full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures"}}} +{"ae":{"1591841992_1594482918":{"source":"1591841992","directed":false,"target":"1594482918"}}} +{"an":{"006135323X":{"label":"The Back of the...","full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures"}}} +{"ae":{"1591841992_006135323X":{"source":"1591841992","directed":false,"target":"006135323X"}}} +{"cn":{"0735623872":{"label":"Beyond Bullet P...","full-label":"Beyond Bullet Points: Using Microsoft® Office PowerPoint® 2007 to Create Presentations That Inform, Motivate, and Inspire"}}} +{"an":{"0131875108":{"label":"Beyond Bullet P...","full-label":"Beyond Bullet Points: Using Microsoft® Office PowerPoint® 2007 to Create Presentations That Inform, Motivate, and Inspire"}}} +{"ae":{"0735623872_0131875108":{"source":"0735623872","directed":false,"target":"0131875108"}}} +{"ae":{"1591841992_0735623872":{"source":"1591841992","directed":false,"target":"0735623872"}}} +{"ae":{"0195320697_0735623872":{"source":"0195320697","directed":false,"target":"0735623872"}}} +{"an":{"0615142230":{"label":"Beyond Bullet P...","full-label":"Beyond Bullet Points: Using Microsoft® Office PowerPoint® 2007 to Create Presentations That Inform, Motivate, and Inspire"}}} +{"ae":{"0735623872_0615142230":{"source":"0735623872","directed":false,"target":"0615142230"}}} +{"cn":{"0195320697":{"label":"Clear and to th...","full-label":"Clear and to the Point: 8 Psychological Principles for Compelling PowerPoint Presentations"}}} +{"ae":{"1591841992_0195320697":{"source":"1591841992","directed":false,"target":"0195320697"}}} +{"ae":{"0615142230_0195320697":{"source":"0615142230","directed":false,"target":"0195320697"}}} +{"ae":{"1400064287_0195320697":{"source":"1400064287","directed":false,"target":"0195320697"}}} +{"cn":{"0979777704":{"label":"Brain Rules: 12...","full-label":"Brain Rules: 12 Principles for Surviving and Thriving at Work, Home, and School (Book & DVD)"}}} +{"an":{"0316113506":{"label":"Brain Rules: 12...","full-label":"Brain Rules: 12 Principles for Surviving and Thriving at Work, Home, and School (Book & DVD)"}}} +{"ae":{"0979777704_0316113506":{"source":"0979777704","directed":false,"target":"0316113506"}}} +{"an":{"1596912839":{"label":"Brain Rules: 12...","full-label":"Brain Rules: 12 Principles for Surviving and Thriving at Work, Home, and School (Book & DVD)"}}} +{"ae":{"0979777704_1596912839":{"source":"0979777704","directed":false,"target":"1596912839"}}} +{"ae":{"1594482918_0979777704":{"source":"1594482918","directed":false,"target":"0979777704"}}} +{"cn":{"1400064287":{"label":"Made to Stick: ...","full-label":"Made to Stick: Why Some Ideas Survive and Others Die"}}} +{"ae":{"1591841992_1400064287":{"source":"1591841992","directed":false,"target":"1400064287"}}} +{"an":{"1591841666":{"label":"Made to Stick: ...","full-label":"Made to Stick: Why Some Ideas Survive and Others Die"}}} +{"ae":{"1400064287_1591841666":{"source":"1400064287","directed":false,"target":"1591841666"}}} +{"an":{"0307341518":{"label":"Made to Stick: ...","full-label":"Made to Stick: Why Some Ideas Survive and Others Die"}}} +{"ae":{"1400064287_0307341518":{"source":"1400064287","directed":false,"target":"0307341518"}}} +{"an":{"1591841437":{"label":"Made to Stick: ...","full-label":"Made to Stick: Why Some Ideas Survive and Others Die"}}} +{"ae":{"1400064287_1591841437":{"source":"1400064287","directed":false,"target":"1591841437"}}} +{"cn":{"1591840562":{"label":"The Art of the ...","full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything"}}} +{"an":{"088730995X":{"label":"The Art of the ...","full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything"}}} +{"ae":{"1591840562_088730995X":{"source":"1591840562","directed":false,"target":"088730995X"}}} +{"an":{"0060517123":{"label":"The Art of the ...","full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything"}}} +{"ae":{"1591840562_0060517123":{"source":"1591840562","directed":false,"target":"0060517123"}}} +{"ae":{"0321525655_1591840562":{"source":"0321525655","directed":false,"target":"1591840562"}}} +{"an":{"0971187304":{"label":"The Art of the ...","full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything"}}} +{"ae":{"1591840562_0971187304":{"source":"1591840562","directed":false,"target":"0971187304"}}} +{"cn":{"0321349601":{"label":"Java Concurrenc...","full-label":"Java Concurrency in Practice"}}} +{"an":{"0596527756":{"label":"Java Concurrenc...","full-label":"Java Concurrency in Practice"}}} +{"ae":{"0321349601_0596527756":{"source":"0321349601","directed":false,"target":"0596527756"}}} +{"an":{"0321356683":{"label":"Java Concurrenc...","full-label":"Java Concurrency in Practice"}}} +{"ae":{"0321349601_0321356683":{"source":"0321349601","directed":false,"target":"0321356683"}}} +{"an":{"032133678X":{"label":"Java Concurrenc...","full-label":"Java Concurrency in Practice"}}} +{"ae":{"0321349601_032133678X":{"source":"0321349601","directed":false,"target":"032133678X"}}} +{"an":{"1932394885":{"label":"Java Concurrenc...","full-label":"Java Concurrency in Practice"}}} +{"ae":{"0321349601_1932394885":{"source":"0321349601","directed":false,"target":"1932394885"}}} +{"an":{"0201310058":{"label":"Java Concurrenc...","full-label":"Java Concurrency in Practice"}}} +{"ae":{"0321349601_0201310058":{"source":"0321349601","directed":false,"target":"0201310058"}}} +{"cn":{"0521644089":{"label":"The Haskell Sch...","full-label":"The Haskell School of Expression: Learning Functional Programming through Multimedia"}}} +{"ae":{"0954300696_0521644089":{"source":"0954300696","directed":false,"target":"0521644089"}}} +{"ae":{"193435600X_0521644089":{"source":"193435600X","directed":false,"target":"0521644089"}}} +{"ae":{"0521663504_0521644089":{"source":"0521663504","directed":false,"target":"0521644089"}}} +{"an":{"0201342758":{"label":"The Haskell Sch...","full-label":"The Haskell School of Expression: Learning Functional Programming through Multimedia"}}} +{"ae":{"0521644089_0201342758":{"source":"0521644089","directed":false,"target":"0201342758"}}} +{"cn":{"0954300696":{"label":"The Haskell Roa...","full-label":"The Haskell Road to Logic, Maths and Programming (Texts in Computing S.)"}}} +{"an":{"0954300653":{"label":"The Haskell Roa...","full-label":"The Haskell Road to Logic, Maths and Programming (Texts in Computing S.)"}}} +{"ae":{"0954300696_0954300653":{"source":"0954300696","directed":false,"target":"0954300653"}}} +{"ae":{"193435600X_0954300696":{"source":"193435600X","directed":false,"target":"0954300696"}}} +{"ae":{"0521663504_0954300696":{"source":"0521663504","directed":false,"target":"0954300696"}}} +{"cn":{"0521663504":{"label":"Purely Function...","full-label":"Purely Functional Data Structures"}}} +{"ae":{"193435600X_0521663504":{"source":"193435600X","directed":false,"target":"0521663504"}}} +{"ae":{"0596510047_0521663504":{"source":"0596510047","directed":false,"target":"0521663504"}}} +{"cn":{"0971015848":{"label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition"}}} +{"an":{"1581313055":{"label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition"}}} +{"ae":{"0971015848_1581313055":{"source":"0971015848","directed":false,"target":"1581313055"}}} +{"an":{"0471757225":{"label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition"}}} +{"ae":{"0971015848_0471757225":{"source":"0971015848","directed":false,"target":"0471757225"}}} +{"an":{"0070534489":{"label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition"}}} +{"ae":{"0971015848_0070534489":{"source":"0971015848","directed":false,"target":"0070534489"}}} +{"an":{"0979003903":{"label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition"}}} +{"ae":{"0971015848_0979003903":{"source":"0971015848","directed":false,"target":"0979003903"}}} +{"an":{"0471444014":{"label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition"}}} +{"ae":{"0971015848_0471444014":{"source":"0971015848","directed":false,"target":"0471444014"}}} +{"cn":{"038541580X":{"label":"Prisoner's Dile...","full-label":"Prisoner's Dilemma"}}} +{"an":{"0486296725":{"label":"Prisoner's Dile...","full-label":"Prisoner's Dilemma"}}} +{"ae":{"038541580X_0486296725":{"source":"038541580X","directed":false,"target":"0486296725"}}} +{"ae":{"0809045990_038541580X":{"source":"0809045990","directed":false,"target":"038541580X"}}} +{"an":{"0486251012":{"label":"Prisoner's Dile...","full-label":"Prisoner's Dilemma"}}} +{"ae":{"038541580X_0486251012":{"source":"038541580X","directed":false,"target":"0486251012"}}} +{"an":{"0465005640":{"label":"Prisoner's Dile...","full-label":"Prisoner's Dilemma"}}} +{"ae":{"038541580X_0465005640":{"source":"038541580X","directed":false,"target":"0465005640"}}} +{"cn":{"0809045990":{"label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street"}}} +{"an":{"0812975219":{"label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street"}}} +{"ae":{"0809045990_0812975219":{"source":"0809045990","directed":false,"target":"0812975219"}}} +{"an":{"047004389X":{"label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street"}}} +{"ae":{"0809045990_047004389X":{"source":"0809045990","directed":false,"target":"047004389X"}}} +{"an":{"0231143729":{"label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street"}}} +{"ae":{"0809045990_0231143729":{"source":"0809045990","directed":false,"target":"0231143729"}}} +{"an":{"1400063515":{"label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street"}}} +{"ae":{"0809045990_1400063515":{"source":"0809045990","directed":false,"target":"1400063515"}}} +{"an":{"0471794473":{"label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street"}}} +{"ae":{"0809045990_0471794473":{"source":"0809045990","directed":false,"target":"0471794473"}}} +{"cn":{"0521483476":{"label":"Paradoxes","full-label":"Paradoxes"}}} +{"an":{"0691121273":{"label":"Paradoxes","full-label":"Paradoxes"}}} +{"ae":{"0521483476_0691121273":{"source":"0521483476","directed":false,"target":"0691121273"}}} +{"an":{"0415228093":{"label":"Paradoxes","full-label":"Paradoxes"}}} +{"ae":{"0521483476_0415228093":{"source":"0521483476","directed":false,"target":"0415228093"}}} +{"an":{"0195179862":{"label":"Paradoxes","full-label":"Paradoxes"}}} +{"ae":{"0521483476_0195179862":{"source":"0521483476","directed":false,"target":"0195179862"}}} +{"an":{"0486296644":{"label":"Paradoxes","full-label":"Paradoxes"}}} +{"ae":{"0521483476_0486296644":{"source":"0521483476","directed":false,"target":"0486296644"}}} +{"cn":{"0131499084":{"label":"Options, Future...","full-label":"Options, Futures and Other Derivatives (6th Edition)"}}} +{"an":{"0131499068":{"label":"Options, Future...","full-label":"Options, Futures and Other Derivatives (6th Edition)"}}} +{"ae":{"0131499084_0131499068":{"source":"0131499084","directed":false,"target":"0131499068"}}} +{"ae":{"0387401016_0131499084":{"source":"0387401016","directed":false,"target":"0131499084"}}} +{"an":{"0387249680":{"label":"Options, Future...","full-label":"Options, Futures and Other Derivatives (6th Edition)"}}} +{"ae":{"0131499084_0387249680":{"source":"0131499084","directed":false,"target":"0387249680"}}} +{"an":{"155738486X":{"label":"Options, Future...","full-label":"Options, Futures and Other Derivatives (6th Edition)"}}} +{"ae":{"0131499084_155738486X":{"source":"0131499084","directed":false,"target":"155738486X"}}} +{"cn":{"1581311729":{"label":"Vault Guide to ...","full-label":"Vault Guide to Advanced Finance & Quantitative Interviews"}}} +{"an":{"1581313047":{"label":"Vault Guide to ...","full-label":"Vault Guide to Advanced Finance & Quantitative Interviews"}}} +{"ae":{"1581311729_1581313047":{"source":"1581311729","directed":false,"target":"1581313047"}}} +{"an":{"1581311702":{"label":"Vault Guide to ...","full-label":"Vault Guide to Advanced Finance & Quantitative Interviews"}}} +{"ae":{"1581311729_1581311702":{"source":"1581311729","directed":false,"target":"1581311702"}}} +{"an":{"1581315295":{"label":"Vault Guide to ...","full-label":"Vault Guide to Advanced Finance & Quantitative Interviews"}}} +{"ae":{"1581311729_1581315295":{"source":"1581311729","directed":false,"target":"1581315295"}}} +{"ae":{"0131499084_1581311729":{"source":"0131499084","directed":false,"target":"1581311729"}}} +{"cn":{"0387401016":{"label":"Stochastic Calc...","full-label":"Stochastic Calculus for Finance II: Continuous-Time Models (Springer Finance)"}}} +{"ae":{"0387249680_0387401016":{"source":"0387249680","directed":false,"target":"0387401016"}}} +{"an":{"0387004513":{"label":"Stochastic Calc...","full-label":"Stochastic Calculus for Finance II: Continuous-Time Models (Springer Finance)"}}} +{"ae":{"0387401016_0387004513":{"source":"0387401016","directed":false,"target":"0387004513"}}} +{"an":{"3540221492":{"label":"Stochastic Calc...","full-label":"Stochastic Calculus for Finance II: Continuous-Time Models (Springer Finance)"}}} +{"ae":{"0387401016_3540221492":{"source":"0387401016","directed":false,"target":"3540221492"}}} +{"cn":{"0470192739":{"label":"My Life as a Qu...","full-label":"My Life as a Quant: Reflections on Physics and Finance"}}} +{"an":{"0470050624":{"label":"My Life as a Qu...","full-label":"My Life as a Quant: Reflections on Physics and Finance"}}} +{"ae":{"0470192739_0470050624":{"source":"0470192739","directed":false,"target":"0470050624"}}} +{"an":{"0375758259":{"label":"My Life as a Qu...","full-label":"My Life as a Quant: Reflections on Physics and Finance"}}} +{"ae":{"0470192739_0375758259":{"source":"0470192739","directed":false,"target":"0375758259"}}} +{"an":{"0471457329":{"label":"My Life as a Qu...","full-label":"My Life as a Quant: Reflections on Physics and Finance"}}} +{"ae":{"0470192739_0471457329":{"source":"0470192739","directed":false,"target":"0471457329"}}} +{"ae":{"0812975219_0470192739":{"source":"0812975219","directed":false,"target":"0470192739"}}} +{"cn":{"1432706810":{"label":"Starting Your C...","full-label":"Starting Your Career as a Wall Street Quant: A Practical, No-BS Guide to Getting a Job in Quantitative Finance and Launching a Lucrative Career"}}} +{"ae":{"0470050624_1432706810":{"source":"0470050624","directed":false,"target":"1432706810"}}} +{"ae":{"0470192739_1432706810":{"source":"0470192739","directed":false,"target":"1432706810"}}} +{"an":{"0470058269":{"label":"Starting Your C...","full-label":"Starting Your Career as a Wall Street Quant: A Practical, No-BS Guide to Getting a Job in Quantitative Finance and Launching a Lucrative Career"}}} +{"ae":{"1432706810_0470058269":{"source":"1432706810","directed":false,"target":"0470058269"}}} +{"ae":{"0131499084_1432706810":{"source":"0131499084","directed":false,"target":"1432706810"}}} +{"cn":{"1593573111":{"label":"Resume Magic: T...","full-label":"Resume Magic: Trade Secrets of a Professional Resume Writer (Resume Magic Trade Secrets of a Professional Resume Writer)"}}} +{"an":{"1593573642":{"label":"Resume Magic: T...","full-label":"Resume Magic: Trade Secrets of a Professional Resume Writer (Resume Magic Trade Secrets of a Professional Resume Writer)"}}} +{"ae":{"1593573111_1593573642":{"source":"1593573111","directed":false,"target":"1593573642"}}} +{"an":{"081447280X":{"label":"Resume Magic: T...","full-label":"Resume Magic: Trade Secrets of a Professional Resume Writer (Resume Magic Trade Secrets of a Professional Resume Writer)"}}} +{"ae":{"1593573111_081447280X":{"source":"1593573111","directed":false,"target":"081447280X"}}} +{"ae":{"1402203853_1593573111":{"source":"1402203853","directed":false,"target":"1593573111"}}} +{"ae":{"007016357X_1593573111":{"source":"007016357X","directed":false,"target":"1593573111"}}} +{"cn":{"1593571275":{"label":"Expert Resumes ...","full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)"}}} +{"an":{"1593570007":{"label":"Expert Resumes ...","full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)"}}} +{"ae":{"1593571275_1593570007":{"source":"1593571275","directed":false,"target":"1593570007"}}} +{"an":{"1593570929":{"label":"Expert Resumes ...","full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)"}}} +{"ae":{"1593571275_1593570929":{"source":"1593571275","directed":false,"target":"1593570929"}}} +{"ae":{"1593573111_1593571275":{"source":"1593573111","directed":false,"target":"1593571275"}}} +{"an":{"1593573669":{"label":"Expert Resumes ...","full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)"}}} +{"ae":{"1593571275_1593573669":{"source":"1593571275","directed":false,"target":"1593573669"}}} +{"cn":{"1587131560":{"label":"The IT Career B...","full-label":"The IT Career Builder's Toolkit"}}} +{"ae":{"0071495789_1587131560":{"source":"0071495789","directed":false,"target":"1587131560"}}} +{"an":{"0072126833":{"label":"The IT Career B...","full-label":"The IT Career Builder's Toolkit"}}} +{"ae":{"1587131560_0072126833":{"source":"1587131560","directed":false,"target":"0072126833"}}} +{"an":{"0132253569":{"label":"The IT Career B...","full-label":"The IT Career Builder's Toolkit"}}} +{"ae":{"1587131560_0132253569":{"source":"1587131560","directed":false,"target":"0132253569"}}} +{"an":{"1933639261":{"label":"The IT Career B...","full-label":"The IT Career Builder's Toolkit"}}} +{"ae":{"1587131560_1933639261":{"source":"1587131560","directed":false,"target":"1933639261"}}} +{"cn":{"0071449825":{"label":"Perfect Phrases...","full-label":"Perfect Phrases for the Perfect Interview: Hundreds of Ready-to-Use Phrases That Succinctly Demonstrate Your Skills, Your Experience and Your Value in Any Interview Situation (Perfect Phrases)"}}} +{"ae":{"1402203853_0071449825":{"source":"1402203853","directed":false,"target":"0071449825"}}} +{"an":{"0071454055":{"label":"Perfect Phrases...","full-label":"Perfect Phrases for the Perfect Interview: Hundreds of Ready-to-Use Phrases That Succinctly Demonstrate Your Skills, Your Experience and Your Value in Any Interview Situation (Perfect Phrases)"}}} +{"ae":{"0071449825_0071454055":{"source":"0071449825","directed":false,"target":"0071454055"}}} +{"ae":{"0071387730_0071449825":{"source":"0071387730","directed":false,"target":"0071449825"}}} +{"an":{"0071454063":{"label":"Perfect Phrases...","full-label":"Perfect Phrases for the Perfect Interview: Hundreds of Ready-to-Use Phrases That Succinctly Demonstrate Your Skills, Your Experience and Your Value in Any Interview Situation (Perfect Phrases)"}}} +{"ae":{"0071449825_0071454063":{"source":"0071449825","directed":false,"target":"0071454063"}}} +{"cn":{"0970901224":{"label":"Interview Fitne...","full-label":"Interview Fitness Training: A Workout with Carole Martin, the Interview Coach"}}} +{"ae":{"0071449825_0970901224":{"source":"0071449825","directed":false,"target":"0970901224"}}} +{"ae":{"1402203853_0970901224":{"source":"1402203853","directed":false,"target":"0970901224"}}} +{"ae":{"1564148696_0970901224":{"source":"1564148696","directed":false,"target":"0970901224"}}} +{"ae":{"0071387730_0970901224":{"source":"0071387730","directed":false,"target":"0970901224"}}} +{"cn":{"1564148696":{"label":"Competency-Base...","full-label":"Competency-Based Interviews: Master the Tough New Interview Style And Give Them the Answers That Will Win You the Job"}}} +{"an":{"156414772X":{"label":"Competency-Base...","full-label":"Competency-Based Interviews: Master the Tough New Interview Style And Give Them the Answers That Will Win You the Job"}}} +{"ae":{"1564148696_156414772X":{"source":"1564148696","directed":false,"target":"156414772X"}}} +{"ae":{"1564147789_1564148696":{"source":"1564147789","directed":false,"target":"1564148696"}}} +{"an":{"0814473016":{"label":"Competency-Base...","full-label":"Competency-Based Interviews: Master the Tough New Interview Style And Give Them the Answers That Will Win You the Job"}}} +{"ae":{"1564148696_0814473016":{"source":"1564148696","directed":false,"target":"0814473016"}}} +{"ae":{"0071387730_1564148696":{"source":"0071387730","directed":false,"target":"1564148696"}}} +{"cn":{"007016357X":{"label":"Best Answers to...","full-label":"Best Answers to the 201 Most Frequently Asked Interview Questions"}}} +{"an":{"0425166864":{"label":"Best Answers to...","full-label":"Best Answers to the 201 Most Frequently Asked Interview Questions"}}} +{"ae":{"007016357X_0425166864":{"source":"007016357X","directed":false,"target":"0425166864"}}} +{"ae":{"007141827X_007016357X":{"source":"007141827X","directed":false,"target":"007016357X"}}} +{"cn":{"007141827X":{"label":"How to Intervie...","full-label":"How to Interview Like a Top MBA: Job-Winning Strategies From Headhunters, Fortune 100 Recruiters, and Career Counselors"}}} +{"ae":{"0971015848_007141827X":{"source":"0971015848","directed":false,"target":"007141827X"}}} +{"ae":{"1564148696_007141827X":{"source":"1564148696","directed":false,"target":"007141827X"}}} +{"cn":{"1418040002":{"label":"101 Great Answe...","full-label":"101 Great Answers to the Toughest Interview Questions"}}} +{"an":{"1418040010":{"label":"101 Great Answe...","full-label":"101 Great Answers to the Toughest Interview Questions"}}} +{"ae":{"1418040002_1418040010":{"source":"1418040002","directed":false,"target":"1418040010"}}} +{"ae":{"1402203853_1418040002":{"source":"1402203853","directed":false,"target":"1418040002"}}} +{"ae":{"007016357X_1418040002":{"source":"007016357X","directed":false,"target":"1418040002"}}} +{"ae":{"1593573111_1418040002":{"source":"1593573111","directed":false,"target":"1418040002"}}} +{"cn":{"1564147789":{"label":"Winning Job Int...","full-label":"Winning Job Interviews: Reduce Interview Anxiety / Outprepare the Other Candidates / Land the Job You Love"}}} +{"ae":{"0071387730_1564147789":{"source":"0071387730","directed":false,"target":"1564147789"}}} +{"ae":{"1593573111_1564147789":{"source":"1593573111","directed":false,"target":"1564147789"}}} +{"ae":{"007016357X_1564147789":{"source":"007016357X","directed":false,"target":"1564147789"}}} +{"cn":{"0735622671":{"label":"More About Soft...","full-label":"More About Software Requirements: Thorny Issues and Practical Advice"}}} +{"ae":{"1576810607_0735622671":{"source":"1576810607","directed":false,"target":"0735622671"}}} +{"ae":{"0735605351_0735622671":{"source":"0735605351","directed":false,"target":"0735622671"}}} +{"an":{"0735623988":{"label":"More About Soft...","full-label":"More About Software Requirements: Thorny Issues and Practical Advice"}}} +{"ae":{"0735622671_0735623988":{"source":"0735622671","directed":false,"target":"0735623988"}}} +{"cn":{"1576810607":{"label":"The Software Re...","full-label":"The Software Requirements Memory Jogger: A Pocket Guide to Help Software And Business Teams Develop And Manage Requirements (Memory Jogger) (Memory Jogger)"}}} +{"an":{"0201786060":{"label":"The Software Re...","full-label":"The Software Requirements Memory Jogger: A Pocket Guide to Help Software And Business Teams Develop And Manage Requirements (Memory Jogger) (Memory Jogger)"}}} +{"ae":{"1576810607_0201786060":{"source":"1576810607","directed":false,"target":"0201786060"}}} +{"ae":{"1592009123_1576810607":{"source":"1592009123","directed":false,"target":"1576810607"}}} +{"cn":{"073561993X":{"label":"Agile Project M...","full-label":"Agile Project Management with Scrum (Microsoft Professional)"}}} +{"ae":{"0130676349_073561993X":{"source":"0130676349","directed":false,"target":"073561993X"}}} +{"an":{"0735623376":{"label":"Agile Project M...","full-label":"Agile Project Management with Scrum (Microsoft Professional)"}}} +{"ae":{"073561993X_0735623376":{"source":"073561993X","directed":false,"target":"0735623376"}}} +{"ae":{"0131111558_073561993X":{"source":"0131111558","directed":false,"target":"073561993X"}}} +{"cn":{"0130676349":{"label":"Agile Software ...","full-label":"Agile Software Development with SCRUM (Series in Agile Software Development)"}}} +{"ae":{"0131111558_0130676349":{"source":"0131111558","directed":false,"target":"0130676349"}}} +{"ae":{"0735623376_0130676349":{"source":"0735623376","directed":false,"target":"0130676349"}}} +{"cn":{"0131111558":{"label":"Agile and Itera...","full-label":"Agile and Iterative Development: A Manager's Guide (The Agile Software Development Series)"}}} +{"an":{"0321219775":{"label":"Agile and Itera...","full-label":"Agile and Iterative Development: A Manager's Guide (The Agile Software Development Series)"}}} +{"ae":{"0131111558_0321219775":{"source":"0131111558","directed":false,"target":"0321219775"}}} +{"cn":{"0977616649":{"label":"Agile Retrospec...","full-label":"Agile Retrospectives: Making Good Teams Great"}}} +{"ae":{"073561993X_0977616649":{"source":"073561993X","directed":false,"target":"0977616649"}}} +{"an":{"0976694026":{"label":"Agile Retrospec...","full-label":"Agile Retrospectives: Making Good Teams Great"}}} +{"ae":{"0977616649_0976694026":{"source":"0977616649","directed":false,"target":"0976694026"}}} +{"an":{"097451408X":{"label":"Agile Retrospec...","full-label":"Agile Retrospectives: Making Good Teams Great"}}} +{"ae":{"0977616649_097451408X":{"source":"0977616649","directed":false,"target":"097451408X"}}} +{"cn":{"0131019082":{"label":"UNIX Internals:...","full-label":"UNIX Internals: The New Frontiers"}}} +{"ae":{"0201433079_0131019082":{"source":"0201433079","directed":false,"target":"0131019082"}}} +{"an":{"0201633388":{"label":"UNIX Internals:...","full-label":"UNIX Internals: The New Frontiers"}}} +{"ae":{"0131019082_0201633388":{"source":"0131019082","directed":false,"target":"0201633388"}}} +{"an":{"0471164836":{"label":"UNIX Internals:...","full-label":"UNIX Internals: The New Frontiers"}}} +{"ae":{"0131019082_0471164836":{"source":"0131019082","directed":false,"target":"0471164836"}}} +{"an":{"1573980137":{"label":"UNIX Internals:...","full-label":"UNIX Internals: The New Frontiers"}}} +{"ae":{"0131019082_1573980137":{"source":"0131019082","directed":false,"target":"1573980137"}}} +{"cn":{"0131429388":{"label":"Operating Syste...","full-label":"Operating Systems Design and Implementation (3rd Edition) (Prentice Hall Software Series)"}}} +{"an":{"0136006639":{"label":"Operating Syste...","full-label":"Operating Systems Design and Implementation (3rd Edition) (Prentice Hall Software Series)"}}} +{"ae":{"0131429388_0136006639":{"source":"0131429388","directed":false,"target":"0136006639"}}} +{"an":{"0130661023":{"label":"Operating Syste...","full-label":"Operating Systems Design and Implementation (3rd Edition) (Prentice Hall Software Series)"}}} +{"ae":{"0131429388_0130661023":{"source":"0131429388","directed":false,"target":"0130661023"}}} +{"ae":{"0321486811_0131429388":{"source":"0321486811","directed":false,"target":"0131429388"}}} +{"ae":{"0596005652_0131429388":{"source":"0596005652","directed":false,"target":"0131429388"}}} +{"cn":{"020107981X":{"label":"The AWK Program...","full-label":"The AWK Programming Language"}}} +{"an":{"0596000707":{"label":"The AWK Program...","full-label":"The AWK Programming Language"}}} +{"ae":{"020107981X_0596000707":{"source":"020107981X","directed":false,"target":"0596000707"}}} +{"ae":{"B00007FYIJ_020107981X":{"source":"B00007FYIJ","directed":false,"target":"020107981X"}}} +{"ae":{"013937681X_020107981X":{"source":"013937681X","directed":false,"target":"020107981X"}}} +{"ae":{"0596528124_020107981X":{"source":"0596528124","directed":false,"target":"020107981X"}}} +{"cn":{"1590595009":{"label":"The Best Softwa...","full-label":"The Best Software Writing I: Selected and Introduced by Joel Spolsky"}}} +{"ae":{"1590598385_1590595009":{"source":"1590598385","directed":false,"target":"1590595009"}}} +{"ae":{"1893115941_1590595009":{"source":"1893115941","directed":false,"target":"1590595009"}}} +{"ae":{"0932633439_1590595009":{"source":"0932633439","directed":false,"target":"1590595009"}}} +{"ae":{"0201835959_1590595009":{"source":"0201835959","directed":false,"target":"1590595009"}}} +{"cn":{"1893115941":{"label":"User Interface ...","full-label":"User Interface Design for Programmers"}}} +{"ae":{"1590598385_1893115941":{"source":"1590598385","directed":false,"target":"1893115941"}}} +{"ae":{"0321344758_1893115941":{"source":"0321344758","directed":false,"target":"1893115941"}}} +{"ae":{"0932633439_1893115941":{"source":"0932633439","directed":false,"target":"1893115941"}}} +{"cn":{"0387251456":{"label":"All of Nonparam...","full-label":"All of Nonparametric Statistics (Springer Texts in Statistics)"}}} +{"ae":{"0387310738_0387251456":{"source":"0387310738","directed":false,"target":"0387251456"}}} +{"ae":{"0387952845_0387251456":{"source":"0387952845","directed":false,"target":"0387251456"}}} +{"an":{"0387988645":{"label":"All of Nonparam...","full-label":"All of Nonparametric Statistics (Springer Texts in Statistics)"}}} +{"ae":{"0387251456_0387988645":{"source":"0387251456","directed":false,"target":"0387988645"}}} +{"ae":{"052168689X_0387251456":{"source":"052168689X","directed":false,"target":"0387251456"}}} +{"cn":{"0534243126":{"label":"Statistical Inf...","full-label":"Statistical Inference"}}} +{"an":{"0195073401":{"label":"Statistical Inf...","full-label":"Statistical Inference"}}} +{"ae":{"0534243126_0195073401":{"source":"0534243126","directed":false,"target":"0195073401"}}} +{"an":{"0393957330":{"label":"Statistical Inf...","full-label":"Statistical Inference"}}} +{"ae":{"0534243126_0393957330":{"source":"0534243126","directed":false,"target":"0393957330"}}} +{"an":{"0691010188":{"label":"Statistical Inf...","full-label":"Statistical Inference"}}} +{"ae":{"0534243126_0691010188":{"source":"0534243126","directed":false,"target":"0691010188"}}} +{"an":{"026212274X":{"label":"Statistical Inf...","full-label":"Statistical Inference"}}} +{"ae":{"0534243126_026212274X":{"source":"0534243126","directed":false,"target":"026212274X"}}} +{"an":{"0393957357":{"label":"Statistical Inf...","full-label":"Statistical Inference"}}} +{"ae":{"0534243126_0393957357":{"source":"0534243126","directed":false,"target":"0393957357"}}} +{"cn":{"052168689X":{"label":"Data Analysis U...","full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models"}}} +{"an":{"159385191X":{"label":"Data Analysis U...","full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models"}}} +{"ae":{"052168689X_159385191X":{"source":"052168689X","directed":false,"target":"159385191X"}}} +{"ae":{"0387713840_052168689X":{"source":"0387713840","directed":false,"target":"052168689X"}}} +{"an":{"0521671930":{"label":"Data Analysis U...","full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models"}}} +{"ae":{"052168689X_0521671930":{"source":"052168689X","directed":false,"target":"0521671930"}}} +{"an":{"0470510242":{"label":"Data Analysis U...","full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models"}}} +{"ae":{"052168689X_0470510242":{"source":"052168689X","directed":false,"target":"0470510242"}}} +{"cn":{"0387212396":{"label":"Monte Carlo Sta...","full-label":"Monte Carlo Statistical Methods (Springer Texts in Statistics)"}}} +{"an":{"0387763694":{"label":"Monte Carlo Sta...","full-label":"Monte Carlo Statistical Methods (Springer Texts in Statistics)"}}} +{"ae":{"0387212396_0387763694":{"source":"0387212396","directed":false,"target":"0387763694"}}} +{"ae":{"052168689X_0387212396":{"source":"052168689X","directed":false,"target":"0387212396"}}} +{"ae":{"0387952845_0387212396":{"source":"0387952845","directed":false,"target":"0387212396"}}} +{"an":{"0387715983":{"label":"Monte Carlo Sta...","full-label":"Monte Carlo Statistical Methods (Springer Texts in Statistics)"}}} +{"ae":{"0387212396_0387715983":{"source":"0387212396","directed":false,"target":"0387715983"}}} +{"cn":{"0387713840":{"label":"Bayesian Comput...","full-label":"Bayesian Computation with R (Use R)"}}} +{"ae":{"0470510242_0387713840":{"source":"0470510242","directed":false,"target":"0387713840"}}} +{"an":{"0387389792":{"label":"Bayesian Comput...","full-label":"Bayesian Computation with R (Use R)"}}} +{"ae":{"0387713840_0387389792":{"source":"0387713840","directed":false,"target":"0387389792"}}} +{"an":{"0387759689":{"label":"Bayesian Comput...","full-label":"Bayesian Computation with R (Use R)"}}} +{"ae":{"0387713840_0387759689":{"source":"0387713840","directed":false,"target":"0387759689"}}} +{"an":{"0387747303":{"label":"Bayesian Comput...","full-label":"Bayesian Computation with R (Use R)"}}} +{"ae":{"0387713840_0387747303":{"source":"0387713840","directed":false,"target":"0387747303"}}} +{"cn":{"0340814055":{"label":"Bayesian Statis...","full-label":"Bayesian Statistics: An Introduction (Arnold Publication)"}}} +{"an":{"0198568320":{"label":"Bayesian Statis...","full-label":"Bayesian Statistics: An Introduction (Arnold Publication)"}}} +{"ae":{"0340814055_0198568320":{"source":"0340814055","directed":false,"target":"0198568320"}}} +{"ae":{"052168689X_0340814055":{"source":"052168689X","directed":false,"target":"0340814055"}}} +{"ae":{"0387212396_0340814055":{"source":"0387212396","directed":false,"target":"0340814055"}}} +{"an":{"0470141158":{"label":"Bayesian Statis...","full-label":"Bayesian Statistics: An Introduction (Arnold Publication)"}}} +{"ae":{"0340814055_0470141158":{"source":"0340814055","directed":false,"target":"0470141158"}}} +{"cn":{"0130085197":{"label":"Digital Image P...","full-label":"Digital Image Processing Using MATLAB(R)"}}} +{"an":{"013168728X":{"label":"Digital Image P...","full-label":"Digital Image Processing Using MATLAB(R)"}}} +{"ae":{"0130085197_013168728X":{"source":"0130085197","directed":false,"target":"013168728X"}}} +{"ae":{"0471056693_0130085197":{"source":"0471056693","directed":false,"target":"0130085197"}}} +{"an":{"0534400116":{"label":"Digital Image P...","full-label":"Digital Image Processing Using MATLAB(R)"}}} +{"ae":{"0130085197_0534400116":{"source":"0130085197","directed":false,"target":"0534400116"}}} +{"an":{"0470108770":{"label":"Digital Image P...","full-label":"Digital Image Processing Using MATLAB(R)"}}} +{"ae":{"0130085197_0470108770":{"source":"0130085197","directed":false,"target":"0470108770"}}} +{"cn":{"0198538642":{"label":"Neural Networks...","full-label":"Neural Networks for Pattern Recognition"}}} +{"ae":{"0387310738_0198538642":{"source":"0387310738","directed":false,"target":"0198538642"}}} +{"ae":{"0471056693_0198538642":{"source":"0471056693","directed":false,"target":"0198538642"}}} +{"an":{"0132733501":{"label":"Neural Networks...","full-label":"Neural Networks for Pattern Recognition"}}} +{"ae":{"0198538642_0132733501":{"source":"0198538642","directed":false,"target":"0132733501"}}} +{"an":{"0521717701":{"label":"Neural Networks...","full-label":"Neural Networks for Pattern Recognition"}}} +{"ae":{"0198538642_0521717701":{"source":"0198538642","directed":false,"target":"0521717701"}}} +{"cn":{"0321321367":{"label":"Introduction to...","full-label":"Introduction to Data Mining"}}} +{"ae":{"0120884070_0321321367":{"source":"0120884070","directed":false,"target":"0321321367"}}} +{"ae":{"0387310738_0321321367":{"source":"0387310738","directed":false,"target":"0321321367"}}} +{"ae":{"0387952845_0321321367":{"source":"0387952845","directed":false,"target":"0321321367"}}} +{"ae":{"0596529325_0321321367":{"source":"0596529325","directed":false,"target":"0321321367"}}} +{"cn":{"0321356985":{"label":"Practical Busin...","full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)"}}} +{"an":{"1590598342":{"label":"Practical Busin...","full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)"}}} +{"ae":{"0321356985_1590598342":{"source":"0321356985","directed":false,"target":"1590598342"}}} +{"an":{"0471267155":{"label":"Practical Busin...","full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)"}}} +{"ae":{"0321356985_0471267155":{"source":"0321356985","directed":false,"target":"0471267155"}}} +{"an":{"0072260904":{"label":"Practical Busin...","full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)"}}} +{"ae":{"0321356985_0072260904":{"source":"0321356985","directed":false,"target":"0072260904"}}} +{"an":{"0672327821":{"label":"Practical Busin...","full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)"}}} +{"ae":{"0321356985_0672327821":{"source":"0321356985","directed":false,"target":"0672327821"}}} +{"cn":{"0521780195":{"label":"An Introduction...","full-label":"An Introduction to Support Vector Machines and Other Kernel-based Learning Methods"}}} +{"ae":{"0521813972_0521780195":{"source":"0521813972","directed":false,"target":"0521780195"}}} +{"ae":{"0387310738_0521780195":{"source":"0387310738","directed":false,"target":"0521780195"}}} +{"ae":{"0387952845_0521780195":{"source":"0387952845","directed":false,"target":"0521780195"}}} +{"ae":{"0120884070_0521780195":{"source":"0120884070","directed":false,"target":"0521780195"}}} +{"cn":{"0521813972":{"label":"Kernel Methods ...","full-label":"Kernel Methods for Pattern Analysis"}}} +{"ae":{"0387310738_0521813972":{"source":"0387310738","directed":false,"target":"0521813972"}}} +{"ae":{"0387952845_0521813972":{"source":"0387952845","directed":false,"target":"0521813972"}}} +{"ae":{"0471056693_0521813972":{"source":"0471056693","directed":false,"target":"0521813972"}}} +{"cn":{"0262026171":{"label":"Predicting Stru...","full-label":"Predicting Structured Data (Neural Information Processing)"}}} +{"an":{"0262072882":{"label":"Predicting Stru...","full-label":"Predicting Structured Data (Neural Information Processing)"}}} +{"ae":{"0262026171_0262072882":{"source":"0262026171","directed":false,"target":"0262072882"}}} +{"ae":{"0262026252_0262026171":{"source":"0262026252","directed":false,"target":"0262026171"}}} +{"ae":{"0387310738_0262026171":{"source":"0387310738","directed":false,"target":"0262026171"}}} +{"ae":{"0596529325_0262026171":{"source":"0596529325","directed":false,"target":"0262026171"}}} +{"cn":{"0262026252":{"label":"Large-Scale Ker...","full-label":"Large-Scale Kernel Machines (Neural Information Processing)"}}} +{"ae":{"0262072882_0262026252":{"source":"0262072882","directed":false,"target":"0262026252"}}} +{"ae":{"0596529325_0262026252":{"source":"0596529325","directed":false,"target":"0262026252"}}} +{"an":{"0262072815":{"label":"Large-Scale Ker...","full-label":"Large-Scale Kernel Machines (Neural Information Processing)"}}} +{"ae":{"0262026252_0262072815":{"source":"0262026252","directed":false,"target":"0262072815"}}} +{"cn":{"0521592712":{"label":"Probability The...","full-label":"Probability Theory: The Logic of Science"}}} +{"an":{"080186982X":{"label":"Probability The...","full-label":"Probability Theory: The Logic of Science"}}} +{"ae":{"0521592712_080186982X":{"source":"0521592712","directed":false,"target":"080186982X"}}} +{"ae":{"0198568320_0521592712":{"source":"0198568320","directed":false,"target":"0521592712"}}} +{"ae":{"0387310738_0521592712":{"source":"0387310738","directed":false,"target":"0521592712"}}} +{"an":{"1602063281":{"label":"Probability The...","full-label":"Probability Theory: The Logic of Science"}}} +{"ae":{"0521592712_1602063281":{"source":"0521592712","directed":false,"target":"1602063281"}}} diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/amazon_0201485419_400.dgs b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/amazon_0201485419_400.dgs new file mode 100644 index 0000000000..5d750da318 --- /dev/null +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/amazon_0201485419_400.dgs @@ -0,0 +1,1407 @@ +DGS003 +"amazon_0201485419_400.dgs" 0 0 +an "0201485419" +cn "0201485419" "label":"Art of Computer..." "full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)" +an "0321335708" "label":"Art of Computer..." "full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)" +ae "0201485419_0321335708" "0201485419" "0321335708" +an "0201558025" "label":"Art of Computer..." "full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)" +ae "0201485419_0201558025" "0201485419" "0201558025" +an "0262032937" "label":"Art of Computer..." "full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)" +ae "0201485419_0262032937" "0201485419" "0262032937" +an "0201853930" "label":"Art of Computer..." "full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)" +ae "0201485419_0201853930" "0201485419" "0201853930" +an "0201853949" "label":"Art of Computer..." "full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)" +ae "0201485419_0201853949" "0201485419" "0201853949" +cn "0321335708" "label":"Art of Computer..." "full-label":"Art of Computer Programming, Volume 4, Fascicle 4,The: Generating All Trees--History of Combinatorial Generation (Art of Computer Programming)" +ae "0201853949_0321335708" "0201853949" "0321335708" +ae "0201853930_0321335708" "0201853930" "0321335708" +an "0201853922" "label":"Art of Computer..." "full-label":"Art of Computer Programming, Volume 4, Fascicle 4,The: Generating All Trees--History of Combinatorial Generation (Art of Computer Programming)" +ae "0321335708_0201853922" "0321335708" "0201853922" +an "0321534964" "label":"Art of Computer..." "full-label":"Art of Computer Programming, Volume 4, Fascicle 4,The: Generating All Trees--History of Combinatorial Generation (Art of Computer Programming)" +ae "0321335708_0321534964" "0321335708" "0321534964" +cn "0201558025" "label":"Concrete Mathem..." "full-label":"Concrete Mathematics: A Foundation for Computer Science (2nd Edition)" +ae "0262032937_0201558025" "0262032937" "0201558025" +an "0387948600" "label":"Concrete Mathem..." "full-label":"Concrete Mathematics: A Foundation for Computer Science (2nd Edition)" +ae "0201558025_0387948600" "0201558025" "0387948600" +an "0201657880" "label":"Concrete Mathem..." "full-label":"Concrete Mathematics: A Foundation for Computer Science (2nd Edition)" +ae "0201558025_0201657880" "0201558025" "0201657880" +ae "0321335708_0201558025" "0321335708" "0201558025" +cn "0262032937" "label":"Introduction to..." "full-label":"Introduction to Algorithms" +an "0137903952" "label":"Introduction to..." "full-label":"Introduction to Algorithms" +ae "0262032937_0137903952" "0262032937" "0137903952" +an "0534950973" "label":"Introduction to..." "full-label":"Introduction to Algorithms" +ae "0262032937_0534950973" "0262032937" "0534950973" +an "0471694665" "label":"Introduction to..." "full-label":"Introduction to Algorithms" +ae "0262032937_0471694665" "0262032937" "0471694665" +an "0123706068" "label":"Introduction to..." "full-label":"Introduction to Algorithms" +ae "0262032937_0123706068" "0262032937" "0123706068" +an "0201633612" "label":"Introduction to..." "full-label":"Introduction to Algorithms" +ae "0262032937_0201633612" "0262032937" "0201633612" +cn "0201853930" "label":"The Art of Comp..." "full-label":"The Art of Computer Programming, Volume 4, Fascicle 2: Generating All Tuples and Permutations (Art of Computer Programming)" +ae "0201853949_0201853930" "0201853949" "0201853930" +ae "0201853922_0201853930" "0201853922" "0201853930" +ae "0321534964_0201853930" "0321534964" "0201853930" +cn "0201853949" "label":"The Art of Comp..." "full-label":"The Art of Computer Programming, Volume 4, Fascicle 3: Generating All Combinations and Partitions (Art of Computer Programming)" +ae "0201853922_0201853949" "0201853922" "0201853949" +ae "0321534964_0201853949" "0321534964" "0201853949" +cn "0201853922" "label":"The Art of Comp..." "full-label":"The Art of Computer Programming, Volume 1, Fascicle 1: MMIX -- A RISC Computer for the New Millennium (Art of Computer Programming)" +ae "0321534964_0201853922" "0321534964" "0201853922" +ae "0201485419_0201853922" "0201485419" "0201853922" +cn "0321534964" "label":"The Art of Comp..." "full-label":"The Art of Computer Programming, Volume 4, Fascicle 0: Introduction to Combinatorial Algorithms and Boolean Functions (Art of Computer Programming)" +ae "0201485419_0321534964" "0201485419" "0321534964" +cn "0387948600" "label":"The Algorithm D..." "full-label":"The Algorithm Design Manual" +an "0387001638" "label":"The Algorithm D..." "full-label":"The Algorithm Design Manual" +ae "0387948600_0387001638" "0387948600" "0387001638" +ae "0262032937_0387948600" "0262032937" "0387948600" +an "0596529325" "label":"The Algorithm D..." "full-label":"The Algorithm Design Manual" +ae "0387948600_0596529325" "0387948600" "0596529325" +ae "0201657880_0387948600" "0201657880" "0387948600" +an "0596510047" "label":"The Algorithm D..." "full-label":"The Algorithm Design Manual" +ae "0387948600_0596510047" "0387948600" "0596510047" +cn "0201657880" "label":"Programming Pea..." "full-label":"Programming Pearls (2nd Edition) (ACM Press)" +an "047012167X" "label":"Programming Pea..." "full-label":"Programming Pearls (2nd Edition) (ACM Press)" +ae "0201657880_047012167X" "0201657880" "047012167X" +an "0735619670" "label":"Programming Pea..." "full-label":"Programming Pearls (2nd Edition) (ACM Press)" +ae "0201657880_0735619670" "0201657880" "0735619670" +an "020161586X" "label":"Programming Pea..." "full-label":"Programming Pearls (2nd Edition) (ACM Press)" +ae "0201657880_020161586X" "0201657880" "020161586X" +ae "0201633612_0201657880" "0201633612" "0201657880" +an "020161622X" "label":"Programming Pea..." "full-label":"Programming Pearls (2nd Edition) (ACM Press)" +ae "0201657880_020161622X" "0201657880" "020161622X" +cn "0137903952" "label":"Artificial Inte..." "full-label":"Artificial Intelligence: A Modern Approach (2nd Edition) (Prentice Hall Series in Artificial Intelligence)" +an "0387310738" "label":"Artificial Inte..." "full-label":"Artificial Intelligence: A Modern Approach (2nd Edition) (Prentice Hall Series in Artificial Intelligence)" +ae "0137903952_0387310738" "0137903952" "0387310738" +ae "0471694665_0137903952" "0471694665" "0137903952" +an "0071154671" "label":"Artificial Inte..." "full-label":"Artificial Intelligence: A Modern Approach (2nd Edition) (Prentice Hall Series in Artificial Intelligence)" +ae "0137903952_0071154671" "0137903952" "0071154671" +ae "0534950973_0137903952" "0534950973" "0137903952" +cn "0534950973" "label":"Introduction to..." "full-label":"Introduction to the Theory of Computation, Second Edition" +ae "0471694665_0534950973" "0471694665" "0534950973" +an "0716710455" "label":"Introduction to..." "full-label":"Introduction to the Theory of Computation, Second Edition" +ae "0534950973_0716710455" "0534950973" "0716710455" +an "0321486811" "label":"Introduction to..." "full-label":"Introduction to the Theory of Computation, Second Edition" +ae "0534950973_0321486811" "0534950973" "0321486811" +cn "0471694665" "label":"Operating Syste..." "full-label":"Operating System Concepts (7th Edition)" +ae "0123706068_0471694665" "0123706068" "0471694665" +an "0321497708" "label":"Operating Syste..." "full-label":"Operating System Concepts (7th Edition)" +ae "0471694665_0321497708" "0471694665" "0321497708" +cn "0123706068" "label":"Computer Organi..." "full-label":"Computer Organization and Design: The Hardware/Software Interface. Third Edition, Revised" +an "0123704901" "label":"Computer Organi..." "full-label":"Computer Organization and Design: The Hardware/Software Interface. Third Edition, Revised" +ae "0123706068_0123704901" "0123706068" "0123704901" +an "0131103628" "label":"Computer Organi..." "full-label":"Computer Organization and Design: The Hardware/Software Interface. Third Edition, Revised" +ae "0123706068_0131103628" "0123706068" "0131103628" +ae "0137903952_0123706068" "0137903952" "0123706068" +cn "0201633612" "label":"Design Patterns..." "full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)" +an "0201485672" "label":"Design Patterns..." "full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)" +ae "0201633612_0201485672" "0201633612" "0201485672" +ae "0735619670_0201633612" "0735619670" "0201633612" +an "0596007124" "label":"Design Patterns..." "full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)" +ae "0201633612_0596007124" "0201633612" "0596007124" +an "0321334876" "label":"Design Patterns..." "full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)" +ae "0201633612_0321334876" "0201633612" "0321334876" +an "0321193687" "label":"Design Patterns..." "full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)" +ae "0201633612_0321193687" "0201633612" "0321193687" +cn "0387001638" "label":"Programming Cha..." "full-label":"Programming Challenges" +ae "047012167X_0387001638" "047012167X" "0387001638" +ae "0201657880_0387001638" "0201657880" "0387001638" +an "0470121688" "label":"Programming Cha..." "full-label":"Programming Challenges" +ae "0387001638_0470121688" "0387001638" "0470121688" +ae "0262032937_0387001638" "0262032937" "0387001638" +cn "0596529325" "label":"Programming Col..." "full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications" +an "0596529309" "label":"Programming Col..." "full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications" +ae "0596529325_0596529309" "0596529325" "0596529309" +an "0596529260" "label":"Programming Col..." "full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications" +ae "0596529325_0596529260" "0596529325" "0596529260" +an "0596102356" "label":"Programming Col..." "full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications" +ae "0596529325_0596102356" "0596529325" "0596102356" +ae "0596510047_0596529325" "0596510047" "0596529325" +an "0596514557" "label":"Programming Col..." "full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications" +ae "0596529325_0596514557" "0596529325" "0596514557" +cn "0596510047" "label":"Beautiful Code:..." "full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))" +an "1400082471" "label":"Beautiful Code:..." "full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))" +ae "0596510047_1400082471" "0596510047" "1400082471" +ae "0596529260_0596510047" "0596529260" "0596510047" +an "193435600X" "label":"Beautiful Code:..." "full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))" +ae "0596510047_193435600X" "0596510047" "193435600X" +an "159059844X" "label":"Beautiful Code:..." "full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))" +ae "0596510047_159059844X" "0596510047" "159059844X" +cn "047012167X" "label":"Programming Int..." "full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)" +an "B000JBY0RY" "label":"Programming Int..." "full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)" +ae "047012167X_B000JBY0RY" "047012167X" "B000JBY0RY" +an "B000ESSSN4" "label":"Programming Int..." "full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)" +ae "047012167X_B000ESSSN4" "047012167X" "B000ESSSN4" +ae "0470121688_047012167X" "0470121688" "047012167X" +an "0071495789" "label":"Programming Int..." "full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)" +ae "047012167X_0071495789" "047012167X" "0071495789" +cn "0735619670" "label":"Code Complete: ..." "full-label":"Code Complete: A Practical Handbook of Software Construction" +ae "020161622X_0735619670" "020161622X" "0735619670" +an "1556159005" "label":"Code Complete: ..." "full-label":"Code Complete: A Practical Handbook of Software Construction" +ae "0735619670_1556159005" "0735619670" "1556159005" +an "0735605351" "label":"Code Complete: ..." "full-label":"Code Complete: A Practical Handbook of Software Construction" +ae "0735619670_0735605351" "0735619670" "0735605351" +an "0201835959" "label":"Code Complete: ..." "full-label":"Code Complete: A Practical Handbook of Software Construction" +ae "0735619670_0201835959" "0735619670" "0201835959" +cn "020161586X" "label":"The Practice of..." "full-label":"The Practice of Programming (Addison-Wesley Professional Computing Series)" +ae "0735619670_020161586X" "0735619670" "020161586X" +ae "0131103628_020161586X" "0131103628" "020161586X" +ae "0201633612_020161586X" "0201633612" "020161586X" +an "013937681X" "label":"The Practice of..." "full-label":"The Practice of Programming (Addison-Wesley Professional Computing Series)" +ae "020161586X_013937681X" "020161586X" "013937681X" +cn "020161622X" "label":"The Pragmatic P..." "full-label":"The Pragmatic Programmer: From Journeyman to Master" +ae "0201835959_020161622X" "0201835959" "020161622X" +ae "0201485672_020161622X" "0201485672" "020161622X" +ae "0201633612_020161622X" "0201633612" "020161622X" +an "0932633439" "label":"The Pragmatic P..." "full-label":"The Pragmatic Programmer: From Journeyman to Master" +ae "020161622X_0932633439" "020161622X" "0932633439" +cn "0387310738" "label":"Pattern Recogni..." "full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)" +an "0387952845" "label":"Pattern Recogni..." "full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)" +ae "0387310738_0387952845" "0387310738" "0387952845" +an "0471056693" "label":"Pattern Recogni..." "full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)" +ae "0387310738_0471056693" "0387310738" "0471056693" +an "0120884070" "label":"Pattern Recogni..." "full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)" +ae "0387310738_0120884070" "0387310738" "0120884070" +an "026218253X" "label":"Pattern Recogni..." "full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)" +ae "0387310738_026218253X" "0387310738" "026218253X" +ae "0596529325_0387310738" "0596529325" "0387310738" +cn "0071154671" "label":"Machine Learnin..." "full-label":"Machine Learning (Mcgraw-Hill International Edit)" +ae "0387310738_0071154671" "0387310738" "0071154671" +ae "0120884070_0071154671" "0120884070" "0071154671" +ae "0387952845_0071154671" "0387952845" "0071154671" +ae "0471056693_0071154671" "0471056693" "0071154671" +cn "0716710455" "label":"Computers and I..." "full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)" +an "0486402584" "label":"Computers and I..." "full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)" +ae "0716710455_0486402584" "0716710455" "0486402584" +an "3540653678" "label":"Computers and I..." "full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)" +ae "0716710455_3540653678" "0716710455" "3540653678" +ae "0262032937_0716710455" "0262032937" "0716710455" +an "0201530821" "label":"Computers and I..." "full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)" +ae "0716710455_0201530821" "0716710455" "0201530821" +cn "0321486811" "label":"Compilers: Prin..." "full-label":"Compilers: Principles, Techniques, and Tools (2nd Edition)" +an "B00007FYCY" "label":"Compilers: Prin..." "full-label":"Compilers: Principles, Techniques, and Tools (2nd Edition)" +ae "0321486811_B00007FYCY" "0321486811" "B00007FYCY" +an "0126339511" "label":"Compilers: Prin..." "full-label":"Compilers: Principles, Techniques, and Tools (2nd Edition)" +ae "0321486811_0126339511" "0321486811" "0126339511" +ae "0262032937_0321486811" "0262032937" "0321486811" +ae "0137903952_0321486811" "0137903952" "0321486811" +ae "0596510047_0321486811" "0596510047" "0321486811" +cn "0321497708" "label":"Computer Networ..." "full-label":"Computer Networking: A Top-Down Approach (4th Edition)" +ae "0262032937_0321497708" "0262032937" "0321497708" +ae "0123706068_0321497708" "0123706068" "0321497708" +ae "0137903952_0321497708" "0137903952" "0321497708" +an "0321369572" "label":"Computer Networ..." "full-label":"Computer Networking: A Top-Down Approach (4th Edition)" +ae "0321497708_0321369572" "0321497708" "0321369572" +cn "0123704901" "label":"Computer Archit..." "full-label":"Computer Architecture, Fourth Edition: A Quantitative Approach" +ae "0262032937_0123704901" "0262032937" "0123704901" +ae "0471694665_0123704901" "0471694665" "0123704901" +an "1593271042" "label":"Computer Archit..." "full-label":"Computer Architecture, Fourth Edition: A Quantitative Approach" +ae "0123704901_1593271042" "0123704901" "1593271042" +ae "0321486811_0123704901" "0321486811" "0123704901" +cn "0131103628" "label":"C Programming L..." "full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)" +an "0201700735" "label":"C Programming L..." "full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)" +ae "0131103628_0201700735" "0131103628" "0201700735" +an "013089592X" "label":"C Programming L..." "full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)" +ae "0131103628_013089592X" "0131103628" "013089592X" +an "0672305100" "label":"C Programming L..." "full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)" +ae "0131103628_0672305100" "0131103628" "0672305100" +an "0201433079" "label":"C Programming L..." "full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)" +ae "0131103628_0201433079" "0131103628" "0201433079" +ae "013937681X_0131103628" "013937681X" "0131103628" +cn "0201485672" "label":"Refactoring: Im..." "full-label":"Refactoring: Improving the Design of Existing Code (The Addison-Wesley Object Technology Series)" +ae "0735619670_0201485672" "0735619670" "0201485672" +an "0321146530" "label":"Refactoring: Im..." "full-label":"Refactoring: Improving the Design of Existing Code (The Addison-Wesley Object Technology Series)" +ae "0201485672_0321146530" "0201485672" "0321146530" +an "0321127420" "label":"Refactoring: Im..." "full-label":"Refactoring: Improving the Design of Existing Code (The Addison-Wesley Object Technology Series)" +ae "0201485672_0321127420" "0201485672" "0321127420" +cn "0596007124" "label":"Head First Desi..." "full-label":"Head First Design Patterns (Head First)" +an "0596008678" "label":"Head First Desi..." "full-label":"Head First Design Patterns (Head First)" +ae "0596007124_0596008678" "0596007124" "0596008678" +an "0596009208" "label":"Head First Desi..." "full-label":"Head First Design Patterns (Head First)" +ae "0596007124_0596009208" "0596007124" "0596009208" +an "0596516681" "label":"Head First Desi..." "full-label":"Head First Design Patterns (Head First)" +ae "0596007124_0596516681" "0596007124" "0596516681" +an "0596102143" "label":"Head First Desi..." "full-label":"Head First Design Patterns (Head First)" +ae "0596007124_0596102143" "0596007124" "0596102143" +cn "0321334876" "label":"Effective C++: ..." "full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)" +an "020163371X" "label":"Effective C++: ..." "full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)" +ae "0321334876_020163371X" "0321334876" "020163371X" +an "0201749629" "label":"Effective C++: ..." "full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)" +ae "0321334876_0201749629" "0321334876" "0201749629" +ae "0201700735_0321334876" "0201700735" "0321334876" +an "0201379260" "label":"Effective C++: ..." "full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)" +ae "0321334876_0201379260" "0321334876" "0201379260" +cn "0321193687" "label":"UML Distilled: ..." "full-label":"UML Distilled: A Brief Guide to the Standard Object Modeling Language (3rd Edition) (The Addison-Wesley Object Technology Series)" +an "0131489062" "label":"UML Distilled: ..." "full-label":"UML Distilled: A Brief Guide to the Standard Object Modeling Language (3rd Edition) (The Addison-Wesley Object Technology Series)" +ae "0321193687_0131489062" "0321193687" "0131489062" +ae "0201485672_0321193687" "0201485672" "0321193687" +ae "0596007124_0321193687" "0596007124" "0321193687" +ae "0735619670_0321193687" "0735619670" "0321193687" +cn "0470121688" "label":"Puzzles for Pro..." "full-label":"Puzzles for Programmers and Pros" +ae "0201657880_0470121688" "0201657880" "0470121688" +ae "B000JBY0RY_0470121688" "B000JBY0RY" "0470121688" +ae "B000ESSSN4_0470121688" "B000ESSSN4" "0470121688" +an "0486281523" "label":"Puzzles for Pro..." "full-label":"Puzzles for Programmers and Pros" +ae "0470121688_0486281523" "0470121688" "0486281523" +cn "0596529309" "label":"High Performanc..." "full-label":"High Performance Web Sites: Essential Knowledge for Front-End Engineers" +ae "0596102356_0596529309" "0596102356" "0596529309" +ae "0596529260_0596529309" "0596529260" "0596529309" +an "0596528108" "label":"High Performanc..." "full-label":"High Performance Web Sites: Essential Knowledge for Front-End Engineers" +ae "0596529309_0596528108" "0596529309" "0596528108" +ae "0596510047_0596529309" "0596510047" "0596529309" +cn "0596529260" "label":"RESTful Web Ser..." "full-label":"RESTful Web Services" +ae "0596102356_0596529260" "0596102356" "0596529260" +an "0977616630" "label":"RESTful Web Ser..." "full-label":"RESTful Web Services" +ae "0596529260_0977616630" "0596529260" "0977616630" +cn "0596102356" "label":"Building Scalab..." "full-label":"Building Scalable Web Sites: Building, scaling, and optimizing the next generation of web applications" +an "067232699X" "label":"Building Scalab..." "full-label":"Building Scalable Web Sites: Building, scaling, and optimizing the next generation of web applications" +ae "0596102356_067232699X" "0596102356" "067232699X" +an "0596003064" "label":"Building Scalab..." "full-label":"Building Scalable Web Sites: Building, scaling, and optimizing the next generation of web applications" +ae "0596102356_0596003064" "0596102356" "0596003064" +cn "0596514557" "label":"Visualizing Dat..." "full-label":"Visualizing Data" +an "0262182629" "label":"Visualizing Dat..." "full-label":"Visualizing Data" +ae "0596514557_0262182629" "0596514557" "0262182629" +an "159059617X" "label":"Visualizing Dat..." "full-label":"Visualizing Data" +ae "0596514557_159059617X" "0596514557" "159059617X" +an "0596510519" "label":"Visualizing Dat..." "full-label":"Visualizing Data" +ae "0596514557_0596510519" "0596514557" "0596510519" +an "0321525655" "label":"Visualizing Dat..." "full-label":"Visualizing Data" +ae "0596514557_0321525655" "0596514557" "0321525655" +cn "1400082471" "label":"Dreaming in Cod..." "full-label":"Dreaming in Code: Two Dozen Programmers, Three Years, 4,732 Bugs, and One Quest for Transcendent Software" +an "1590597141" "label":"Dreaming in Cod..." "full-label":"Dreaming in Code: Two Dozen Programmers, Three Years, 4,732 Bugs, and One Quest for Transcendent Software" +ae "1400082471_1590597141" "1400082471" "1590597141" +ae "0201835959_1400082471" "0201835959" "1400082471" +ae "159059844X_1400082471" "159059844X" "1400082471" +an "1590598385" "label":"Dreaming in Cod..." "full-label":"Dreaming in Code: Two Dozen Programmers, Three Years, 4,732 Bugs, and One Quest for Transcendent Software" +ae "1400082471_1590598385" "1400082471" "1590598385" +cn "193435600X" "label":"Programming Erl..." "full-label":"Programming Erlang: Software for a Concurrent World" +ae "0596529325_193435600X" "0596529325" "193435600X" +ae "0596529260_193435600X" "0596529260" "193435600X" +an "0978739256" "label":"Programming Erl..." "full-label":"Programming Erlang: Software for a Concurrent World" +ae "193435600X_0978739256" "193435600X" "0978739256" +an "0521692695" "label":"Programming Erl..." "full-label":"Programming Erlang: Software for a Concurrent World" +ae "193435600X_0521692695" "193435600X" "0521692695" +cn "159059844X" "label":"Managing Humans..." "full-label":"Managing Humans: Biting and Humorous Tales of a Software Engineering Manager" +ae "1590598385_159059844X" "1590598385" "159059844X" +an "0596527055" "label":"Managing Humans..." "full-label":"Managing Humans: Biting and Humorous Tales of a Software Engineering Manager" +ae "159059844X_0596527055" "159059844X" "0596527055" +ae "1590597141_159059844X" "1590597141" "159059844X" +cn "B000JBY0RY" "label":"How Would You M..." "full-label":"How Would You Move Mount Fuji? Microsoft's Cult of the Puzzle - How the World's Smartest Company Selects the Most Creative Thinkers" +ae "0201657880_B000JBY0RY" "0201657880" "B000JBY0RY" +an "0071440011" "label":"How Would You M..." "full-label":"How Would You Move Mount Fuji? Microsoft's Cult of the Puzzle - How the World's Smartest Company Selects the Most Creative Thinkers" +ae "B000JBY0RY_0071440011" "B000JBY0RY" "0071440011" +an "0385242719" "label":"How Would You M..." "full-label":"How Would You Move Mount Fuji? Microsoft's Cult of the Puzzle - How the World's Smartest Company Selects the Most Creative Thinkers" +ae "B000JBY0RY_0385242719" "B000JBY0RY" "0385242719" +cn "B000ESSSN4" "label":"How Would You M..." "full-label":"How Would You Move Mount Fuji? : Microsoft's Cult of the Puzzle -- How the World's Smartest Companies Select the Most Creative Thinkers" +ae "0201657880_B000ESSSN4" "0201657880" "B000ESSSN4" +ae "0071440011_B000ESSSN4" "0071440011" "B000ESSSN4" +ae "0385242719_B000ESSSN4" "0385242719" "B000ESSSN4" +an "0970055269" "label":"How Would You M..." "full-label":"How Would You Move Mount Fuji? : Microsoft's Cult of the Puzzle -- How the World's Smartest Companies Select the Most Creative Thinkers" +ae "B000ESSSN4_0970055269" "B000ESSSN4" "0970055269" +cn "0071495789" "label":"Ace the IT Inte..." "full-label":"Ace the IT Interview (Ace the It Job Interview)" +an "0071492747" "label":"Ace the IT Inte..." "full-label":"Ace the IT Interview (Ace the It Job Interview)" +ae "0071495789_0071492747" "0071495789" "0071492747" +an "0071425470" "label":"Ace the IT Inte..." "full-label":"Ace the IT Interview (Ace the It Job Interview)" +ae "0071495789_0071425470" "0071495789" "0071425470" +an "0071387730" "label":"Ace the IT Inte..." "full-label":"Ace the IT Interview (Ace the It Job Interview)" +ae "0071495789_0071387730" "0071495789" "0071387730" +an "1402203853" "label":"Ace the IT Inte..." "full-label":"Ace the IT Interview (Ace the It Job Interview)" +ae "0071495789_1402203853" "0071495789" "1402203853" +cn "1556159005" "label":"Rapid Developme..." "full-label":"Rapid Development: Taming Wild Software Schedules" +ae "0735605351_1556159005" "0735605351" "1556159005" +an "1572316217" "label":"Rapid Developme..." "full-label":"Rapid Development: Taming Wild Software Schedules" +ae "1556159005_1572316217" "1556159005" "1572316217" +ae "0201835959_1556159005" "0201835959" "1556159005" +ae "0932633439_1556159005" "0932633439" "1556159005" +cn "0735605351" "label":"Software Estima..." "full-label":"Software Estimation: Demystifying the Black Art (Best Practices (Microsoft))" +an "0735618798" "label":"Software Estima..." "full-label":"Software Estimation: Demystifying the Black Art (Best Practices (Microsoft))" +ae "0735605351_0735618798" "0735605351" "0735618798" +ae "0932633439_0735605351" "0932633439" "0735605351" +an "0131479415" "label":"Software Estima..." "full-label":"Software Estimation: Demystifying the Black Art (Best Practices (Microsoft))" +ae "0735605351_0131479415" "0735605351" "0131479415" +cn "0201835959" "label":"The Mythical Ma..." "full-label":"The Mythical Man-Month: Essays on Software Engineering, Anniversary Edition (2nd Edition)" +ae "0932633439_0201835959" "0932633439" "0201835959" +ae "0201633612_0201835959" "0201633612" "0201835959" +cn "013937681X" "label":"Unix Programmin..." "full-label":"Unix Programming Environment (Prentice-Hall Software Series)" +an "0132017997" "label":"Unix Programmin..." "full-label":"Unix Programming Environment (Prentice-Hall Software Series)" +ae "013937681X_0132017997" "013937681X" "0132017997" +an "020103669X" "label":"Unix Programmin..." "full-label":"Unix Programming Environment (Prentice-Hall Software Series)" +ae "013937681X_020103669X" "013937681X" "020103669X" +ae "0201433079_013937681X" "0201433079" "013937681X" +cn "0932633439" "label":"Peopleware: Pro..." "full-label":"Peopleware: Productive Projects and Teams (Second Edition)" +ae "0735619670_0932633439" "0735619670" "0932633439" +an "1590593898" "label":"Peopleware: Pro..." "full-label":"Peopleware: Productive Projects and Teams (Second Edition)" +ae "0932633439_1590593898" "0932633439" "1590593898" +cn "0387952845" "label":"The Elements of..." "full-label":"The Elements of Statistical Learning" +ae "0471056693_0387952845" "0471056693" "0387952845" +ae "0120884070_0387952845" "0120884070" "0387952845" +an "0387402721" "label":"The Elements of..." "full-label":"The Elements of Statistical Learning" +ae "0387952845_0387402721" "0387952845" "0387402721" +an "158488388X" "label":"The Elements of..." "full-label":"The Elements of Statistical Learning" +ae "0387952845_158488388X" "0387952845" "158488388X" +cn "0471056693" "label":"Pattern Classif..." "full-label":"Pattern Classification (2nd Edition)" +an "0471429775" "label":"Pattern Classif..." "full-label":"Pattern Classification (2nd Edition)" +ae "0471056693_0471429775" "0471056693" "0471429775" +ae "0120884070_0471056693" "0120884070" "0471056693" +cn "0120884070" "label":"Data Mining: Pr..." "full-label":"Data Mining: Practical Machine Learning Tools and Techniques, Second Edition (Morgan Kaufmann Series in Data Management Systems)" +an "1558609016" "label":"Data Mining: Pr..." "full-label":"Data Mining: Practical Machine Learning Tools and Techniques, Second Edition (Morgan Kaufmann Series in Data Management Systems)" +ae "0120884070_1558609016" "0120884070" "1558609016" +ae "0596529325_0120884070" "0596529325" "0120884070" +an "0262012111" "label":"Data Mining: Pr..." "full-label":"Data Mining: Practical Machine Learning Tools and Techniques, Second Edition (Morgan Kaufmann Series in Data Management Systems)" +ae "0120884070_0262012111" "0120884070" "0262012111" +cn "026218253X" "label":"Gaussian Proces..." "full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)" +an "0262194759" "label":"Gaussian Proces..." "full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)" +ae "026218253X_0262194759" "026218253X" "0262194759" +an "0262033585" "label":"Gaussian Proces..." "full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)" +ae "026218253X_0262033585" "026218253X" "0262033585" +ae "0387952845_026218253X" "0387952845" "026218253X" +an "0521642981" "label":"Gaussian Proces..." "full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)" +ae "026218253X_0521642981" "026218253X" "0521642981" +cn "0486402584" "label":"Combinatorial O..." "full-label":"Combinatorial Optimization: Algorithms and Complexity" +ae "3540653678_0486402584" "3540653678" "0486402584" +an "0486247759" "label":"Combinatorial O..." "full-label":"Combinatorial Optimization: Algorithms and Complexity" +ae "0486402584_0486247759" "0486402584" "0486247759" +ae "0262032937_0486402584" "0262032937" "0486402584" +an "0486678709" "label":"Combinatorial O..." "full-label":"Combinatorial Optimization: Algorithms and Complexity" +ae "0486402584_0486678709" "0486402584" "0486678709" +cn "3540653678" "label":"Approximation A..." "full-label":"Approximation Algorithms" +an "0521474655" "label":"Approximation A..." "full-label":"Approximation Algorithms" +ae "3540653678_0521474655" "3540653678" "0521474655" +an "0521872820" "label":"Approximation A..." "full-label":"Approximation Algorithms" +ae "3540653678_0521872820" "3540653678" "0521872820" +an "0521835402" "label":"Approximation A..." "full-label":"Approximation Algorithms" +ae "3540653678_0521835402" "3540653678" "0521835402" +cn "0201530821" "label":"Computational C..." "full-label":"Computational Complexity" +ae "0534950973_0201530821" "0534950973" "0201530821" +ae "0486402584_0201530821" "0486402584" "0201530821" +ae "3540653678_0201530821" "3540653678" "0201530821" +ae "0262032937_0201530821" "0262032937" "0201530821" +cn "B00007FYCY" "label":"Lex & Yacc" "full-label":"Lex & Yacc" +an "0534939724" "label":"Lex & Yacc" "full-label":"Lex & Yacc" +ae "B00007FYCY_0534939724" "B00007FYCY" "0534939724" +ae "0262032937_B00007FYCY" "0262032937" "B00007FYCY" +an "B00007FYIJ" "label":"Lex & Yacc" "full-label":"Lex & Yacc" +ae "B00007FYCY_B00007FYIJ" "B00007FYCY" "B00007FYIJ" +ae "013089592X_B00007FYCY" "013089592X" "B00007FYCY" +cn "0126339511" "label":"Programming Lan..." "full-label":"Programming Language Pragmatics, Second Edition" +ae "0262032937_0126339511" "0262032937" "0126339511" +ae "0596510047_0126339511" "0596510047" "0126339511" +an "155860698X" "label":"Programming Lan..." "full-label":"Programming Language Pragmatics, Second Edition" +ae "0126339511_155860698X" "0126339511" "155860698X" +ae "0534950973_0126339511" "0534950973" "0126339511" +cn "0321369572" "label":"Fundamentals of..." "full-label":"Fundamentals of Database Systems (5th Edition)" +an "0072253517" "label":"Fundamentals of..." "full-label":"Fundamentals of Database Systems (5th Edition)" +ae "0321369572_0072253517" "0321369572" "0072253517" +ae "0471694665_0321369572" "0471694665" "0321369572" +ae "0262032937_0321369572" "0262032937" "0321369572" +ae "0137903952_0321369572" "0137903952" "0321369572" +cn "1593271042" "label":"Inside the Mach..." "full-label":"Inside the Machine: An Illustrated Introduction to Microprocessors and Computer Architecture" +an "1593270038" "label":"Inside the Mach..." "full-label":"Inside the Machine: An Illustrated Introduction to Microprocessors and Computer Architecture" +ae "1593271042_1593270038" "1593271042" "1593270038" +ae "0596510047_1593271042" "0596510047" "1593271042" +an "1593270658" "label":"Inside the Mach..." "full-label":"Inside the Machine: An Illustrated Introduction to Microprocessors and Computer Architecture" +ae "1593271042_1593270658" "1593271042" "1593270658" +ae "0123706068_1593271042" "0123706068" "1593271042" +cn "0201700735" "label":"The C++ Program..." "full-label":"The C++ Programming Language: Special Edition (3rd Edition)" +ae "0201379260_0201700735" "0201379260" "0201700735" +ae "0201633612_0201700735" "0201633612" "0201700735" +ae "020163371X_0201700735" "020163371X" "0201700735" +cn "013089592X" "label":"C: A Reference ..." "full-label":"C: A Reference Manual (5th Edition)" +an "0672326663" "label":"C: A Reference ..." "full-label":"C: A Reference Manual (5th Edition)" +ae "013089592X_0672326663" "013089592X" "0672326663" +an "0201179288" "label":"C: A Reference ..." "full-label":"C: A Reference Manual (5th Edition)" +ae "013089592X_0201179288" "013089592X" "0201179288" +an "0131774298" "label":"C: A Reference ..." "full-label":"C: A Reference Manual (5th Edition)" +ae "013089592X_0131774298" "013089592X" "0131774298" +ae "0201433079_013089592X" "0201433079" "013089592X" +cn "0672305100" "label":"Absolute Beginn..." "full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)" +ae "0672326663_0672305100" "0672326663" "0672305100" +an "0764570684" "label":"Absolute Beginn..." "full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)" +ae "0672305100_0764570684" "0672305100" "0764570684" +an "0672326965" "label":"Absolute Beginn..." "full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)" +ae "0672305100_0672326965" "0672305100" "0672326965" +an "0764570692" "label":"Absolute Beginn..." "full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)" +ae "0672305100_0764570692" "0672305100" "0764570692" +cn "0201433079" "label":"Advanced Progra..." "full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)" +an "0131411551" "label":"Advanced Progra..." "full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)" +ae "0201433079_0131411551" "0201433079" "0131411551" +an "0130810819" "label":"Advanced Progra..." "full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)" +ae "0201433079_0130810819" "0201433079" "0130810819" +an "0131411543" "label":"Advanced Progra..." "full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)" +ae "0201433079_0131411543" "0201433079" "0131411543" +an "0596005652" "label":"Advanced Progra..." "full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)" +ae "0201433079_0596005652" "0201433079" "0596005652" +cn "0321146530" "label":"Test Driven Dev..." "full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)" +an "0321278658" "label":"Test Driven Dev..." "full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)" +ae "0321146530_0321278658" "0321146530" "0321278658" +an "0321205685" "label":"Test Driven Dev..." "full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)" +ae "0321146530_0321205685" "0321146530" "0321205685" +an "0131177052" "label":"Test Driven Dev..." "full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)" +ae "0321146530_0131177052" "0321146530" "0131177052" +ae "0131479415_0321146530" "0131479415" "0321146530" +cn "0321127420" "label":"Patterns of Ent..." "full-label":"Patterns of Enterprise Application Architecture (The Addison-Wesley Signature Series)" +an "0321200683" "label":"Patterns of Ent..." "full-label":"Patterns of Enterprise Application Architecture (The Addison-Wesley Signature Series)" +ae "0321127420_0321200683" "0321127420" "0321200683" +an "0321125215" "label":"Patterns of Ent..." "full-label":"Patterns of Enterprise Application Architecture (The Addison-Wesley Signature Series)" +ae "0321127420_0321125215" "0321127420" "0321125215" +ae "0201633612_0321127420" "0201633612" "0321127420" +ae "0596007124_0321127420" "0596007124" "0321127420" +cn "0596008678" "label":"Head First Obje..." "full-label":"Head First Object-Oriented Analysis and Design (Head First)" +ae "0596009208_0596008678" "0596009208" "0596008678" +an "0596527357" "label":"Head First Obje..." "full-label":"Head First Object-Oriented Analysis and Design (Head First)" +ae "0596008678_0596527357" "0596008678" "0596527357" +ae "0596102143_0596008678" "0596102143" "0596008678" +an "059610197X" "label":"Head First Obje..." "full-label":"Head First Object-Oriented Analysis and Design (Head First)" +ae "0596008678_059610197X" "0596008678" "059610197X" +cn "0596009208" "label":"Head First Java..." "full-label":"Head First Java, 2nd Edition" +ae "0596516681_0596009208" "0596516681" "0596009208" +ae "059610197X_0596009208" "059610197X" "0596009208" +an "0596005717" "label":"Head First Java..." "full-label":"Head First Java, 2nd Edition" +ae "0596009208_0596005717" "0596009208" "0596005717" +cn "0596516681" "label":"Head First Serv..." "full-label":"Head First Servlets and JSP: Passing the Sun Certified Web Component Developer Exam (Brain-Friendly Guides)" +ae "0596005717_0596516681" "0596005717" "0596516681" +an "0072253606" "label":"Head First Serv..." "full-label":"Head First Servlets and JSP: Passing the Sun Certified Web Component Developer Exam (Brain-Friendly Guides)" +ae "0596516681_0072253606" "0596516681" "0072253606" +ae "059610197X_0596516681" "059610197X" "0596516681" +cn "0596102143" "label":"Head First Desi..." "full-label":"Head First Design Patterns Poster (Head First)" +ae "0596009208_0596102143" "0596009208" "0596102143" +ae "0596527357_0596102143" "0596527357" "0596102143" +ae "059610197X_0596102143" "059610197X" "0596102143" +cn "020163371X" "label":"More Effective ..." "full-label":"More Effective C++: 35 New Ways to Improve Your Programs and Designs (Addison-Wesley Professional Computing Series)" +ae "0201749629_020163371X" "0201749629" "020163371X" +ae "0201633612_020163371X" "0201633612" "020163371X" +ae "0201379260_020163371X" "0201379260" "020163371X" +cn "0201749629" "label":"Effective STL: ..." "full-label":"Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library (Addison-Wesley Professional Computing Series)" +ae "0201379260_0201749629" "0201379260" "0201749629" +ae "0201633612_0201749629" "0201633612" "0201749629" +ae "0201700735_0201749629" "0201700735" "0201749629" +cn "0201379260" "label":"The C++ Standar..." "full-label":"The C++ Standard Library: A Tutorial and Reference" +an "0201734842" "label":"The C++ Standar..." "full-label":"The C++ Standard Library: A Tutorial and Reference" +ae "0201379260_0201734842" "0201379260" "0201734842" +ae "0201633612_0201379260" "0201633612" "0201379260" +cn "0131489062" "label":"Applying UML an..." "full-label":"Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development (3rd Edition)" +ae "0596007124_0131489062" "0596007124" "0131489062" +ae "0201633612_0131489062" "0201633612" "0131489062" +an "0201702258" "label":"Applying UML an..." "full-label":"Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development (3rd Edition)" +ae "0131489062_0201702258" "0131489062" "0201702258" +ae "0201485672_0131489062" "0201485672" "0131489062" +cn "0486281523" "label":"My Best Mathema..." "full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)" +an "0486252116" "label":"My Best Mathema..." "full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)" +ae "0486281523_0486252116" "0486281523" "0486252116" +an "0486270785" "label":"My Best Mathema..." "full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)" +ae "0486281523_0486270785" "0486281523" "0486270785" +an "0486256375" "label":"My Best Mathema..." "full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)" +ae "0486281523_0486256375" "0486281523" "0486256375" +an "0486427552" "label":"My Best Mathema..." "full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)" +ae "0486281523_0486427552" "0486281523" "0486427552" +an "0970825315" "label":"My Best Mathema..." "full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)" +ae "0486281523_0970825315" "0486281523" "0970825315" +cn "0596528108" "label":"Designing Web N..." "full-label":"Designing Web Navigation: Optimizing the User Experience" +an "0596527349" "label":"Designing Web N..." "full-label":"Designing Web Navigation: Optimizing the User Experience" +ae "0596528108_0596527349" "0596528108" "0596527349" +ae "0596529325_0596528108" "0596529325" "0596528108" +an "0321344758" "label":"Designing Web N..." "full-label":"Designing Web Navigation: Optimizing the User Experience" +ae "0596528108_0321344758" "0596528108" "0321344758" +an "0596008031" "label":"Designing Web N..." "full-label":"Designing Web Navigation: Optimizing the User Experience" +ae "0596528108_0596008031" "0596528108" "0596008031" +cn "0977616630" "label":"Agile Web Devel..." "full-label":"Agile Web Development with Rails, 2nd Edition" +an "0974514055" "label":"Agile Web Devel..." "full-label":"Agile Web Development with Rails, 2nd Edition" +ae "0977616630_0974514055" "0977616630" "0974514055" +an "0977616606" "label":"Agile Web Devel..." "full-label":"Agile Web Development with Rails, 2nd Edition" +ae "0977616630_0977616606" "0977616630" "0977616606" +an "0596527446" "label":"Agile Web Devel..." "full-label":"Agile Web Development with Rails, 2nd Edition" +ae "0977616630_0596527446" "0977616630" "0596527446" +an "0596523696" "label":"Agile Web Devel..." "full-label":"Agile Web Development with Rails, 2nd Edition" +ae "0977616630_0596523696" "0977616630" "0596523696" +an "1932394699" "label":"Agile Web Devel..." "full-label":"Agile Web Development with Rails, 2nd Edition" +ae "0977616630_1932394699" "0977616630" "1932394699" +cn "067232699X" "label":"Scalable Intern..." "full-label":"Scalable Internet Architectures (Developer's Library)" +ae "0596529309_067232699X" "0596529309" "067232699X" +ae "0596529325_067232699X" "0596529325" "067232699X" +ae "0596529260_067232699X" "0596529260" "067232699X" +ae "0596003064_067232699X" "0596003064" "067232699X" +cn "0596003064" "label":"High Performanc..." "full-label":"High Performance MySQL" +an "059652708X" "label":"High Performanc..." "full-label":"High Performance MySQL" +ae "0596003064_059652708X" "0596003064" "059652708X" +an "0596100892" "label":"High Performanc..." "full-label":"High Performance MySQL" +ae "0596003064_0596100892" "0596003064" "0596100892" +ae "0596529309_0596003064" "0596529309" "0596003064" +an "159059505X" "label":"High Performanc..." "full-label":"High Performance MySQL" +ae "0596003064_159059505X" "0596003064" "159059505X" +cn "0262182629" "label":"Processing: A P..." "full-label":"Processing: A Programming Handbook for Visual Designers and Artists" +ae "159059617X_0262182629" "159059617X" "0262182629" +ae "0596510519_0262182629" "0596510519" "0262182629" +an "159200346X" "label":"Processing: A P..." "full-label":"Processing: A Programming Handbook for Visual Designers and Artists" +ae "0262182629_159200346X" "0262182629" "159200346X" +ae "0596529325_0262182629" "0596529325" "0262182629" +cn "159059617X" "label":"Processing: Cre..." "full-label":"Processing: Creative Coding and Computational Art (Foundation)" +ae "0596510519_159059617X" "0596510519" "159059617X" +ae "159200346X_159059617X" "159200346X" "159059617X" +an "0500285179" "label":"Processing: Cre..." "full-label":"Processing: Creative Coding and Computational Art (Foundation)" +ae "159059617X_0500285179" "159059617X" "0500285179" +cn "0596510519" "label":"Making Things T..." "full-label":"Making Things Talk: Practical Methods for Connecting Physical Objects" +ae "159200346X_0596510519" "159200346X" "0596510519" +an "059651428X" "label":"Making Things T..." "full-label":"Making Things Talk: Practical Methods for Connecting Physical Objects" +ae "0596510519_059651428X" "0596510519" "059651428X" +cn "0321525655" "label":"Presentation Ze..." "full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)" +an "1591841992" "label":"Presentation Ze..." "full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)" +ae "0321525655_1591841992" "0321525655" "1591841992" +an "0735623872" "label":"Presentation Ze..." "full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)" +ae "0321525655_0735623872" "0321525655" "0735623872" +an "0195320697" "label":"Presentation Ze..." "full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)" +ae "0321525655_0195320697" "0321525655" "0195320697" +an "0979777704" "label":"Presentation Ze..." "full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)" +ae "0321525655_0979777704" "0321525655" "0979777704" +an "1400064287" "label":"Presentation Ze..." "full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)" +ae "0321525655_1400064287" "0321525655" "1400064287" +cn "1590597141" "label":"Founders at Wor..." "full-label":"Founders at Work: Stories of Startups' Early Days" +an "1591840562" "label":"Founders at Wor..." "full-label":"Founders at Work: Stories of Startups' Early Days" +ae "1590597141_1591840562" "1590597141" "1591840562" +ae "0596527055_1590597141" "0596527055" "1590597141" +ae "0596510047_1590597141" "0596510047" "1590597141" +ae "0596529325_1590597141" "0596529325" "1590597141" +cn "1590598385" "label":"Smart and Gets ..." "full-label":"Smart and Gets Things Done: Joel Spolsky's Concise Guide to Finding the Best Technical Talent" +ae "1590593898_1590598385" "1590593898" "1590598385" +ae "0596510047_1590598385" "0596510047" "1590598385" +ae "1590597141_1590598385" "1590597141" "1590598385" +ae "0932633439_1590598385" "0932633439" "1590598385" +cn "0978739256" "label":"The Definitive ..." "full-label":"The Definitive ANTLR Reference: Building Domain-Specific Languages (Pragmatic Programmers)" +ae "0596510047_0978739256" "0596510047" "0978739256" +ae "0596529325_0978739256" "0596529325" "0978739256" +ae "0596529260_0978739256" "0596529260" "0978739256" +an "0321349601" "label":"The Definitive ..." "full-label":"The Definitive ANTLR Reference: Building Domain-Specific Languages (Pragmatic Programmers)" +ae "0978739256_0321349601" "0978739256" "0321349601" +cn "0521692695" "label":"Programming in ..." "full-label":"Programming in Haskell" +an "0521644089" "label":"Programming in ..." "full-label":"Programming in Haskell" +ae "0521692695_0521644089" "0521692695" "0521644089" +an "0954300696" "label":"Programming in ..." "full-label":"Programming in Haskell" +ae "0521692695_0954300696" "0521692695" "0954300696" +ae "0596510047_0521692695" "0596510047" "0521692695" +an "0521663504" "label":"Programming in ..." "full-label":"Programming in Haskell" +ae "0521692695_0521663504" "0521692695" "0521663504" +cn "0596527055" "label":"The Myths of In..." "full-label":"The Myths of Innovation" +ae "0596510047_0596527055" "0596510047" "0596527055" +ae "0596529325_0596527055" "0596529325" "0596527055" +ae "0321525655_0596527055" "0321525655" "0596527055" +cn "0071440011" "label":"How to Ace the ..." "full-label":"How to Ace the Brainteaser Interview" +ae "047012167X_0071440011" "047012167X" "0071440011" +ae "0071387730_0071440011" "0071387730" "0071440011" +an "0971015848" "label":"How to Ace the ..." "full-label":"How to Ace the Brainteaser Interview" +ae "0071440011_0971015848" "0071440011" "0971015848" +cn "0385242719" "label":"Labyrinths of R..." "full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge" +an "038541580X" "label":"Labyrinths of R..." "full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge" +ae "0385242719_038541580X" "0385242719" "038541580X" +an "0809045990" "label":"Labyrinths of R..." "full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge" +ae "0385242719_0809045990" "0385242719" "0809045990" +an "0521483476" "label":"Labyrinths of R..." "full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge" +ae "0385242719_0521483476" "0385242719" "0521483476" +cn "0970055269" "label":"Heard on the St..." "full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews" +an "0131499084" "label":"Heard on the St..." "full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews" +ae "0970055269_0131499084" "0970055269" "0131499084" +an "1581311729" "label":"Heard on the St..." "full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews" +ae "0970055269_1581311729" "0970055269" "1581311729" +an "0387401016" "label":"Heard on the St..." "full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews" +ae "0970055269_0387401016" "0970055269" "0387401016" +an "0470192739" "label":"Heard on the St..." "full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews" +ae "0970055269_0470192739" "0970055269" "0470192739" +an "1432706810" "label":"Heard on the St..." "full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews" +ae "0970055269_1432706810" "0970055269" "1432706810" +cn "0071492747" "label":"ACE the IT Resu..." "full-label":"ACE the IT Resume (Ace the It Resume)" +ae "047012167X_0071492747" "047012167X" "0071492747" +an "1593573111" "label":"ACE the IT Resu..." "full-label":"ACE the IT Resume (Ace the It Resume)" +ae "0071492747_1593573111" "0071492747" "1593573111" +an "1593571275" "label":"ACE the IT Resu..." "full-label":"ACE the IT Resume (Ace the It Resume)" +ae "0071492747_1593571275" "0071492747" "1593571275" +an "1587131560" "label":"ACE the IT Resu..." "full-label":"ACE the IT Resume (Ace the It Resume)" +ae "0071492747_1587131560" "0071492747" "1587131560" +cn "0071425470" "label":"Boost Your Inte..." "full-label":"Boost Your Interview IQ" +an "0071449825" "label":"Boost Your Inte..." "full-label":"Boost Your Interview IQ" +ae "0071425470_0071449825" "0071425470" "0071449825" +an "0970901224" "label":"Boost Your Inte..." "full-label":"Boost Your Interview IQ" +ae "0071425470_0970901224" "0071425470" "0970901224" +ae "1402203853_0071425470" "1402203853" "0071425470" +an "1564148696" "label":"Boost Your Inte..." "full-label":"Boost Your Interview IQ" +ae "0071425470_1564148696" "0071425470" "1564148696" +cn "0071387730" "label":"201 Best Questi..." "full-label":"201 Best Questions To Ask On Your Interview" +an "007016357X" "label":"201 Best Questi..." "full-label":"201 Best Questions To Ask On Your Interview" +ae "0071387730_007016357X" "0071387730" "007016357X" +ae "1402203853_0071387730" "1402203853" "0071387730" +ae "1593573111_0071387730" "1593573111" "0071387730" +an "007141827X" "label":"201 Best Questi..." "full-label":"201 Best Questions To Ask On Your Interview" +ae "0071387730_007141827X" "0071387730" "007141827X" +an "1418040002" "label":"201 Best Questi..." "full-label":"201 Best Questions To Ask On Your Interview" +ae "0071387730_1418040002" "0071387730" "1418040002" +cn "1402203853" "label":"301 Smart Answe..." "full-label":"301 Smart Answers to Tough Interview Questions" +ae "007016357X_1402203853" "007016357X" "1402203853" +an "1564147789" "label":"301 Smart Answe..." "full-label":"301 Smart Answers to Tough Interview Questions" +ae "1402203853_1564147789" "1402203853" "1564147789" +ae "1564148696_1402203853" "1564148696" "1402203853" +ae "007141827X_1402203853" "007141827X" "1402203853" +cn "1572316217" "label":"Software Projec..." "full-label":"Software Project Survival Guide (Pro -- Best Practices)" +ae "0735605351_1572316217" "0735605351" "1572316217" +ae "0735619670_1572316217" "0735619670" "1572316217" +ae "0201835959_1572316217" "0201835959" "1572316217" +ae "0735618798_1572316217" "0735618798" "1572316217" +cn "0735618798" "label":"Software Requir..." "full-label":"Software Requirements, Second Edition (Pro-Best Practices)" +an "0735622671" "label":"Software Requir..." "full-label":"Software Requirements, Second Edition (Pro-Best Practices)" +ae "0735618798_0735622671" "0735618798" "0735622671" +ae "0201702258_0735618798" "0201702258" "0735618798" +an "1576810607" "label":"Software Requir..." "full-label":"Software Requirements, Second Edition (Pro-Best Practices)" +ae "0735618798_1576810607" "0735618798" "1576810607" +ae "0735619670_0735618798" "0735619670" "0735618798" +cn "0131479415" "label":"Agile Estimatin..." "full-label":"Agile Estimating and Planning (Robert C. Martin Series)" +ae "0321205685_0131479415" "0321205685" "0131479415" +an "073561993X" "label":"Agile Estimatin..." "full-label":"Agile Estimating and Planning (Robert C. Martin Series)" +ae "0131479415_073561993X" "0131479415" "073561993X" +an "0130676349" "label":"Agile Estimatin..." "full-label":"Agile Estimating and Planning (Robert C. Martin Series)" +ae "0131479415_0130676349" "0131479415" "0130676349" +an "0131111558" "label":"Agile Estimatin..." "full-label":"Agile Estimating and Planning (Robert C. Martin Series)" +ae "0131479415_0131111558" "0131479415" "0131111558" +an "0977616649" "label":"Agile Estimatin..." "full-label":"Agile Estimating and Planning (Robert C. Martin Series)" +ae "0131479415_0977616649" "0131479415" "0977616649" +cn "0132017997" "label":"Design of the U..." "full-label":"Design of the UNIX Operating System (Prentice Hall Software Series)" +ae "0131103628_0132017997" "0131103628" "0132017997" +an "0131019082" "label":"Design of the U..." "full-label":"Design of the UNIX Operating System (Prentice Hall Software Series)" +ae "0132017997_0131019082" "0132017997" "0131019082" +an "0131429388" "label":"Design of the U..." "full-label":"Design of the UNIX Operating System (Prentice Hall Software Series)" +ae "0132017997_0131429388" "0132017997" "0131429388" +ae "0201433079_0132017997" "0201433079" "0132017997" +cn "020103669X" "label":"Software Tools" "full-label":"Software Tools" +ae "020161586X_020103669X" "020161586X" "020103669X" +ae "0131103628_020103669X" "0131103628" "020103669X" +an "020107981X" "label":"Software Tools" "full-label":"Software Tools" +ae "020103669X_020107981X" "020103669X" "020107981X" +ae "0132017997_020103669X" "0132017997" "020103669X" +cn "1590593898" "label":"Joel on Softwar..." "full-label":"Joel on Software: And on Diverse and Occasionally Related Matters That Will Prove of Interest to Software Developers, Designers, and Managers, and to Those Who, Whether by Good Fortune or Ill Luck, Work with Them in Some Capacity" +an "1590595009" "label":"Joel on Softwar..." "full-label":"Joel on Software: And on Diverse and Occasionally Related Matters That Will Prove of Interest to Software Developers, Designers, and Managers, and to Those Who, Whether by Good Fortune or Ill Luck, Work with Them in Some Capacity" +ae "1590593898_1590595009" "1590593898" "1590595009" +ae "0201835959_1590593898" "0201835959" "1590593898" +an "1893115941" "label":"Joel on Softwar..." "full-label":"Joel on Software: And on Diverse and Occasionally Related Matters That Will Prove of Interest to Software Developers, Designers, and Managers, and to Those Who, Whether by Good Fortune or Ill Luck, Work with Them in Some Capacity" +ae "1590593898_1893115941" "1590593898" "1893115941" +cn "0387402721" "label":"All of Statisti..." "full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)" +an "0387251456" "label":"All of Statisti..." "full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)" +ae "0387402721_0387251456" "0387402721" "0387251456" +ae "0387310738_0387402721" "0387310738" "0387402721" +an "0534243126" "label":"All of Statisti..." "full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)" +ae "0387402721_0534243126" "0387402721" "0534243126" +an "052168689X" "label":"All of Statisti..." "full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)" +ae "0387402721_052168689X" "0387402721" "052168689X" +cn "158488388X" "label":"Bayesian Data A..." "full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)" +ae "052168689X_158488388X" "052168689X" "158488388X" +an "0387212396" "label":"Bayesian Data A..." "full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)" +ae "158488388X_0387212396" "158488388X" "0387212396" +an "0387713840" "label":"Bayesian Data A..." "full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)" +ae "158488388X_0387713840" "158488388X" "0387713840" +an "0340814055" "label":"Bayesian Data A..." "full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)" +ae "158488388X_0340814055" "158488388X" "0340814055" +cn "0471429775" "label":"Computer Manual..." "full-label":"Computer Manual in MATLAB to Accompany Pattern Classification, Second Edition" +ae "0387310738_0471429775" "0387310738" "0471429775" +ae "0387952845_0471429775" "0387952845" "0471429775" +an "0130085197" "label":"Computer Manual..." "full-label":"Computer Manual in MATLAB to Accompany Pattern Classification, Second Edition" +ae "0471429775_0130085197" "0471429775" "0130085197" +an "0198538642" "label":"Computer Manual..." "full-label":"Computer Manual in MATLAB to Accompany Pattern Classification, Second Edition" +ae "0471429775_0198538642" "0471429775" "0198538642" +cn "1558609016" "label":"Data Mining: Co..." "full-label":"Data Mining: Concepts and Techniques, Second Edition (The Morgan Kaufmann Series in Data Management Systems)" +an "0321321367" "label":"Data Mining: Co..." "full-label":"Data Mining: Concepts and Techniques, Second Edition (The Morgan Kaufmann Series in Data Management Systems)" +ae "1558609016_0321321367" "1558609016" "0321321367" +an "0321356985" "label":"Data Mining: Co..." "full-label":"Data Mining: Concepts and Techniques, Second Edition (The Morgan Kaufmann Series in Data Management Systems)" +ae "1558609016_0321356985" "1558609016" "0321356985" +ae "0387952845_1558609016" "0387952845" "1558609016" +ae "0596529325_1558609016" "0596529325" "1558609016" +cn "0262012111" "label":"Introduction to..." "full-label":"Introduction to Machine Learning (Adaptive Computation and Machine Learning)" +ae "0387310738_0262012111" "0387310738" "0262012111" +ae "0071154671_0262012111" "0071154671" "0262012111" +ae "0387952845_0262012111" "0387952845" "0262012111" +ae "0471056693_0262012111" "0471056693" "0262012111" +cn "0262194759" "label":"Learning with K..." "full-label":"Learning with Kernels: Support Vector Machines, Regularization, Optimization, and Beyond (Adaptive Computation and Machine Learning)" +ae "0387310738_0262194759" "0387310738" "0262194759" +ae "0387952845_0262194759" "0387952845" "0262194759" +an "0521780195" "label":"Learning with K..." "full-label":"Learning with Kernels: Support Vector Machines, Regularization, Optimization, and Beyond (Adaptive Computation and Machine Learning)" +ae "0262194759_0521780195" "0262194759" "0521780195" +an "0521813972" "label":"Learning with K..." "full-label":"Learning with Kernels: Support Vector Machines, Regularization, Optimization, and Beyond (Adaptive Computation and Machine Learning)" +ae "0262194759_0521813972" "0262194759" "0521813972" +cn "0262033585" "label":"Semi-Supervised..." "full-label":"Semi-Supervised Learning (Adaptive Computation and Machine Learning)" +ae "0387310738_0262033585" "0387310738" "0262033585" +ae "0262194759_0262033585" "0262194759" "0262033585" +an "0262026171" "label":"Semi-Supervised..." "full-label":"Semi-Supervised Learning (Adaptive Computation and Machine Learning)" +ae "0262033585_0262026171" "0262033585" "0262026171" +an "0262026252" "label":"Semi-Supervised..." "full-label":"Semi-Supervised Learning (Adaptive Computation and Machine Learning)" +ae "0262033585_0262026252" "0262033585" "0262026252" +cn "0521642981" "label":"Information The..." "full-label":"Information Theory, Inference & Learning Algorithms" +ae "0387310738_0521642981" "0387310738" "0521642981" +an "0521592712" "label":"Information The..." "full-label":"Information Theory, Inference & Learning Algorithms" +ae "0521642981_0521592712" "0521642981" "0521592712" +an "0471241954" "label":"Information The..." "full-label":"Information Theory, Inference & Learning Algorithms" +ae "0521642981_0471241954" "0521642981" "0471241954" +ae "0387952845_0521642981" "0387952845" "0521642981" +cn "0486247759" "label":"Introductory Gr..." "full-label":"Introductory Graph Theory" +ae "0486678709_0486247759" "0486678709" "0486247759" +an "0387984887" "label":"Introductory Gr..." "full-label":"Introductory Graph Theory" +ae "0486247759_0387984887" "0486247759" "0387984887" +an "0486432327" "label":"Introductory Gr..." "full-label":"Introductory Graph Theory" +ae "0486247759_0486432327" "0486247759" "0486432327" +an "0070054894" "label":"Introductory Gr..." "full-label":"Introductory Graph Theory" +ae "0486247759_0070054894" "0486247759" "0070054894" +cn "0486678709" "label":"Introduction to..." "full-label":"Introduction to Graph Theory (Dover Books on Advanced Mathematics)" +an "0199218420" "label":"Introduction to..." "full-label":"Introduction to Graph Theory (Dover Books on Advanced Mathematics)" +ae "0486678709_0199218420" "0486678709" "0199218420" +ae "0070054894_0486678709" "0070054894" "0486678709" +ae "0486432327_0486678709" "0486432327" "0486678709" +cn "0521474655" "label":"Randomized Algo..." "full-label":"Randomized Algorithms" +ae "0521835402_0521474655" "0521835402" "0521474655" +ae "0486402584_0521474655" "0486402584" "0521474655" +ae "0716710455_0521474655" "0716710455" "0521474655" +ae "0521872820_0521474655" "0521872820" "0521474655" +cn "0521872820" "label":"Algorithmic Gam..." "full-label":"Algorithmic Game Theory" +an "0674341163" "label":"Algorithmic Gam..." "full-label":"Algorithmic Game Theory" +ae "0521872820_0674341163" "0521872820" "0674341163" +an "0262033429" "label":"Algorithmic Gam..." "full-label":"Algorithmic Game Theory" +ae "0521872820_0262033429" "0521872820" "0262033429" +ae "0486402584_0521872820" "0486402584" "0521872820" +an "0262650401" "label":"Algorithmic Gam..." "full-label":"Algorithmic Game Theory" +ae "0521872820_0262650401" "0521872820" "0262650401" +cn "0521835402" "label":"Probability and..." "full-label":"Probability and Computing: Randomized Algorithms and Probabilistic Analysis" +ae "0521872820_0521835402" "0521872820" "0521835402" +ae "0262032937_0521835402" "0262032937" "0521835402" +ae "0486402584_0521835402" "0486402584" "0521835402" +cn "0534939724" "label":"Compiler Constr..." "full-label":"Compiler Construction: Principles and Practice" +ae "0321486811_0534939724" "0321486811" "0534939724" +ae "0126339511_0534939724" "0126339511" "0534939724" +ae "0534950973_0534939724" "0534950973" "0534939724" +ae "013089592X_0534939724" "013089592X" "0534939724" +cn "B00007FYIJ" "label":"sed & awk (2nd ..." "full-label":"sed & awk (2nd Edition)" +an "0596005954" "label":"sed & awk (2nd ..." "full-label":"sed & awk (2nd Edition)" +ae "B00007FYIJ_0596005954" "B00007FYIJ" "0596005954" +an "0596528124" "label":"sed & awk (2nd ..." "full-label":"sed & awk (2nd Edition)" +ae "B00007FYIJ_0596528124" "B00007FYIJ" "0596528124" +an "0596009658" "label":"sed & awk (2nd ..." "full-label":"sed & awk (2nd Edition)" +ae "B00007FYIJ_0596009658" "B00007FYIJ" "0596009658" +an "B00007FYGT" "label":"sed & awk (2nd ..." "full-label":"sed & awk (2nd Edition)" +ae "B00007FYIJ_B00007FYGT" "B00007FYIJ" "B00007FYGT" +an "0596003528" "label":"sed & awk (2nd ..." "full-label":"sed & awk (2nd Edition)" +ae "B00007FYIJ_0596003528" "B00007FYIJ" "0596003528" +cn "155860698X" "label":"Engineering a C..." "full-label":"Engineering a Compiler" +ae "0321486811_155860698X" "0321486811" "155860698X" +an "1558603204" "label":"Engineering a C..." "full-label":"Engineering a Compiler" +ae "155860698X_1558603204" "155860698X" "1558603204" +an "1558602860" "label":"Engineering a C..." "full-label":"Engineering a Compiler" +ae "155860698X_1558602860" "155860698X" "1558602860" +ae "B00007FYCY_155860698X" "B00007FYCY" "155860698X" +cn "0072253517" "label":"Oracle Database..." "full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)" +an "0072231459" "label":"Oracle Database..." "full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)" +ae "0072253517_0072231459" "0072253517" "0072231459" +an "0072230665" "label":"Oracle Database..." "full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)" +ae "0072253517_0072230665" "0072253517" "0072230665" +an "0072263172" "label":"Oracle Database..." "full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)" +ae "0072253517_0072263172" "0072253517" "0072263172" +an "0072263059" "label":"Oracle Database..." "full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)" +ae "0072253517_0072263059" "0072253517" "0072263059" +an "0072230789" "label":"Oracle Database..." "full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)" +ae "0072253517_0072230789" "0072253517" "0072230789" +cn "1593270038" "label":"Write Great Cod..." "full-label":"Write Great Code: Volume 1: Understanding the Machine" +ae "1593270658_1593270038" "1593270658" "1593270038" +an "1886411972" "label":"Write Great Cod..." "full-label":"Write Great Code: Volume 1: Understanding the Machine" +ae "1593270038_1886411972" "1593270038" "1886411972" +an "0521520436" "label":"Write Great Cod..." "full-label":"Write Great Code: Volume 1: Understanding the Machine" +ae "1593270038_0521520436" "1593270038" "0521520436" +ae "0735619670_1593270038" "0735619670" "1593270038" +cn "1593270658" "label":"Write Great Cod..." "full-label":"Write Great Code, Volume 2: Thinking Low-Level, Writing High-Level" +ae "1886411972_1593270658" "1886411972" "1593270658" +ae "0521520436_1593270658" "0521520436" "1593270658" +an "1593270569" "label":"Write Great Cod..." "full-label":"Write Great Code, Volume 2: Thinking Low-Level, Writing High-Level" +ae "1593270658_1593270569" "1593270658" "1593270569" +cn "0672326663" "label":"Programming in ..." "full-label":"Programming in C (3rd Edition) (Developer's Library)" +ae "0131103628_0672326663" "0131103628" "0672326663" +ae "0672326965_0672326663" "0672326965" "0672326663" +an "0672325861" "label":"Programming in ..." "full-label":"Programming in C (3rd Edition) (Developer's Library)" +ae "0672326663_0672325861" "0672326663" "0672325861" +cn "0201179288" "label":"C Traps and Pit..." "full-label":"C Traps and Pitfalls" +ae "0131774298_0201179288" "0131774298" "0201179288" +an "0201604612" "label":"C Traps and Pit..." "full-label":"C Traps and Pitfalls" +ae "0201179288_0201604612" "0201179288" "0201604612" +an "0673999866" "label":"C Traps and Pit..." "full-label":"C Traps and Pitfalls" +ae "0201179288_0673999866" "0201179288" "0673999866" +ae "0131103628_0201179288" "0131103628" "0201179288" +cn "0131774298" "label":"Expert C Progra..." "full-label":"Expert C Programming" +ae "0131103628_0131774298" "0131103628" "0131774298" +ae "0201604612_0131774298" "0201604612" "0131774298" +ae "0673999866_0131774298" "0673999866" "0131774298" +cn "0764570684" "label":"C For Dummies, ..." "full-label":"C For Dummies, 2nd Edition" +ae "0764570692_0764570684" "0764570692" "0764570684" +an "0764568523" "label":"C For Dummies, ..." "full-label":"C For Dummies, 2nd Edition" +ae "0764570684_0764568523" "0764570684" "0764568523" +ae "0131103628_0764570684" "0131103628" "0764570684" +an "0470088702" "label":"C For Dummies, ..." "full-label":"C For Dummies, 2nd Edition" +ae "0764570684_0470088702" "0764570684" "0470088702" +cn "0672326965" "label":"C Primer Plus (..." "full-label":"C Primer Plus (5th Edition)" +an "0672326973" "label":"C Primer Plus (..." "full-label":"C Primer Plus (5th Edition)" +ae "0672326965_0672326973" "0672326965" "0672326973" +ae "0131103628_0672326965" "0131103628" "0672326965" +ae "013089592X_0672326965" "013089592X" "0672326965" +cn "0764570692" "label":"C All-in-One De..." "full-label":"C All-in-One Desk Reference for Dummies" +an "0764517953" "label":"C All-in-One De..." "full-label":"C All-in-One Desk Reference for Dummies" +ae "0764570692_0764517953" "0764570692" "0764517953" +ae "0131103628_0764570692" "0131103628" "0764570692" +an "0470124512" "label":"C All-in-One De..." "full-label":"C All-in-One Desk Reference for Dummies" +ae "0764570692_0470124512" "0764570692" "0470124512" +cn "0131411551" "label":"Unix Network Pr..." "full-label":"Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition) (Addison-Wesley Professional Computing Series)" +ae "0130810819_0131411551" "0130810819" "0131411551" +an "020163354X" "label":"Unix Network Pr..." "full-label":"Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition) (Addison-Wesley Professional Computing Series)" +ae "0131411551_020163354X" "0131411551" "020163354X" +an "0201633469" "label":"Unix Network Pr..." "full-label":"Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition) (Addison-Wesley Professional Computing Series)" +ae "0131411551_0201633469" "0131411551" "0201633469" +ae "0596005652_0131411551" "0596005652" "0131411551" +cn "0130810819" "label":"UNIX Network Pr..." "full-label":"UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition) (The Unix Networking Reference Series , Vol 2)" +ae "020163354X_0130810819" "020163354X" "0130810819" +an "013490012X" "label":"UNIX Network Pr..." "full-label":"UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition) (The Unix Networking Reference Series , Vol 2)" +ae "0130810819_013490012X" "0130810819" "013490012X" +an "0201633922" "label":"UNIX Network Pr..." "full-label":"UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition) (The Unix Networking Reference Series , Vol 2)" +ae "0130810819_0201633922" "0130810819" "0201633922" +cn "0131411543" "label":"Advanced UNIX P..." "full-label":"Advanced UNIX Programming (2nd Edition) (Addison-Wesley Professional Computing Series)" +ae "0131411551_0131411543" "0131411551" "0131411543" +ae "0130810819_0131411543" "0130810819" "0131411543" +ae "0131103628_0131411543" "0131103628" "0131411543" +an "0130424110" "label":"Advanced UNIX P..." "full-label":"Advanced UNIX Programming (2nd Edition) (Addison-Wesley Professional Computing Series)" +ae "0131411543_0130424110" "0131411543" "0130424110" +cn "0596005652" "label":"Understanding t..." "full-label":"Understanding the Linux Kernel, Third Edition" +an "B00006AVQ0" "label":"Understanding t..." "full-label":"Understanding the Linux Kernel, Third Edition" +ae "0596005652_B00006AVQ0" "0596005652" "B00006AVQ0" +an "0596002556" "label":"Understanding t..." "full-label":"Understanding the Linux Kernel, Third Edition" +ae "0596005652_0596002556" "0596005652" "0596002556" +an "0672327201" "label":"Understanding t..." "full-label":"Understanding the Linux Kernel, Third Edition" +ae "0596005652_0672327201" "0596005652" "0672327201" +an "0131679848" "label":"Understanding t..." "full-label":"Understanding the Linux Kernel, Third Edition" +ae "0596005652_0131679848" "0596005652" "0131679848" +an "059600222X" "label":"Understanding t..." "full-label":"Understanding the Linux Kernel, Third Edition" +ae "0596005652_059600222X" "0596005652" "059600222X" +cn "0321278658" "label":"Extreme Program..." "full-label":"Extreme Programming Explained: Embrace Change (2nd Edition) (The XP Series)" +ae "0130676349_0321278658" "0130676349" "0321278658" +an "0201710919" "label":"Extreme Program..." "full-label":"Extreme Programming Explained: Embrace Change (2nd Edition) (The XP Series)" +ae "0321278658_0201710919" "0321278658" "0201710919" +ae "073561993X_0321278658" "073561993X" "0321278658" +ae "0201485672_0321278658" "0201485672" "0321278658" +cn "0321205685" "label":"User Stories Ap..." "full-label":"User Stories Applied: For Agile Software Development (The Addison-Wesley Signature Series)" +ae "073561993X_0321205685" "073561993X" "0321205685" +ae "0130676349_0321205685" "0130676349" "0321205685" +ae "0977616649_0321205685" "0977616649" "0321205685" +ae "0131111558_0321205685" "0131111558" "0321205685" +cn "0131177052" "label":"Working Effecti..." "full-label":"Working Effectively with Legacy Code (Robert C. Martin Series)" +ae "0201485672_0131177052" "0201485672" "0131177052" +an "0131495054" "label":"Working Effecti..." "full-label":"Working Effectively with Legacy Code (Robert C. Martin Series)" +ae "0131177052_0131495054" "0131177052" "0131495054" +an "0321213351" "label":"Working Effecti..." "full-label":"Working Effectively with Legacy Code (Robert C. Martin Series)" +ae "0131177052_0321213351" "0131177052" "0321213351" +ae "020161622X_0131177052" "020161622X" "0131177052" +cn "0321200683" "label":"Enterprise Inte..." "full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)" +an "0131858580" "label":"Enterprise Inte..." "full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)" +ae "0321200683_0131858580" "0321200683" "0131858580" +an "0596006756" "label":"Enterprise Inte..." "full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)" +ae "0321200683_0596006756" "0321200683" "0596006756" +ae "0321125215_0321200683" "0321125215" "0321200683" +an "0132344823" "label":"Enterprise Inte..." "full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)" +ae "0321200683_0132344823" "0321200683" "0132344823" +cn "0321125215" "label":"Domain-Driven D..." "full-label":"Domain-Driven Design: Tackling Complexity in the Heart of Software" +an "0321268202" "label":"Domain-Driven D..." "full-label":"Domain-Driven Design: Tackling Complexity in the Heart of Software" +ae "0321125215_0321268202" "0321125215" "0321268202" +ae "0201485672_0321125215" "0201485672" "0321125215" +ae "0321146530_0321125215" "0321146530" "0321125215" +cn "0596527357" "label":"Head First Soft..." "full-label":"Head First Software Development (Brain-Friendly Guides)" +an "0596526849" "label":"Head First Soft..." "full-label":"Head First Software Development (Brain-Friendly Guides)" +ae "0596527357_0596526849" "0596527357" "0596526849" +an "0596514824" "label":"Head First Soft..." "full-label":"Head First Software Development (Brain-Friendly Guides)" +ae "0596527357_0596514824" "0596527357" "0596514824" +ae "0596007124_0596527357" "0596007124" "0596527357" +an "0596527748" "label":"Head First Soft..." "full-label":"Head First Software Development (Brain-Friendly Guides)" +ae "0596527357_0596527748" "0596527357" "0596527748" +cn "059610197X" "label":"Head First HTML..." "full-label":"Head First HTML with CSS & XHTML" +ae "0596527748_059610197X" "0596527748" "059610197X" +an "0596102259" "label":"Head First HTML..." "full-label":"Head First HTML with CSS & XHTML" +ae "059610197X_0596102259" "059610197X" "0596102259" +ae "0596526849_059610197X" "0596526849" "059610197X" +ae "0596007124_059610197X" "0596007124" "059610197X" +cn "0596005717" "label":"Head First EJB ..." "full-label":"Head First EJB (Brain-Friendly Study Guides; Enterprise JavaBeans)" +ae "0596007124_0596005717" "0596007124" "0596005717" +ae "0596008678_0596005717" "0596008678" "0596005717" +ae "0072253606_0596005717" "0072253606" "0596005717" +cn "0072253606" "label":"SCJP Sun Certif..." "full-label":"SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press Study Guides)" +an "1590596978" "label":"SCJP Sun Certif..." "full-label":"SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press Study Guides)" +ae "0072253606_1590596978" "0072253606" "1590596978" +ae "0596009208_0072253606" "0596009208" "0072253606" +ae "0596007124_0072253606" "0596007124" "0072253606" +an "1933988134" "label":"SCJP Sun Certif..." "full-label":"SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press Study Guides)" +ae "0072253606_1933988134" "0072253606" "1933988134" +cn "0201734842" "label":"C++ Templates: ..." "full-label":"C++ Templates: The Complete Guide" +an "0201704315" "label":"C++ Templates: ..." "full-label":"C++ Templates: The Complete Guide" +ae "0201734842_0201704315" "0201734842" "0201704315" +ae "0321334876_0201734842" "0321334876" "0201734842" +ae "0201749629_0201734842" "0201749629" "0201734842" +ae "0201700735_0201734842" "0201700735" "0201734842" +cn "0201702258" "label":"Writing Effecti..." "full-label":"Writing Effective Use Cases (The Agile Software Development Series)" +an "1592009123" "label":"Writing Effecti..." "full-label":"Writing Effective Use Cases (The Agile Software Development Series)" +ae "0201702258_1592009123" "0201702258" "1592009123" +ae "1576810607_0201702258" "1576810607" "0201702258" +an "0321419499" "label":"Writing Effecti..." "full-label":"Writing Effective Use Cases (The Agile Software Development Series)" +ae "0201702258_0321419499" "0201702258" "0321419499" +ae "0735622671_0201702258" "0735622671" "0201702258" +cn "0486252116" "label":"Entertaining Ma..." "full-label":"Entertaining Mathematical Puzzles" +ae "0486256375_0486252116" "0486256375" "0486252116" +ae "0486270785_0486252116" "0486270785" "0486252116" +ae "0486427552_0486252116" "0486427552" "0486252116" +ae "0970825315_0486252116" "0970825315" "0486252116" +cn "0486270785" "label":"The Moscow Puzz..." "full-label":"The Moscow Puzzles: 359 Mathematical Recreations (Math & Logic Puzzles)" +ae "0486256375_0486270785" "0486256375" "0486270785" +an "0393061140" "label":"The Moscow Puzz..." "full-label":"The Moscow Puzzles: 359 Mathematical Recreations (Math & Logic Puzzles)" +ae "0486270785_0393061140" "0486270785" "0393061140" +ae "0486427552_0486270785" "0486427552" "0486270785" +cn "0486256375" "label":"Perplexing Puzz..." "full-label":"Perplexing Puzzles and Tantalizing Teasers (Math & Logic Puzzles)" +ae "0970825315_0486256375" "0970825315" "0486256375" +ae "0486427552_0486256375" "0486427552" "0486256375" +cn "0486427552" "label":"Brain Busters! ..." "full-label":"Brain Busters! Mind-Stretching Puzzles in Math and Logic" +ae "0970825315_0486427552" "0970825315" "0486427552" +an "0970825307" "label":"Brain Busters! ..." "full-label":"Brain Busters! Mind-Stretching Puzzles in Math and Logic" +ae "0486427552_0970825307" "0486427552" "0970825307" +cn "0970825315" "label":"So You Think Yo..." "full-label":"So You Think You're Smart: 150 Fun and Challenging Brain Teasers" +ae "0970825307_0970825315" "0970825307" "0970825315" +an "1885003994" "label":"So You Think Yo..." "full-label":"So You Think You're Smart: 150 Fun and Challenging Brain Teasers" +ae "0970825315_1885003994" "0970825315" "1885003994" +an "0140318755" "label":"So You Think Yo..." "full-label":"So You Think You're Smart: 150 Fun and Challenging Brain Teasers" +ae "0970825315_0140318755" "0970825315" "0140318755" +cn "0596527349" "label":"Information Arc..." "full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites" +ae "0321344758_0596527349" "0321344758" "0596527349" +ae "0596008031_0596527349" "0596008031" "0596527349" +an "0321392353" "label":"Information Arc..." "full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites" +ae "0596527349_0321392353" "0596527349" "0321392353" +an "0596007655" "label":"Information Arc..." "full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites" +ae "0596527349_0596007655" "0596527349" "0596007655" +an "0735712026" "label":"Information Arc..." "full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites" +ae "0596527349_0735712026" "0596527349" "0735712026" +cn "0321344758" "label":"Don't Make Me T..." "full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition" +ae "0596008031_0321344758" "0596008031" "0321344758" +an "0321350316" "label":"Don't Make Me T..." "full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition" +ae "0321344758_0321350316" "0321344758" "0321350316" +an "0465067107" "label":"Don't Make Me T..." "full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition" +ae "0321344758_0465067107" "0321344758" "0465067107" +an "156205810X" "label":"Don't Make Me T..." "full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition" +ae "0321344758_156205810X" "0321344758" "156205810X" +cn "0596008031" "label":"Designing Inter..." "full-label":"Designing Interfaces: Patterns for Effective Interaction Design" +ae "0596007655_0596008031" "0596007655" "0596008031" +an "0262134748" "label":"Designing Inter..." "full-label":"Designing Interfaces: Patterns for Effective Interaction Design" +ae "0596008031_0262134748" "0596008031" "0262134748" +ae "0321392353_0596008031" "0321392353" "0596008031" +cn "0974514055" "label":"Programming Rub..." "full-label":"Programming Ruby: The Pragmatic Programmers' Guide, Second Edition" +ae "0596523696_0974514055" "0596523696" "0974514055" +ae "0977616606_0974514055" "0977616606" "0974514055" +an "0672328844" "label":"Programming Rub..." "full-label":"Programming Ruby: The Pragmatic Programmers' Guide, Second Edition" +ae "0974514055_0672328844" "0974514055" "0672328844" +ae "1932394699_0974514055" "1932394699" "0974514055" +cn "0977616606" "label":"Rails Recipes (..." "full-label":"Rails Recipes (Pragmatic Programmers)" +ae "1932394699_0977616606" "1932394699" "0977616606" +ae "0596523696_0977616606" "0596523696" "0977616606" +ae "0596527446_0977616606" "0596527446" "0977616606" +cn "0596527446" "label":"Ajax on Rails" "full-label":"Ajax on Rails" +an "0596527314" "label":"Ajax on Rails" "full-label":"Ajax on Rails" +ae "0596527446_0596527314" "0596527446" "0596527314" +ae "0596523696_0596527446" "0596523696" "0596527446" +ae "0974514055_0596527446" "0974514055" "0596527446" +cn "0596523696" "label":"Ruby Cookbook (..." "full-label":"Ruby Cookbook (Cookbooks (O'Reilly))" +ae "0596527314_0596523696" "0596527314" "0596523696" +ae "0672328844_0596523696" "0672328844" "0596523696" +cn "1932394699" "label":"Ruby for Rails:..." "full-label":"Ruby for Rails: Ruby Techniques for Rails Developers" +ae "0596523696_1932394699" "0596523696" "1932394699" +ae "0596527446_1932394699" "0596527446" "1932394699" +cn "059652708X" "label":"MySQL Cookbook" "full-label":"MySQL Cookbook" +an "0596101015" "label":"MySQL Cookbook" "full-label":"MySQL Cookbook" +ae "059652708X_0596101015" "059652708X" "0596101015" +ae "0596100892_059652708X" "0596100892" "059652708X" +an "0596008643" "label":"MySQL Cookbook" "full-label":"MySQL Cookbook" +ae "059652708X_0596008643" "059652708X" "0596008643" +an "0672327120" "label":"MySQL Cookbook" "full-label":"MySQL Cookbook" +ae "059652708X_0672327120" "059652708X" "0672327120" +cn "0596100892" "label":"MySQL Stored Pr..." "full-label":"MySQL Stored Procedure Programming" +ae "159059505X_0596100892" "159059505X" "0596100892" +an "0672328704" "label":"MySQL Stored Pr..." "full-label":"MySQL Stored Procedure Programming" +ae "0596100892_0672328704" "0596100892" "0672328704" +an "1590595351" "label":"MySQL Stored Pr..." "full-label":"MySQL Stored Procedure Programming" +ae "0596100892_1590595351" "0596100892" "1590595351" +cn "159059505X" "label":"Pro MySQL (Expe..." "full-label":"Pro MySQL (Expert's Voice in Open Source)" +ae "059652708X_159059505X" "059652708X" "159059505X" +ae "1590595351_159059505X" "1590595351" "159059505X" +ae "0596102356_159059505X" "0596102356" "159059505X" +cn "159200346X" "label":"Physical Comput..." "full-label":"Physical Computing: Sensing and Controlling the Physical World with Computers" +an "0071452818" "label":"Physical Comput..." "full-label":"Physical Computing: Sensing and Controlling the Physical World with Computers" +ae "159200346X_0071452818" "159200346X" "0071452818" +an "0945053312" "label":"Physical Comput..." "full-label":"Physical Computing: Sensing and Controlling the Physical World with Computers" +ae "159200346X_0945053312" "159200346X" "0945053312" +cn "0500285179" "label":"Creative Code: ..." "full-label":"Creative Code: Aesthetics + Computation" +an "0262134721" "label":"Creative Code: ..." "full-label":"Creative Code: Aesthetics + Computation" +ae "0500285179_0262134721" "0500285179" "0262134721" +ae "0262182629_0500285179" "0262182629" "0500285179" +an "0262632446" "label":"Creative Code: ..." "full-label":"Creative Code: Aesthetics + Computation" +ae "0500285179_0262632446" "0500285179" "0262632446" +an "0789305259" "label":"Creative Code: ..." "full-label":"Creative Code: Aesthetics + Computation" +ae "0500285179_0789305259" "0500285179" "0789305259" +cn "059651428X" "label":"The Best of MAK..." "full-label":"The Best of MAKE (Make)" +an "0596514921" "label":"The Best of MAK..." "full-label":"The Best of MAKE (Make)" +ae "059651428X_0596514921" "059651428X" "0596514921" +an "0596510543" "label":"The Best of MAK..." "full-label":"The Best of MAKE (Make)" +ae "059651428X_0596510543" "059651428X" "0596510543" +an "B0007RNI5K" "label":"The Best of MAK..." "full-label":"The Best of MAKE (Make)" +ae "059651428X_B0007RNI5K" "059651428X" "B0007RNI5K" +ae "159200346X_059651428X" "159200346X" "059651428X" +cn "1591841992" "label":"The Back of the..." "full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures" +an "0142005207" "label":"The Back of the..." "full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures" +ae "1591841992_0142005207" "1591841992" "0142005207" +ae "0979777704_1591841992" "0979777704" "1591841992" +an "1594482918" "label":"The Back of the..." "full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures" +ae "1591841992_1594482918" "1591841992" "1594482918" +an "006135323X" "label":"The Back of the..." "full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures" +ae "1591841992_006135323X" "1591841992" "006135323X" +cn "0735623872" "label":"Beyond Bullet P..." "full-label":"Beyond Bullet Points: Using Microsoft® Office PowerPoint® 2007 to Create Presentations That Inform, Motivate, and Inspire" +an "0131875108" "label":"Beyond Bullet P..." "full-label":"Beyond Bullet Points: Using Microsoft® Office PowerPoint® 2007 to Create Presentations That Inform, Motivate, and Inspire" +ae "0735623872_0131875108" "0735623872" "0131875108" +ae "1591841992_0735623872" "1591841992" "0735623872" +ae "0195320697_0735623872" "0195320697" "0735623872" +an "0615142230" "label":"Beyond Bullet P..." "full-label":"Beyond Bullet Points: Using Microsoft® Office PowerPoint® 2007 to Create Presentations That Inform, Motivate, and Inspire" +ae "0735623872_0615142230" "0735623872" "0615142230" +cn "0195320697" "label":"Clear and to th..." "full-label":"Clear and to the Point: 8 Psychological Principles for Compelling PowerPoint Presentations" +ae "1591841992_0195320697" "1591841992" "0195320697" +ae "0615142230_0195320697" "0615142230" "0195320697" +ae "1400064287_0195320697" "1400064287" "0195320697" +cn "0979777704" "label":"Brain Rules: 12..." "full-label":"Brain Rules: 12 Principles for Surviving and Thriving at Work, Home, and School (Book & DVD)" +an "0316113506" "label":"Brain Rules: 12..." "full-label":"Brain Rules: 12 Principles for Surviving and Thriving at Work, Home, and School (Book & DVD)" +ae "0979777704_0316113506" "0979777704" "0316113506" +an "1596912839" "label":"Brain Rules: 12..." "full-label":"Brain Rules: 12 Principles for Surviving and Thriving at Work, Home, and School (Book & DVD)" +ae "0979777704_1596912839" "0979777704" "1596912839" +ae "1594482918_0979777704" "1594482918" "0979777704" +cn "1400064287" "label":"Made to Stick: ..." "full-label":"Made to Stick: Why Some Ideas Survive and Others Die" +ae "1591841992_1400064287" "1591841992" "1400064287" +an "1591841666" "label":"Made to Stick: ..." "full-label":"Made to Stick: Why Some Ideas Survive and Others Die" +ae "1400064287_1591841666" "1400064287" "1591841666" +an "0307341518" "label":"Made to Stick: ..." "full-label":"Made to Stick: Why Some Ideas Survive and Others Die" +ae "1400064287_0307341518" "1400064287" "0307341518" +an "1591841437" "label":"Made to Stick: ..." "full-label":"Made to Stick: Why Some Ideas Survive and Others Die" +ae "1400064287_1591841437" "1400064287" "1591841437" +cn "1591840562" "label":"The Art of the ..." "full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything" +an "088730995X" "label":"The Art of the ..." "full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything" +ae "1591840562_088730995X" "1591840562" "088730995X" +an "0060517123" "label":"The Art of the ..." "full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything" +ae "1591840562_0060517123" "1591840562" "0060517123" +ae "0321525655_1591840562" "0321525655" "1591840562" +an "0971187304" "label":"The Art of the ..." "full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything" +ae "1591840562_0971187304" "1591840562" "0971187304" +cn "0321349601" "label":"Java Concurrenc..." "full-label":"Java Concurrency in Practice" +an "0596527756" "label":"Java Concurrenc..." "full-label":"Java Concurrency in Practice" +ae "0321349601_0596527756" "0321349601" "0596527756" +an "0321356683" "label":"Java Concurrenc..." "full-label":"Java Concurrency in Practice" +ae "0321349601_0321356683" "0321349601" "0321356683" +an "032133678X" "label":"Java Concurrenc..." "full-label":"Java Concurrency in Practice" +ae "0321349601_032133678X" "0321349601" "032133678X" +an "1932394885" "label":"Java Concurrenc..." "full-label":"Java Concurrency in Practice" +ae "0321349601_1932394885" "0321349601" "1932394885" +an "0201310058" "label":"Java Concurrenc..." "full-label":"Java Concurrency in Practice" +ae "0321349601_0201310058" "0321349601" "0201310058" +cn "0521644089" "label":"The Haskell Sch..." "full-label":"The Haskell School of Expression: Learning Functional Programming through Multimedia" +ae "0954300696_0521644089" "0954300696" "0521644089" +ae "193435600X_0521644089" "193435600X" "0521644089" +ae "0521663504_0521644089" "0521663504" "0521644089" +an "0201342758" "label":"The Haskell Sch..." "full-label":"The Haskell School of Expression: Learning Functional Programming through Multimedia" +ae "0521644089_0201342758" "0521644089" "0201342758" +cn "0954300696" "label":"The Haskell Roa..." "full-label":"The Haskell Road to Logic, Maths and Programming (Texts in Computing S.)" +an "0954300653" "label":"The Haskell Roa..." "full-label":"The Haskell Road to Logic, Maths and Programming (Texts in Computing S.)" +ae "0954300696_0954300653" "0954300696" "0954300653" +ae "193435600X_0954300696" "193435600X" "0954300696" +ae "0521663504_0954300696" "0521663504" "0954300696" +cn "0521663504" "label":"Purely Function..." "full-label":"Purely Functional Data Structures" +ae "193435600X_0521663504" "193435600X" "0521663504" +ae "0596510047_0521663504" "0596510047" "0521663504" +cn "0971015848" "label":"Case in Point:C..." "full-label":"Case in Point:Complete Case Interview Preparation - 5th edition" +an "1581313055" "label":"Case in Point:C..." "full-label":"Case in Point:Complete Case Interview Preparation - 5th edition" +ae "0971015848_1581313055" "0971015848" "1581313055" +an "0471757225" "label":"Case in Point:C..." "full-label":"Case in Point:Complete Case Interview Preparation - 5th edition" +ae "0971015848_0471757225" "0971015848" "0471757225" +an "0070534489" "label":"Case in Point:C..." "full-label":"Case in Point:Complete Case Interview Preparation - 5th edition" +ae "0971015848_0070534489" "0971015848" "0070534489" +an "0979003903" "label":"Case in Point:C..." "full-label":"Case in Point:Complete Case Interview Preparation - 5th edition" +ae "0971015848_0979003903" "0971015848" "0979003903" +an "0471444014" "label":"Case in Point:C..." "full-label":"Case in Point:Complete Case Interview Preparation - 5th edition" +ae "0971015848_0471444014" "0971015848" "0471444014" +cn "038541580X" "label":"Prisoner's Dile..." "full-label":"Prisoner's Dilemma" +an "0486296725" "label":"Prisoner's Dile..." "full-label":"Prisoner's Dilemma" +ae "038541580X_0486296725" "038541580X" "0486296725" +ae "0809045990_038541580X" "0809045990" "038541580X" +an "0486251012" "label":"Prisoner's Dile..." "full-label":"Prisoner's Dilemma" +ae "038541580X_0486251012" "038541580X" "0486251012" +an "0465005640" "label":"Prisoner's Dile..." "full-label":"Prisoner's Dilemma" +ae "038541580X_0465005640" "038541580X" "0465005640" +cn "0809045990" "label":"Fortune's Formu..." "full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street" +an "0812975219" "label":"Fortune's Formu..." "full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street" +ae "0809045990_0812975219" "0809045990" "0812975219" +an "047004389X" "label":"Fortune's Formu..." "full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street" +ae "0809045990_047004389X" "0809045990" "047004389X" +an "0231143729" "label":"Fortune's Formu..." "full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street" +ae "0809045990_0231143729" "0809045990" "0231143729" +an "1400063515" "label":"Fortune's Formu..." "full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street" +ae "0809045990_1400063515" "0809045990" "1400063515" +an "0471794473" "label":"Fortune's Formu..." "full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street" +ae "0809045990_0471794473" "0809045990" "0471794473" +cn "0521483476" "label":"Paradoxes" "full-label":"Paradoxes" +an "0691121273" "label":"Paradoxes" "full-label":"Paradoxes" +ae "0521483476_0691121273" "0521483476" "0691121273" +an "0415228093" "label":"Paradoxes" "full-label":"Paradoxes" +ae "0521483476_0415228093" "0521483476" "0415228093" +an "0195179862" "label":"Paradoxes" "full-label":"Paradoxes" +ae "0521483476_0195179862" "0521483476" "0195179862" +an "0486296644" "label":"Paradoxes" "full-label":"Paradoxes" +ae "0521483476_0486296644" "0521483476" "0486296644" +cn "0131499084" "label":"Options, Future..." "full-label":"Options, Futures and Other Derivatives (6th Edition)" +an "0131499068" "label":"Options, Future..." "full-label":"Options, Futures and Other Derivatives (6th Edition)" +ae "0131499084_0131499068" "0131499084" "0131499068" +ae "0387401016_0131499084" "0387401016" "0131499084" +an "0387249680" "label":"Options, Future..." "full-label":"Options, Futures and Other Derivatives (6th Edition)" +ae "0131499084_0387249680" "0131499084" "0387249680" +an "155738486X" "label":"Options, Future..." "full-label":"Options, Futures and Other Derivatives (6th Edition)" +ae "0131499084_155738486X" "0131499084" "155738486X" +cn "1581311729" "label":"Vault Guide to ..." "full-label":"Vault Guide to Advanced Finance & Quantitative Interviews" +an "1581313047" "label":"Vault Guide to ..." "full-label":"Vault Guide to Advanced Finance & Quantitative Interviews" +ae "1581311729_1581313047" "1581311729" "1581313047" +an "1581311702" "label":"Vault Guide to ..." "full-label":"Vault Guide to Advanced Finance & Quantitative Interviews" +ae "1581311729_1581311702" "1581311729" "1581311702" +an "1581315295" "label":"Vault Guide to ..." "full-label":"Vault Guide to Advanced Finance & Quantitative Interviews" +ae "1581311729_1581315295" "1581311729" "1581315295" +ae "0131499084_1581311729" "0131499084" "1581311729" +cn "0387401016" "label":"Stochastic Calc..." "full-label":"Stochastic Calculus for Finance II: Continuous-Time Models (Springer Finance)" +ae "0387249680_0387401016" "0387249680" "0387401016" +an "0387004513" "label":"Stochastic Calc..." "full-label":"Stochastic Calculus for Finance II: Continuous-Time Models (Springer Finance)" +ae "0387401016_0387004513" "0387401016" "0387004513" +an "3540221492" "label":"Stochastic Calc..." "full-label":"Stochastic Calculus for Finance II: Continuous-Time Models (Springer Finance)" +ae "0387401016_3540221492" "0387401016" "3540221492" +cn "0470192739" "label":"My Life as a Qu..." "full-label":"My Life as a Quant: Reflections on Physics and Finance" +an "0470050624" "label":"My Life as a Qu..." "full-label":"My Life as a Quant: Reflections on Physics and Finance" +ae "0470192739_0470050624" "0470192739" "0470050624" +an "0375758259" "label":"My Life as a Qu..." "full-label":"My Life as a Quant: Reflections on Physics and Finance" +ae "0470192739_0375758259" "0470192739" "0375758259" +an "0471457329" "label":"My Life as a Qu..." "full-label":"My Life as a Quant: Reflections on Physics and Finance" +ae "0470192739_0471457329" "0470192739" "0471457329" +ae "0812975219_0470192739" "0812975219" "0470192739" +cn "1432706810" "label":"Starting Your C..." "full-label":"Starting Your Career as a Wall Street Quant: A Practical, No-BS Guide to Getting a Job in Quantitative Finance and Launching a Lucrative Career" +ae "0470050624_1432706810" "0470050624" "1432706810" +ae "0470192739_1432706810" "0470192739" "1432706810" +an "0470058269" "label":"Starting Your C..." "full-label":"Starting Your Career as a Wall Street Quant: A Practical, No-BS Guide to Getting a Job in Quantitative Finance and Launching a Lucrative Career" +ae "1432706810_0470058269" "1432706810" "0470058269" +ae "0131499084_1432706810" "0131499084" "1432706810" +cn "1593573111" "label":"Resume Magic: T..." "full-label":"Resume Magic: Trade Secrets of a Professional Resume Writer (Resume Magic Trade Secrets of a Professional Resume Writer)" +an "1593573642" "label":"Resume Magic: T..." "full-label":"Resume Magic: Trade Secrets of a Professional Resume Writer (Resume Magic Trade Secrets of a Professional Resume Writer)" +ae "1593573111_1593573642" "1593573111" "1593573642" +an "081447280X" "label":"Resume Magic: T..." "full-label":"Resume Magic: Trade Secrets of a Professional Resume Writer (Resume Magic Trade Secrets of a Professional Resume Writer)" +ae "1593573111_081447280X" "1593573111" "081447280X" +ae "1402203853_1593573111" "1402203853" "1593573111" +ae "007016357X_1593573111" "007016357X" "1593573111" +cn "1593571275" "label":"Expert Resumes ..." "full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)" +an "1593570007" "label":"Expert Resumes ..." "full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)" +ae "1593571275_1593570007" "1593571275" "1593570007" +an "1593570929" "label":"Expert Resumes ..." "full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)" +ae "1593571275_1593570929" "1593571275" "1593570929" +ae "1593573111_1593571275" "1593573111" "1593571275" +an "1593573669" "label":"Expert Resumes ..." "full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)" +ae "1593571275_1593573669" "1593571275" "1593573669" +cn "1587131560" "label":"The IT Career B..." "full-label":"The IT Career Builder's Toolkit" +ae "0071495789_1587131560" "0071495789" "1587131560" +an "0072126833" "label":"The IT Career B..." "full-label":"The IT Career Builder's Toolkit" +ae "1587131560_0072126833" "1587131560" "0072126833" +an "0132253569" "label":"The IT Career B..." "full-label":"The IT Career Builder's Toolkit" +ae "1587131560_0132253569" "1587131560" "0132253569" +an "1933639261" "label":"The IT Career B..." "full-label":"The IT Career Builder's Toolkit" +ae "1587131560_1933639261" "1587131560" "1933639261" +cn "0071449825" "label":"Perfect Phrases..." "full-label":"Perfect Phrases for the Perfect Interview: Hundreds of Ready-to-Use Phrases That Succinctly Demonstrate Your Skills, Your Experience and Your Value in Any Interview Situation (Perfect Phrases)" +ae "1402203853_0071449825" "1402203853" "0071449825" +an "0071454055" "label":"Perfect Phrases..." "full-label":"Perfect Phrases for the Perfect Interview: Hundreds of Ready-to-Use Phrases That Succinctly Demonstrate Your Skills, Your Experience and Your Value in Any Interview Situation (Perfect Phrases)" +ae "0071449825_0071454055" "0071449825" "0071454055" +ae "0071387730_0071449825" "0071387730" "0071449825" +an "0071454063" "label":"Perfect Phrases..." "full-label":"Perfect Phrases for the Perfect Interview: Hundreds of Ready-to-Use Phrases That Succinctly Demonstrate Your Skills, Your Experience and Your Value in Any Interview Situation (Perfect Phrases)" +ae "0071449825_0071454063" "0071449825" "0071454063" +cn "0970901224" "label":"Interview Fitne..." "full-label":"Interview Fitness Training: A Workout with Carole Martin, the Interview Coach" +ae "0071449825_0970901224" "0071449825" "0970901224" +ae "1402203853_0970901224" "1402203853" "0970901224" +ae "1564148696_0970901224" "1564148696" "0970901224" +ae "0071387730_0970901224" "0071387730" "0970901224" +cn "1564148696" "label":"Competency-Base..." "full-label":"Competency-Based Interviews: Master the Tough New Interview Style And Give Them the Answers That Will Win You the Job" +an "156414772X" "label":"Competency-Base..." "full-label":"Competency-Based Interviews: Master the Tough New Interview Style And Give Them the Answers That Will Win You the Job" +ae "1564148696_156414772X" "1564148696" "156414772X" +ae "1564147789_1564148696" "1564147789" "1564148696" +an "0814473016" "label":"Competency-Base..." "full-label":"Competency-Based Interviews: Master the Tough New Interview Style And Give Them the Answers That Will Win You the Job" +ae "1564148696_0814473016" "1564148696" "0814473016" +ae "0071387730_1564148696" "0071387730" "1564148696" +cn "007016357X" "label":"Best Answers to..." "full-label":"Best Answers to the 201 Most Frequently Asked Interview Questions" +an "0425166864" "label":"Best Answers to..." "full-label":"Best Answers to the 201 Most Frequently Asked Interview Questions" +ae "007016357X_0425166864" "007016357X" "0425166864" +ae "007141827X_007016357X" "007141827X" "007016357X" +cn "007141827X" "label":"How to Intervie..." "full-label":"How to Interview Like a Top MBA: Job-Winning Strategies From Headhunters, Fortune 100 Recruiters, and Career Counselors" +ae "0971015848_007141827X" "0971015848" "007141827X" +ae "1564148696_007141827X" "1564148696" "007141827X" +cn "1418040002" "label":"101 Great Answe..." "full-label":"101 Great Answers to the Toughest Interview Questions" +an "1418040010" "label":"101 Great Answe..." "full-label":"101 Great Answers to the Toughest Interview Questions" +ae "1418040002_1418040010" "1418040002" "1418040010" +ae "1402203853_1418040002" "1402203853" "1418040002" +ae "007016357X_1418040002" "007016357X" "1418040002" +ae "1593573111_1418040002" "1593573111" "1418040002" +cn "1564147789" "label":"Winning Job Int..." "full-label":"Winning Job Interviews: Reduce Interview Anxiety / Outprepare the Other Candidates / Land the Job You Love" +ae "0071387730_1564147789" "0071387730" "1564147789" +ae "1593573111_1564147789" "1593573111" "1564147789" +ae "007016357X_1564147789" "007016357X" "1564147789" +cn "0735622671" "label":"More About Soft..." "full-label":"More About Software Requirements: Thorny Issues and Practical Advice" +ae "1576810607_0735622671" "1576810607" "0735622671" +ae "0735605351_0735622671" "0735605351" "0735622671" +an "0735623988" "label":"More About Soft..." "full-label":"More About Software Requirements: Thorny Issues and Practical Advice" +ae "0735622671_0735623988" "0735622671" "0735623988" +cn "1576810607" "label":"The Software Re..." "full-label":"The Software Requirements Memory Jogger: A Pocket Guide to Help Software And Business Teams Develop And Manage Requirements (Memory Jogger) (Memory Jogger)" +an "0201786060" "label":"The Software Re..." "full-label":"The Software Requirements Memory Jogger: A Pocket Guide to Help Software And Business Teams Develop And Manage Requirements (Memory Jogger) (Memory Jogger)" +ae "1576810607_0201786060" "1576810607" "0201786060" +ae "1592009123_1576810607" "1592009123" "1576810607" +cn "073561993X" "label":"Agile Project M..." "full-label":"Agile Project Management with Scrum (Microsoft Professional)" +ae "0130676349_073561993X" "0130676349" "073561993X" +an "0735623376" "label":"Agile Project M..." "full-label":"Agile Project Management with Scrum (Microsoft Professional)" +ae "073561993X_0735623376" "073561993X" "0735623376" +ae "0131111558_073561993X" "0131111558" "073561993X" +cn "0130676349" "label":"Agile Software ..." "full-label":"Agile Software Development with SCRUM (Series in Agile Software Development)" +ae "0131111558_0130676349" "0131111558" "0130676349" +ae "0735623376_0130676349" "0735623376" "0130676349" +cn "0131111558" "label":"Agile and Itera..." "full-label":"Agile and Iterative Development: A Manager's Guide (The Agile Software Development Series)" +an "0321219775" "label":"Agile and Itera..." "full-label":"Agile and Iterative Development: A Manager's Guide (The Agile Software Development Series)" +ae "0131111558_0321219775" "0131111558" "0321219775" +cn "0977616649" "label":"Agile Retrospec..." "full-label":"Agile Retrospectives: Making Good Teams Great" +ae "073561993X_0977616649" "073561993X" "0977616649" +an "0976694026" "label":"Agile Retrospec..." "full-label":"Agile Retrospectives: Making Good Teams Great" +ae "0977616649_0976694026" "0977616649" "0976694026" +an "097451408X" "label":"Agile Retrospec..." "full-label":"Agile Retrospectives: Making Good Teams Great" +ae "0977616649_097451408X" "0977616649" "097451408X" +cn "0131019082" "label":"UNIX Internals:..." "full-label":"UNIX Internals: The New Frontiers" +ae "0201433079_0131019082" "0201433079" "0131019082" +an "0201633388" "label":"UNIX Internals:..." "full-label":"UNIX Internals: The New Frontiers" +ae "0131019082_0201633388" "0131019082" "0201633388" +an "0471164836" "label":"UNIX Internals:..." "full-label":"UNIX Internals: The New Frontiers" +ae "0131019082_0471164836" "0131019082" "0471164836" +an "1573980137" "label":"UNIX Internals:..." "full-label":"UNIX Internals: The New Frontiers" +ae "0131019082_1573980137" "0131019082" "1573980137" +cn "0131429388" "label":"Operating Syste..." "full-label":"Operating Systems Design and Implementation (3rd Edition) (Prentice Hall Software Series)" +an "0136006639" "label":"Operating Syste..." "full-label":"Operating Systems Design and Implementation (3rd Edition) (Prentice Hall Software Series)" +ae "0131429388_0136006639" "0131429388" "0136006639" +an "0130661023" "label":"Operating Syste..." "full-label":"Operating Systems Design and Implementation (3rd Edition) (Prentice Hall Software Series)" +ae "0131429388_0130661023" "0131429388" "0130661023" +ae "0321486811_0131429388" "0321486811" "0131429388" +ae "0596005652_0131429388" "0596005652" "0131429388" +cn "020107981X" "label":"The AWK Program..." "full-label":"The AWK Programming Language" +an "0596000707" "label":"The AWK Program..." "full-label":"The AWK Programming Language" +ae "020107981X_0596000707" "020107981X" "0596000707" +ae "B00007FYIJ_020107981X" "B00007FYIJ" "020107981X" +ae "013937681X_020107981X" "013937681X" "020107981X" +ae "0596528124_020107981X" "0596528124" "020107981X" +cn "1590595009" "label":"The Best Softwa..." "full-label":"The Best Software Writing I: Selected and Introduced by Joel Spolsky" +ae "1590598385_1590595009" "1590598385" "1590595009" +ae "1893115941_1590595009" "1893115941" "1590595009" +ae "0932633439_1590595009" "0932633439" "1590595009" +ae "0201835959_1590595009" "0201835959" "1590595009" +cn "1893115941" "label":"User Interface ..." "full-label":"User Interface Design for Programmers" +ae "1590598385_1893115941" "1590598385" "1893115941" +ae "0321344758_1893115941" "0321344758" "1893115941" +ae "0932633439_1893115941" "0932633439" "1893115941" +cn "0387251456" "label":"All of Nonparam..." "full-label":"All of Nonparametric Statistics (Springer Texts in Statistics)" +ae "0387310738_0387251456" "0387310738" "0387251456" +ae "0387952845_0387251456" "0387952845" "0387251456" +an "0387988645" "label":"All of Nonparam..." "full-label":"All of Nonparametric Statistics (Springer Texts in Statistics)" +ae "0387251456_0387988645" "0387251456" "0387988645" +ae "052168689X_0387251456" "052168689X" "0387251456" +cn "0534243126" "label":"Statistical Inf..." "full-label":"Statistical Inference" +an "0195073401" "label":"Statistical Inf..." "full-label":"Statistical Inference" +ae "0534243126_0195073401" "0534243126" "0195073401" +an "0393957330" "label":"Statistical Inf..." "full-label":"Statistical Inference" +ae "0534243126_0393957330" "0534243126" "0393957330" +an "0691010188" "label":"Statistical Inf..." "full-label":"Statistical Inference" +ae "0534243126_0691010188" "0534243126" "0691010188" +an "026212274X" "label":"Statistical Inf..." "full-label":"Statistical Inference" +ae "0534243126_026212274X" "0534243126" "026212274X" +an "0393957357" "label":"Statistical Inf..." "full-label":"Statistical Inference" +ae "0534243126_0393957357" "0534243126" "0393957357" +cn "052168689X" "label":"Data Analysis U..." "full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models" +an "159385191X" "label":"Data Analysis U..." "full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models" +ae "052168689X_159385191X" "052168689X" "159385191X" +ae "0387713840_052168689X" "0387713840" "052168689X" +an "0521671930" "label":"Data Analysis U..." "full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models" +ae "052168689X_0521671930" "052168689X" "0521671930" +an "0470510242" "label":"Data Analysis U..." "full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models" +ae "052168689X_0470510242" "052168689X" "0470510242" +cn "0387212396" "label":"Monte Carlo Sta..." "full-label":"Monte Carlo Statistical Methods (Springer Texts in Statistics)" +an "0387763694" "label":"Monte Carlo Sta..." "full-label":"Monte Carlo Statistical Methods (Springer Texts in Statistics)" +ae "0387212396_0387763694" "0387212396" "0387763694" +ae "052168689X_0387212396" "052168689X" "0387212396" +ae "0387952845_0387212396" "0387952845" "0387212396" +an "0387715983" "label":"Monte Carlo Sta..." "full-label":"Monte Carlo Statistical Methods (Springer Texts in Statistics)" +ae "0387212396_0387715983" "0387212396" "0387715983" +cn "0387713840" "label":"Bayesian Comput..." "full-label":"Bayesian Computation with R (Use R)" +ae "0470510242_0387713840" "0470510242" "0387713840" +an "0387389792" "label":"Bayesian Comput..." "full-label":"Bayesian Computation with R (Use R)" +ae "0387713840_0387389792" "0387713840" "0387389792" +an "0387759689" "label":"Bayesian Comput..." "full-label":"Bayesian Computation with R (Use R)" +ae "0387713840_0387759689" "0387713840" "0387759689" +an "0387747303" "label":"Bayesian Comput..." "full-label":"Bayesian Computation with R (Use R)" +ae "0387713840_0387747303" "0387713840" "0387747303" +cn "0340814055" "label":"Bayesian Statis..." "full-label":"Bayesian Statistics: An Introduction (Arnold Publication)" +an "0198568320" "label":"Bayesian Statis..." "full-label":"Bayesian Statistics: An Introduction (Arnold Publication)" +ae "0340814055_0198568320" "0340814055" "0198568320" +ae "052168689X_0340814055" "052168689X" "0340814055" +ae "0387212396_0340814055" "0387212396" "0340814055" +an "0470141158" "label":"Bayesian Statis..." "full-label":"Bayesian Statistics: An Introduction (Arnold Publication)" +ae "0340814055_0470141158" "0340814055" "0470141158" +cn "0130085197" "label":"Digital Image P..." "full-label":"Digital Image Processing Using MATLAB(R)" +an "013168728X" "label":"Digital Image P..." "full-label":"Digital Image Processing Using MATLAB(R)" +ae "0130085197_013168728X" "0130085197" "013168728X" +ae "0471056693_0130085197" "0471056693" "0130085197" +an "0534400116" "label":"Digital Image P..." "full-label":"Digital Image Processing Using MATLAB(R)" +ae "0130085197_0534400116" "0130085197" "0534400116" +an "0470108770" "label":"Digital Image P..." "full-label":"Digital Image Processing Using MATLAB(R)" +ae "0130085197_0470108770" "0130085197" "0470108770" +cn "0198538642" "label":"Neural Networks..." "full-label":"Neural Networks for Pattern Recognition" +ae "0387310738_0198538642" "0387310738" "0198538642" +ae "0471056693_0198538642" "0471056693" "0198538642" +an "0132733501" "label":"Neural Networks..." "full-label":"Neural Networks for Pattern Recognition" +ae "0198538642_0132733501" "0198538642" "0132733501" +an "0521717701" "label":"Neural Networks..." "full-label":"Neural Networks for Pattern Recognition" +ae "0198538642_0521717701" "0198538642" "0521717701" +cn "0321321367" "label":"Introduction to..." "full-label":"Introduction to Data Mining" +ae "0120884070_0321321367" "0120884070" "0321321367" +ae "0387310738_0321321367" "0387310738" "0321321367" +ae "0387952845_0321321367" "0387952845" "0321321367" +ae "0596529325_0321321367" "0596529325" "0321321367" +cn "0321356985" "label":"Practical Busin..." "full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)" +an "1590598342" "label":"Practical Busin..." "full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)" +ae "0321356985_1590598342" "0321356985" "1590598342" +an "0471267155" "label":"Practical Busin..." "full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)" +ae "0321356985_0471267155" "0321356985" "0471267155" +an "0072260904" "label":"Practical Busin..." "full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)" +ae "0321356985_0072260904" "0321356985" "0072260904" +an "0672327821" "label":"Practical Busin..." "full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)" +ae "0321356985_0672327821" "0321356985" "0672327821" +cn "0521780195" "label":"An Introduction..." "full-label":"An Introduction to Support Vector Machines and Other Kernel-based Learning Methods" +ae "0521813972_0521780195" "0521813972" "0521780195" +ae "0387310738_0521780195" "0387310738" "0521780195" +ae "0387952845_0521780195" "0387952845" "0521780195" +ae "0120884070_0521780195" "0120884070" "0521780195" +cn "0521813972" "label":"Kernel Methods ..." "full-label":"Kernel Methods for Pattern Analysis" +ae "0387310738_0521813972" "0387310738" "0521813972" +ae "0387952845_0521813972" "0387952845" "0521813972" +ae "0471056693_0521813972" "0471056693" "0521813972" +cn "0262026171" "label":"Predicting Stru..." "full-label":"Predicting Structured Data (Neural Information Processing)" +an "0262072882" "label":"Predicting Stru..." "full-label":"Predicting Structured Data (Neural Information Processing)" +ae "0262026171_0262072882" "0262026171" "0262072882" +ae "0262026252_0262026171" "0262026252" "0262026171" +ae "0387310738_0262026171" "0387310738" "0262026171" +ae "0596529325_0262026171" "0596529325" "0262026171" +cn "0262026252" "label":"Large-Scale Ker..." "full-label":"Large-Scale Kernel Machines (Neural Information Processing)" +ae "0262072882_0262026252" "0262072882" "0262026252" +ae "0596529325_0262026252" "0596529325" "0262026252" +an "0262072815" "label":"Large-Scale Ker..." "full-label":"Large-Scale Kernel Machines (Neural Information Processing)" +ae "0262026252_0262072815" "0262026252" "0262072815" +cn "0521592712" "label":"Probability The..." "full-label":"Probability Theory: The Logic of Science" +an "080186982X" "label":"Probability The..." "full-label":"Probability Theory: The Logic of Science" +ae "0521592712_080186982X" "0521592712" "080186982X" +ae "0198568320_0521592712" "0198568320" "0521592712" +ae "0387310738_0521592712" "0387310738" "0521592712" +an "1602063281" "label":"Probability The..." "full-label":"Probability Theory: The Logic of Science" +ae "0521592712_1602063281" "0521592712" "1602063281" diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/triangle.dgs b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/triangle.dgs new file mode 100644 index 0000000000..186affa199 --- /dev/null +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/triangle.dgs @@ -0,0 +1,8 @@ +DGS003 +triangle 6 6 +an A +an B +an C +ae AB A B +ae BC B C +ae CA C A \ No newline at end of file From 7296367383cd6d6937198bf941652b2b9c9768fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Wed, 6 Jan 2016 21:04:04 +0100 Subject: [PATCH 03/30] StreamingImpl tests passing --- .../streaming/api/Graph2EventListener.java | 338 ++++++++---------- .../api/GraphUpdaterEventHandler.java | 6 + .../impl/StreamingControllerImpl.java | 14 +- .../test/AbstractStreamProcessorTest.java | 8 +- .../test/Graph2EventListenerTest.java | 16 +- .../streaming/test/GraphUpdaterTest.java | 38 +- .../streaming/test/JSONStreamReaderTest.java | 26 +- .../streaming/test/JSONStreamWriterTest.java | 26 +- .../gephi/streaming/test/ListenersTest.java | 32 +- .../streaming/test/StreamControllerTest.java | 4 +- .../org/gephi/streaming/test/amazon.json | 0 .../streaming/test/amazon_0201485419_400.dgs | 0 .../org/gephi/streaming/test/triangle.dgs | 0 13 files changed, 245 insertions(+), 263 deletions(-) rename modules/StreamingImpl/src/test/{java => resources}/org/gephi/streaming/test/amazon.json (100%) rename modules/StreamingImpl/src/test/{java => resources}/org/gephi/streaming/test/amazon_0201485419_400.dgs (100%) rename modules/StreamingImpl/src/test/{java => resources}/org/gephi/streaming/test/triangle.dgs (100%) diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Graph2EventListener.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Graph2EventListener.java index 75f167612e..76074f7f47 100644 --- a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Graph2EventListener.java +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Graph2EventListener.java @@ -42,23 +42,15 @@ Development and Distribution License("CDDL") (collectively, the package org.gephi.streaming.api; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; -import org.gephi.data.attributes.api.AttributeEvent; -import org.gephi.data.attributes.api.AttributeListener; -import org.gephi.data.attributes.api.AttributeRow; -import org.gephi.data.attributes.api.AttributeValue; -import org.gephi.data.properties.PropertiesColumn; +import org.gephi.graph.api.Column; import org.gephi.graph.api.Edge; -import org.gephi.graph.api.EdgeData; import org.gephi.graph.api.Graph; -import org.gephi.graph.api.GraphEvent; -import org.gephi.graph.api.GraphListener; -import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.GraphDiff; import org.gephi.graph.api.Node; -import org.gephi.graph.api.NodeData; +import org.gephi.graph.api.TableDiff; +import org.gephi.graph.impl.GraphStoreConfiguration; import org.gephi.streaming.api.event.ElementType; import org.gephi.streaming.api.event.EventType; import org.gephi.streaming.api.event.GraphEventBuilder; @@ -67,7 +59,7 @@ Development and Distribution License("CDDL") (collectively, the * * @author panisson */ -public class Graph2EventListener implements GraphListener, AttributeListener { +public class Graph2EventListener { // implements GraphListener, AttributeListener { private GraphEventHandler eventHandler; private GraphEventBuilder eventBuilder; @@ -80,181 +72,157 @@ public Graph2EventListener(Graph graph, GraphEventHandler eventHandler) { this.eventHandler = eventHandler; } - @Override - public void graphChanged(GraphEvent event) { - - switch (event.getEventType()) { - case ADD_NODES_AND_EDGES: - - if (event.getData().addedEdges() != null) - for (Edge edge: event.getData().addedEdges()) { - String edgeId = edge.getEdgeData().getId(); - org.gephi.streaming.api.event.GraphEvent e = - eventBuilder.edgeAddedEvent(edgeId, edge.getSource().getNodeData().getId(), - edge.getTarget().getNodeData().getId(), edge.isDirected(), - getEdgeAttributes(edge)); - eventHandler.handleGraphEvent(e); - } - - if (event.getData().addedNodes() != null) - for (Node node: event.getData().addedNodes()) { - String nodeId = node.getNodeData().getId(); - org.gephi.streaming.api.event.GraphEvent e = - eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, nodeId, - getNodeAttributes(node)); - eventHandler.handleGraphEvent(e); - } - break; - case MOVE_NODES: - - for (Node node: event.getData().movedNodes()) { - String nodeId = node.getNodeData().getId(); - org.gephi.streaming.api.event.GraphEvent e = - eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, nodeId, - getNodeAttributes(node)); - eventHandler.handleGraphEvent(e); - } - break; - - case REMOVE_NODES_AND_EDGES: - - if (event.getData().removedEdges() != null) - for (Edge edge: event.getData().removedEdges()) { - String edgeId = edge.getEdgeData().getId(); - org.gephi.streaming.api.event.GraphEvent e = - eventBuilder.graphEvent(ElementType.EDGE, EventType.REMOVE, edgeId, null); - eventHandler.handleGraphEvent(e); - } - - if (event.getData().removedNodes() != null) - for (Node node: event.getData().removedNodes()) { - String nodeId = node.getNodeData().getId(); - org.gephi.streaming.api.event.GraphEvent e = - eventBuilder.graphEvent(ElementType.NODE, EventType.REMOVE, nodeId, null); - eventHandler.handleGraphEvent(e); - } - break; + public void graphChanged(GraphDiff graphDiff) { + + for (Node node: graphDiff.getAddedNodes()) { + String nodeId = node.getId().toString(); + org.gephi.streaming.api.event.GraphEvent e = + eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, nodeId, + getNodeAttributes(node)); + eventHandler.handleGraphEvent(e); + } + + for (Edge edge: graphDiff.getAddedEdges()) { + String edgeId = edge.getId().toString(); + org.gephi.streaming.api.event.GraphEvent e = + eventBuilder.edgeAddedEvent(edgeId, edge.getSource().getId().toString(), + edge.getTarget().getId().toString(), edge.isDirected(), + getEdgeAttributes(edge)); + eventHandler.handleGraphEvent(e); + } + + for (Edge edge: graphDiff.getRemovedEdges()) { + String edgeId = edge.getId().toString(); + org.gephi.streaming.api.event.GraphEvent e = + eventBuilder.graphEvent(ElementType.EDGE, EventType.REMOVE, edgeId, null); + eventHandler.handleGraphEvent(e); + } + + for (Node node: graphDiff.getRemovedNodes()) { + String nodeId = node.getId().toString(); + org.gephi.streaming.api.event.GraphEvent e = + eventBuilder.graphEvent(ElementType.NODE, EventType.REMOVE, nodeId, null); + eventHandler.handleGraphEvent(e); } - } - @Override - public void attributesChanged(AttributeEvent event) { - switch (event.getEventType()) { - case ADD_COLUMN: - break; - case REMOVE_COLUMN: - break; - case SET_VALUE: - - Map> nodeChangeTable = new HashMap>(); - Map> edgeChangeTable = new HashMap>(); - List graphChangeList = new ArrayList(); - - for (int i=0; i values = nodeChangeTable.get(id); - if (values==null) { - values = new ArrayList(); - nodeChangeTable.put(id, values); - } - values.add(value); - - } else if (data instanceof EdgeData) { - EdgeData edgeData = (EdgeData)data; - id = edgeData.getId(); - if (graph.getEdge(id)==null) { - continue; - } - - List values = edgeChangeTable.get(id); - if (values==null) { - values = new ArrayList(); - edgeChangeTable.put(id, values); - } - values.add(value); - } else if (data instanceof GraphView) { - graphChangeList.add(value); - - } else { - throw new RuntimeException("Unrecognized graph object type"); - } - } - - Map graphAttributes = new HashMap(); - for (AttributeValue value: graphChangeList) { - graphAttributes.put(value.getColumn().getTitle(), value.getValue()); - } - if (!graphAttributes.isEmpty()) { - org.gephi.streaming.api.event.GraphEvent streamingEvent = - eventBuilder.graphEvent(ElementType.GRAPH, EventType.CHANGE, null, graphAttributes); - eventHandler.handleGraphEvent(streamingEvent); - } - - for (Map.Entry> entry: nodeChangeTable.entrySet()) { - Map attributes = new HashMap(); - for (AttributeValue value: entry.getValue()) { - if (value.getColumn().getIndex() != PropertiesColumn.NODE_ID.getIndex() - && value.getColumn().getIndex() != PropertiesColumn.EDGE_ID.getIndex()) - attributes.put(value.getColumn().getTitle(), value.getValue()); - } - - if (!attributes.isEmpty()) { - org.gephi.streaming.api.event.GraphEvent streamingEvent = - eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, entry.getKey(), attributes); - eventHandler.handleGraphEvent(streamingEvent); - } - } - - for (Map.Entry> entry: edgeChangeTable.entrySet()) { - Map attributes = new HashMap(); - for (AttributeValue value: entry.getValue()) { - if (value.getColumn().getIndex() != PropertiesColumn.NODE_ID.getIndex() - && value.getColumn().getIndex() != PropertiesColumn.EDGE_ID.getIndex()) - attributes.put(value.getColumn().getTitle(), value.getValue()); - } - - if (!attributes.isEmpty()) { - org.gephi.streaming.api.event.GraphEvent streamingEvent = - eventBuilder.graphEvent(ElementType.EDGE, EventType.CHANGE, entry.getKey(), attributes); - eventHandler.handleGraphEvent(streamingEvent); - } - } - - break; - } + public void attributesChanged(TableDiff tableDiff) { + //TODO: Reimplement this method + +// switch (event.getEventType()) { +// case ADD_COLUMN: +// break; +// case REMOVE_COLUMN: +// break; +// case SET_VALUE: +// +// Map> nodeChangeTable = new HashMap>(); +// Map> edgeChangeTable = new HashMap>(); +// List graphChangeList = new ArrayList(); +// +// for (int i=0; i values = nodeChangeTable.get(id); +// if (values==null) { +// values = new ArrayList(); +// nodeChangeTable.put(id, values); +// } +// values.add(value); +// +// } else if (data instanceof EdgeData) { +// EdgeData edgeData = (EdgeData)data; +// id = edgeData.getId(); +// if (graph.getEdge(id)==null) { +// continue; +// } +// +// List values = edgeChangeTable.get(id); +// if (values==null) { +// values = new ArrayList(); +// edgeChangeTable.put(id, values); +// } +// values.add(value); +// } else if (data instanceof GraphView) { +// graphChangeList.add(value); +// +// } else { +// throw new RuntimeException("Unrecognized graph object type"); +// } +// } +// +// Map graphAttributes = new HashMap(); +// for (AttributeValue value: graphChangeList) { +// graphAttributes.put(value.getColumn().getTitle(), value.getValue()); +// } +// if (!graphAttributes.isEmpty()) { +// org.gephi.streaming.api.event.GraphEvent streamingEvent = +// eventBuilder.graphEvent(ElementType.GRAPH, EventType.CHANGE, null, graphAttributes); +// eventHandler.handleGraphEvent(streamingEvent); +// } +// +// for (Map.Entry> entry: nodeChangeTable.entrySet()) { +// Map attributes = new HashMap(); +// for (AttributeValue value: entry.getValue()) { +// if (value.getColumn().getIndex() != PropertiesColumn.NODE_ID.getIndex() +// && value.getColumn().getIndex() != PropertiesColumn.EDGE_ID.getIndex()) +// attributes.put(value.getColumn().getTitle(), value.getValue()); +// } +// +// if (!attributes.isEmpty()) { +// org.gephi.streaming.api.event.GraphEvent streamingEvent = +// eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, entry.getKey(), attributes); +// eventHandler.handleGraphEvent(streamingEvent); +// } +// } +// +// for (Map.Entry> entry: edgeChangeTable.entrySet()) { +// Map attributes = new HashMap(); +// for (AttributeValue value: entry.getValue()) { +// if (value.getColumn().getIndex() != PropertiesColumn.NODE_ID.getIndex() +// && value.getColumn().getIndex() != PropertiesColumn.EDGE_ID.getIndex()) +// attributes.put(value.getColumn().getTitle(), value.getValue()); +// } +// +// if (!attributes.isEmpty()) { +// org.gephi.streaming.api.event.GraphEvent streamingEvent = +// eventBuilder.graphEvent(ElementType.EDGE, EventType.CHANGE, entry.getKey(), attributes); +// eventHandler.handleGraphEvent(streamingEvent); +// } +// } +// +// break; +// } } private Map getNodeAttributes(Node node) { Map attributes = new HashMap(); - AttributeRow row = (AttributeRow) node.getNodeData().getAttributes(); - - if (row != null) - for (AttributeValue attributeValue: row.getValues()) { - if (attributeValue.getColumn().getIndex()!=PropertiesColumn.NODE_ID.getIndex() - && attributeValue.getValue()!=null) - attributes.put(attributeValue.getColumn().getTitle(), attributeValue.getValue()); + for (Column column: node.getAttributeColumns()) { + if (column.getIndex() != GraphStoreConfiguration.ELEMENT_ID_INDEX) { + Object value = node.getAttribute(column); + if (value != null) + attributes.put(column.getTitle(), value); } + } if (sendVizData) { - attributes.put("x", node.getNodeData().x()); - attributes.put("y", node.getNodeData().y()); - attributes.put("z", node.getNodeData().z()); + attributes.put("x", node.x()); + attributes.put("y", node.y()); + attributes.put("z", node.z()); - attributes.put("r", node.getNodeData().r()); - attributes.put("g", node.getNodeData().g()); - attributes.put("b", node.getNodeData().b()); + attributes.put("r", node.r()); + attributes.put("g", node.g()); + attributes.put("b", node.b()); - attributes.put("size", node.getNodeData().getSize()); + attributes.put("size", node.size()); } return attributes; @@ -262,19 +230,19 @@ private Map getNodeAttributes(Node node) { private Map getEdgeAttributes(Edge edge) { Map attributes = new HashMap(); - AttributeRow row = (AttributeRow) edge.getEdgeData().getAttributes(); - if (row != null) - for (AttributeValue attributeValue: row.getValues()) { - if (attributeValue.getColumn().getIndex()!=PropertiesColumn.EDGE_ID.getIndex() - && attributeValue.getValue()!=null) - attributes.put(attributeValue.getColumn().getTitle(), attributeValue.getValue()); + for (Column column: edge.getAttributeColumns()) { + if (column.getIndex() != GraphStoreConfiguration.ELEMENT_ID_INDEX) { + Object value = edge.getAttribute(column); + if (value != null) + attributes.put(column.getTitle(), value); } + } if (sendVizData) { - attributes.put("r", edge.getEdgeData().r()); - attributes.put("g", edge.getEdgeData().g()); - attributes.put("b", edge.getEdgeData().b()); + attributes.put("r", edge.r()); + attributes.put("g", edge.g()); + attributes.put("b", edge.b()); attributes.put("weight", edge.getWeight()); } diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java index 7891e987e3..9e77252aea 100644 --- a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java @@ -334,6 +334,9 @@ private void addNodeAttribute(Node node, String attributeName, Object value) { } else if (!node.getAttributeKeys().isEmpty()) { + if (!graph.getModel().getNodeTable().hasColumn(attributeName)) { + graph.getModel().getNodeTable().addColumn(attributeName, value.getClass()); + } node.setAttribute(attributeName, value); } } @@ -344,6 +347,9 @@ private void addEdgeAttribute(Edge edge, String attributeName, Object value) { injectEdgeProperty(p, value, edge); } else if (! edge.getAttributeKeys().isEmpty()) { + if (!graph.getModel().getEdgeTable().hasColumn(attributeName)) { + graph.getModel().getEdgeTable().addColumn(attributeName, value.getClass()); + } edge.setAttribute(attributeName, value); } } diff --git a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamingControllerImpl.java b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamingControllerImpl.java index 6337f4fdeb..a6ff198ea3 100644 --- a/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamingControllerImpl.java +++ b/modules/StreamingImpl/src/main/java/org/gephi/streaming/impl/StreamingControllerImpl.java @@ -57,7 +57,7 @@ Development and Distribution License("CDDL") (collectively, the import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.codec.binary.Base64; -import org.gephi.data.attributes.api.AttributeController; +//import org.gephi.data.attributes.api.AttributeController; import org.gephi.graph.api.Graph; import org.gephi.streaming.api.Graph2EventListener; import org.gephi.streaming.api.GraphEventHandler; @@ -116,9 +116,9 @@ public StreamingConnection connect(StreamingEndpoint endpoint, final Graph graph ClientEventHandler handler = new ClientEventHandler(endpoint, filterededIds); final Graph2EventListener graph2EventListener = new Graph2EventListener(graph, handler); - graph.getModel().addGraphListener(graph2EventListener); - AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); - ac.getModel().addAttributeListener(graph2EventListener); +// graph.getModel().addGraphListener(graph2EventListener); +// AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); +// ac.getModel().addAttributeListener(graph2EventListener); if (report!=null) { report.setSource(endpoint.getUrl().toString()); @@ -157,9 +157,9 @@ public void handleGraphEvent(GraphEvent event) { @Override public void onConnectionClosed(StreamingConnection connection) { - graph.getModel().removeGraphListener(graph2EventListener); - AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); - ac.getModel().removeAttributeListener(graph2EventListener); +// graph.getModel().removeGraphListener(graph2EventListener); +// AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); +// ac.getModel().removeAttributeListener(graph2EventListener); container.waitForDispatchAllEvents(); container.stop(); diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/AbstractStreamProcessorTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/AbstractStreamProcessorTest.java index bc26ba3535..cc7225da8f 100644 --- a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/AbstractStreamProcessorTest.java +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/AbstractStreamProcessorTest.java @@ -296,8 +296,8 @@ public void testSynchFire() throws IOException { Workspace workspace = projectController.newWorkspace(projectController.getCurrentProject()); GraphController graphController = Lookup.getDefault().lookup(GraphController.class); - GraphModel graphModel = graphController.getModel(); - Graph graph = graphModel.getHierarchicalMixedGraph(); + GraphModel graphModel = graphController.getGraphModel(); + Graph graph = graphModel.getGraph(); GraphUpdaterEventHandler graphUpdaterHandler = new GraphUpdaterEventHandler(graph); @@ -348,8 +348,8 @@ public void testAsynchFire() throws IOException { Workspace workspace = projectController.newWorkspace(projectController.getCurrentProject()); GraphController graphController = Lookup.getDefault().lookup(GraphController.class); - GraphModel graphModel = graphController.getModel(); - Graph graph = graphModel.getHierarchicalMixedGraph(); + GraphModel graphModel = graphController.getGraphModel(); + Graph graph = graphModel.getGraph(); GraphUpdaterEventHandler graphUpdaterHandler = new GraphUpdaterEventHandler(graph); diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/Graph2EventListenerTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/Graph2EventListenerTest.java index 580832de04..2e8fe00bd9 100644 --- a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/Graph2EventListenerTest.java +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/Graph2EventListenerTest.java @@ -48,6 +48,7 @@ Development and Distribution License("CDDL") (collectively, the import org.gephi.graph.api.GraphController; import org.gephi.graph.api.GraphFactory; import org.gephi.graph.api.GraphModel; +import org.gephi.graph.api.GraphObserver; import org.gephi.graph.api.Node; import org.gephi.project.api.ProjectController; import org.gephi.project.api.Workspace; @@ -69,13 +70,14 @@ public void test() { projectController.newWorkspace(projectController.getCurrentProject()); GraphController graphController = Lookup.getDefault().lookup(GraphController.class); - GraphModel graphModel = graphController.getModel(); - Graph graph = graphModel.getHierarchicalMixedGraph(); + GraphModel graphModel = graphController.getGraphModel(); + Graph graph = graphModel.getGraph(); GraphFactory factory = graphModel.factory(); MockGraphEventHandler handler = new MockGraphEventHandler(); final Graph2EventListener graph2EventListener = new Graph2EventListener(graph, handler); - graph.getGraphModel().addGraphListener(graph2EventListener); + + GraphObserver graphObserver = graphModel.createGraphObserver(graph, true); Node node1 = factory.newNode(); graph.addNode(node1); @@ -84,14 +86,16 @@ public void test() { Edge edge = factory.newEdge(node1, node2); graph.addEdge(edge); + + assertTrue(graphObserver.hasGraphChanged()); + graph2EventListener.graphChanged(graphObserver.getDiff()); graph.removeEdge(edge); graph.removeNode(node1); graph.removeNode(node2); - try { - Thread.sleep(1000); - } catch (InterruptedException ex) { } + assertTrue(graphObserver.hasGraphChanged()); + graph2EventListener.graphChanged(graphObserver.getDiff()); assertEquals(6, handler.getEventCount()); } diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/GraphUpdaterTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/GraphUpdaterTest.java index 3e586363cf..9749122a17 100644 --- a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/GraphUpdaterTest.java +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/GraphUpdaterTest.java @@ -72,7 +72,7 @@ public void setUp() { projectController.newProject(); GraphController graphController = Lookup.getDefault().lookup(GraphController.class); - graphModel = graphController.getModel(); + graphModel = graphController.getGraphModel(); } @After @@ -81,7 +81,7 @@ public void tearDown() { @Test public void testUpdateGraph() { - Graph graph = graphModel.getMixedGraph(); + Graph graph = graphModel.getGraph(); GraphUpdaterEventHandler operationSupport = new GraphUpdaterEventHandler(graph); Map attributes = new HashMap(); GraphEventBuilder eventBuilder = new GraphEventBuilder(this); @@ -91,14 +91,14 @@ public void testUpdateGraph() { operationSupport.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "1", attributes)); Node node1 = graph.getNode("1"); assertNotNull(node1); - assertEquals("1", node1.getNodeData().getAttributes().getValue("label")); + assertEquals("1", node1.getAttribute("label")); attributes.clear(); attributes.put("label", "2"); operationSupport.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "2", attributes)); Node node2 = graph.getNode("2"); assertNotNull(node2); - assertEquals("2", node2.getNodeData().getAttributes().getValue("label")); + assertEquals("2", node2.getAttribute("label")); attributes.clear(); attributes.put("weight", 0.5f); @@ -132,7 +132,7 @@ public void testUpdateGraph() { operationSupport.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, "1", attributes)); node1 = graph.getNode("1"); assertNotNull(node1); - assertEquals("Node 1", node1.getNodeData().getAttributes().getValue("label")); + assertEquals("Node 1", node1.getAttribute("label")); attributes.clear(); attributes.put("label", null); @@ -140,8 +140,8 @@ public void testUpdateGraph() { node1 = graph.getNode("1"); assertNotNull(node1); assertNull("Label should be null but was "+ - node1.getNodeData().getAttributes().getValue("label"), - node1.getNodeData().getAttributes().getValue("label")); + node1.getAttribute("label"), + node1.getAttribute("label")); attributes.clear(); attributes.put("label", "Node 1"); @@ -152,16 +152,16 @@ public void testUpdateGraph() { operationSupport.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, "1", attributes)); node1 = graph.getNode("1"); assertNotNull(node1); - assertEquals("Node 1", node1.getNodeData().getAttributes().getValue("label")); - assertEquals("myattributevalue", node1.getNodeData().getAttributes().getValue("myattribute")); + assertEquals("Node 1", node1.getAttribute("label")); + assertEquals("myattributevalue", node1.getAttribute("myattribute")); attributes.clear(); attributes.put("myattribute", null); operationSupport.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, "1", attributes)); node1 = graph.getNode("1"); assertNotNull(node1); - assertEquals("Node 1", node1.getNodeData().getAttributes().getValue("label")); - assertNull(node1.getNodeData().getAttributes().getValue("myattribute")); + assertEquals("Node 1", node1.getAttribute("label")); + assertNull(node1.getAttribute("myattribute")); attributes.clear(); attributes.put("label", "Node 1"); @@ -175,14 +175,14 @@ public void testUpdateGraph() { operationSupport.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, "1", attributes)); node1 = graph.getNode("1"); assertNotNull(node1); - assertEquals("Node 1", node1.getNodeData().getAttributes().getValue("label")); - assertEquals(5, node1.getNodeData().getSize(), 1.0e-5); - assertEquals(0.5, node1.getNodeData().r(), 1.0e-5); - assertEquals(0.6, node1.getNodeData().g(), 1.0e-5); - assertEquals(0.7, node1.getNodeData().b(), 1.0e-5); - assertEquals(1, node1.getNodeData().x(), 1.0e-5); - assertEquals(2, node1.getNodeData().y(), 1.0e-5); - assertEquals(3, node1.getNodeData().z(), 1.0e-5); + assertEquals("Node 1", node1.getAttribute("label")); + assertEquals(5, node1.size(), 1.0e-5); + assertEquals(0.5, node1.r(), 1.0e-2); + assertEquals(0.6, node1.g(), 1.0e-2); + assertEquals(0.7, node1.b(), 1.0e-2); + assertEquals(1, node1.x(), 1.0e-5); + assertEquals(2, node1.y(), 1.0e-5); + assertEquals(3, node1.z(), 1.0e-5); } diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONStreamReaderTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONStreamReaderTest.java index 61d0e6157a..7436078d60 100644 --- a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONStreamReaderTest.java +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONStreamReaderTest.java @@ -246,8 +246,8 @@ public void testChangeNodeAttribute() throws IOException { workspace = projectController.newWorkspace(projectController.getCurrentProject()); GraphController graphController = Lookup.getDefault().lookup(GraphController.class); - GraphModel graphModel = graphController.getModel(); - Graph graph = graphModel.getHierarchicalMixedGraph(); + GraphModel graphModel = graphController.getGraphModel(); + Graph graph = graphModel.getGraph(); GraphEventHandler handler = new GraphUpdaterEventHandler(graph); @@ -268,8 +268,8 @@ public void testChangeNodeAttribute() throws IOException { Node a = graph.getNode("A"); - assertEquals(3.0, a.getNodeData().getSize(), 0.0); - assertEquals(a.getNodeData().getAttributes().getValue("key"), "value"); + assertEquals(3.0, a.size(), 0.0); + assertEquals(a.getAttribute("key"), "value"); } @Test @@ -284,8 +284,8 @@ public void testChangeGraphAttribute() throws IOException { workspace = projectController.newWorkspace(projectController.getCurrentProject()); GraphController graphController = Lookup.getDefault().lookup(GraphController.class); - GraphModel graphModel = graphController.getModel(); - Graph graph = graphModel.getHierarchicalMixedGraph(); + GraphModel graphModel = graphController.getGraphModel(); + Graph graph = graphModel.getGraph(); GraphEventHandler handler = new GraphUpdaterEventHandler(graph); @@ -303,7 +303,7 @@ public void testChangeGraphAttribute() throws IOException { Exceptions.printStackTrace(ex); } - assertEquals("Graph Label", graph.getAttributes().getValue("label")); + assertEquals("Graph Label", graph.getAttribute("label")); } @@ -319,8 +319,8 @@ public void testChangeEdgeAttribute() throws IOException { workspace = projectController.newWorkspace(projectController.getCurrentProject()); GraphController graphController = Lookup.getDefault().lookup(GraphController.class); - GraphModel graphModel = graphController.getModel(); - Graph graph = graphModel.getHierarchicalMixedGraph(); + GraphModel graphModel = graphController.getGraphModel(); + Graph graph = graphModel.getGraph(); GraphEventHandler handler = new GraphUpdaterEventHandler(graph); @@ -330,8 +330,8 @@ public void testChangeEdgeAttribute() throws IOException { String evstr = "{\"an\":{\"A\":{\"label\":\"Streaming Node A\",\"size\":2}}}\n\r"; evstr += "{\"an\":{\"B\":{\"label\":\"Streaming Node B\",\"size\":2}}}\n\r"; - evstr += "{\"ae\":{\"AB\":{\"source\":\"A\",\"target\":\"B\",\"directed\":false,\"label\":\"Edge AB\",\"size\":2}}}\n\r"; - evstr += "{\"ce\":{\"AB\":{\"label\":\"Edge AB changed\",\"size\":3, \"key\":\"value\"}}}\n\r"; + evstr += "{\"ae\":{\"AB\":{\"source\":\"A\",\"target\":\"B\",\"directed\":false,\"label\":\"Edge AB\",\"weight\":2}}}\n\r"; + evstr += "{\"ce\":{\"AB\":{\"label\":\"Edge AB changed\",\"weight\":3, \"key\":\"value\"}}}\n\r"; ByteArrayInputStream bais = new ByteArrayInputStream(evstr.getBytes()); streamReader.processStream(bais); @@ -343,8 +343,8 @@ public void testChangeEdgeAttribute() throws IOException { Edge ab = graph.getEdge("AB"); - assertEquals(3.0, ab.getEdgeData().getSize(), 0.0); - assertEquals(ab.getEdgeData().getAttributes().getValue("key"), "value"); + assertEquals(3.0, ab.getWeight(), 0.0); + assertEquals(ab.getAttribute("key"), "value"); } diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONStreamWriterTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONStreamWriterTest.java index 7bff9acd9a..89b7101219 100644 --- a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONStreamWriterTest.java +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/JSONStreamWriterTest.java @@ -50,10 +50,10 @@ Development and Distribution License("CDDL") (collectively, the import java.util.HashMap; import java.util.List; import java.util.Map; -import org.gephi.data.attributes.api.AttributeController; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphController; import org.gephi.graph.api.GraphModel; +import org.gephi.graph.api.GraphObserver; import org.gephi.project.api.Project; import org.gephi.project.api.ProjectController; import org.gephi.project.api.Workspace; @@ -301,29 +301,25 @@ public void testChangeGraphAttribute() throws IOException { workspace = projectController.newWorkspace(projectController.getCurrentProject()); GraphController graphController = Lookup.getDefault().lookup(GraphController.class); - GraphModel graphModel = graphController.getModel(); - Graph graph = graphModel.getHierarchicalMixedGraph(); - - AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); - ac.getModel(); + GraphModel graphModel = graphController.getGraphModel(); + Graph graph = graphModel.getGraph(); ByteArrayOutputStream out = new ByteArrayOutputStream(); StreamWriterFactory factory = Lookup.getDefault().lookup(StreamWriterFactory.class); StreamWriter streamWriter = factory.createStreamWriter(streamType, out); Graph2EventListener listener = new Graph2EventListener(graph, streamWriter); - graphModel.addGraphListener(listener); - ac.getModel().addAttributeListener(listener); - graph.getAttributes().setValue("Label", "Graph Label"); + GraphObserver graphObserver = graphModel.createGraphObserver(graph, true); + graph.setAttribute("Label", "Graph Label"); + graph.addNode(graphModel.factory().newNode()); - try { - Thread.sleep(100); - } catch (InterruptedException ex) { - Exceptions.printStackTrace(ex); - } + assertTrue(graphObserver.hasGraphChanged()); + listener.graphChanged(graphObserver.getDiff()); + + System.out.println(new String(out.toByteArray())); - assertEquals("{\"cg\":{\"Label\":\"Graph Label\"}}\r\n", new String(out.toByteArray())); + assertEquals("{\"an\":{\"0\":{\"r\":0,\"b\":0,\"size\":0,\"g\":0,\"x\":0,\"y\":0,\"z\":0}}}\r\n", new String(out.toByteArray())); } } diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/ListenersTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/ListenersTest.java index 2d1d1b3397..97571e598d 100644 --- a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/ListenersTest.java +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/ListenersTest.java @@ -42,10 +42,13 @@ Development and Distribution License("CDDL") (collectively, the package org.gephi.streaming.test; -import org.gephi.data.attributes.api.AttributeController; +import java.util.HashMap; +import java.util.Map; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphController; import org.gephi.graph.api.GraphModel; +import org.gephi.graph.api.GraphObserver; +import org.gephi.graph.api.TableObserver; import org.gephi.project.api.Project; import org.gephi.project.api.ProjectController; import org.gephi.project.api.Workspace; @@ -55,6 +58,7 @@ Development and Distribution License("CDDL") (collectively, the import org.gephi.streaming.api.event.ElementType; import org.gephi.streaming.api.event.EventType; import org.gephi.streaming.api.event.GraphEventBuilder; +import static org.junit.Assert.assertTrue; import org.junit.Test; import org.openide.util.Exceptions; import org.openide.util.Lookup; @@ -78,8 +82,8 @@ public void runTest() { // projectController.openWorkspace(workspace); GraphController graphController = Lookup.getDefault().lookup(GraphController.class); - GraphModel graphModel = graphController.getModel(); - Graph graph = graphModel.getHierarchicalMixedGraph(); + GraphModel graphModel = graphController.getGraphModel(); + Graph graph = graphModel.getGraph(); GraphEventHandler printerHandler = new GraphEventHandler() { @@ -90,9 +94,8 @@ public void handleGraphEvent(org.gephi.streaming.api.event.GraphEvent event) { }; Graph2EventListener listener = new Graph2EventListener(graph, printerHandler); - graph.getGraphModel().addGraphListener(listener); - AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); - ac.getModel().addAttributeListener(listener); + + GraphObserver graphObserver = graphModel.createGraphObserver(graph, true); GraphUpdaterEventHandler graphUpdaterHandler = new GraphUpdaterEventHandler(graph); GraphEventBuilder eventBuilder = new GraphEventBuilder(this); @@ -100,12 +103,17 @@ public void handleGraphEvent(org.gephi.streaming.api.event.GraphEvent event) { graphUpdaterHandler.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "A", null)); graphUpdaterHandler.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, "B", null)); graphUpdaterHandler.handleGraphEvent(eventBuilder.edgeAddedEvent("AB", "A", "B", false, null)); - - try { - Thread.sleep(1000); - } catch (InterruptedException ex) { - Exceptions.printStackTrace(ex); - } + + assertTrue(graphObserver.hasGraphChanged()); + listener.graphChanged(graphObserver.getDiff()); + + TableObserver nodeObserver = graphModel.getNodeTable().createTableObserver(true); + Map attributes = new HashMap(); + attributes.put("bbb", 2.0); + graphUpdaterHandler.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, "A", attributes)); + + assertTrue(nodeObserver.hasTableChanged()); + listener.attributesChanged(nodeObserver.getDiff()); } } diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/StreamControllerTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/StreamControllerTest.java index 1421614067..7d59dc1f7e 100644 --- a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/StreamControllerTest.java +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/StreamControllerTest.java @@ -82,8 +82,8 @@ public void connectToStream() throws IOException { // projectController.openWorkspace(workspace); GraphController graphController = Lookup.getDefault().lookup(GraphController.class); - GraphModel graphModel = graphController.getModel(); - Graph graph = graphModel.getHierarchicalMixedGraph(); + GraphModel graphModel = graphController.getGraphModel(); + Graph graph = graphModel.getGraph(); // Connect to stream - Streaming API diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/amazon.json b/modules/StreamingImpl/src/test/resources/org/gephi/streaming/test/amazon.json similarity index 100% rename from modules/StreamingImpl/src/test/java/org/gephi/streaming/test/amazon.json rename to modules/StreamingImpl/src/test/resources/org/gephi/streaming/test/amazon.json diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/amazon_0201485419_400.dgs b/modules/StreamingImpl/src/test/resources/org/gephi/streaming/test/amazon_0201485419_400.dgs similarity index 100% rename from modules/StreamingImpl/src/test/java/org/gephi/streaming/test/amazon_0201485419_400.dgs rename to modules/StreamingImpl/src/test/resources/org/gephi/streaming/test/amazon_0201485419_400.dgs diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/triangle.dgs b/modules/StreamingImpl/src/test/resources/org/gephi/streaming/test/triangle.dgs similarity index 100% rename from modules/StreamingImpl/src/test/java/org/gephi/streaming/test/triangle.dgs rename to modules/StreamingImpl/src/test/resources/org/gephi/streaming/test/triangle.dgs From df1a3695d70282f7b4198f90530089f5d6d82d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Wed, 6 Jan 2016 21:22:50 +0100 Subject: [PATCH 04/30] StreamingServer tests migration --- .../GraphStreamingEventProcessorTest.java | 154 ++ .../gephi/streaming/server/test/Main3.java | 121 ++ .../streaming/server/test/MainServer.java | 98 ++ .../streaming/server/test/MainServer2.java | 152 ++ .../server/test/StreamingServerTest.java | 119 ++ .../streaming/server/test/alt_add_remove.dgs | 673 ++++++++ .../gephi/streaming/server/test/amazon.json | 1405 ++++++++++++++++ .../server/test/amazon_0201485419_400.dgs | 1407 +++++++++++++++++ .../gephi/streaming/server/test/graph.json | 1190 ++++++++++++++ 9 files changed, 5319 insertions(+) create mode 100644 modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/GraphStreamingEventProcessorTest.java create mode 100644 modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/Main3.java create mode 100644 modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/MainServer.java create mode 100644 modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/MainServer2.java create mode 100644 modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/StreamingServerTest.java create mode 100644 modules/StreamingServer/src/test/resources/org/gephi/streaming/server/test/alt_add_remove.dgs create mode 100644 modules/StreamingServer/src/test/resources/org/gephi/streaming/server/test/amazon.json create mode 100644 modules/StreamingServer/src/test/resources/org/gephi/streaming/server/test/amazon_0201485419_400.dgs create mode 100644 modules/StreamingServer/src/test/resources/org/gephi/streaming/server/test/graph.json diff --git a/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/GraphStreamingEventProcessorTest.java b/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/GraphStreamingEventProcessorTest.java new file mode 100644 index 0000000000..71ec3477b2 --- /dev/null +++ b/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/GraphStreamingEventProcessorTest.java @@ -0,0 +1,154 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server.test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.URL; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import org.gephi.data.attributes.api.AttributeController; + +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphController; +import org.gephi.graph.api.GraphModel; +import org.gephi.project.api.ProjectController; +import org.gephi.streaming.api.CompositeGraphEventHandler; +import org.gephi.streaming.api.Graph2EventListener; +import org.gephi.streaming.api.GraphEventHandler; +import org.gephi.streaming.api.GraphUpdaterEventHandler; +import org.gephi.streaming.api.Report; +import org.gephi.streaming.api.StreamReader; +import org.gephi.streaming.api.StreamReaderFactory; +import org.gephi.streaming.api.StreamWriter; +import org.gephi.streaming.api.StreamWriterFactory; +import org.gephi.streaming.api.StreamingConnection; +import org.gephi.streaming.api.StreamingEndpoint; +import org.gephi.streaming.api.event.GraphEvent; +import org.gephi.streaming.api.event.GraphEventBuilder; +import org.gephi.streaming.impl.StreamingConnectionImpl; +import org.gephi.streaming.server.impl.FilteredGraphEventHandler; +import org.junit.Test; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; + +/** + * @author panisson + * + */ +public class GraphStreamingEventProcessorTest { + + private static final String DGS_RESOURCE = "amazon_0201485419_400.dgs"; + + @Test + public void testAll() throws IOException { + + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + projectController.newProject(); + projectController.newWorkspace(projectController.getCurrentProject()); + + String streamType = "DGS"; + URL url = this.getClass().getResource(DGS_RESOURCE); + + AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); + ac.getModel(); + + GraphController graphController = Lookup.getDefault().lookup(GraphController.class); + GraphModel graphModel = graphController.getModel(); + + Graph graph = graphModel.getHierarchicalMixedGraph(); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamWriterFactory factory = Lookup.getDefault().lookup(StreamWriterFactory.class); + StreamWriter streamWriter = factory.createStreamWriter(streamType, out); + + GraphEventHandler graphUpdaterHandler = new GraphUpdaterEventHandler(graph); + + final Set processedEvents = Collections.synchronizedSet(new HashSet()); + GraphEventHandler eventCollector = new GraphEventHandler() { + + public void handleGraphEvent(GraphEvent event) { + processedEvents.add(event); + } + }; + + GraphEventHandler composite = new CompositeGraphEventHandler(graphUpdaterHandler, eventCollector); + + Graph2EventListener listener = new Graph2EventListener(graph, new FilteredGraphEventHandler(streamWriter, processedEvents)); +// Graph2EventListener listener = new Graph2EventListener(graph, streamWriter); + graphModel.addGraphListener(listener); + ac.getModel().addAttributeListener(listener); + + StreamingConnection connection = + connectToStream(url, streamType, composite); + + connection.process(); + + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } + + System.out.println(out.toString()); + + } + + public static StreamingConnection connectToStream( + URL url, String streamType, GraphEventHandler handler) + throws IOException { + StreamReaderFactory readerFactory = + Lookup.getDefault().lookup(StreamReaderFactory.class); + + GraphEventBuilder eventBuilder = new GraphEventBuilder(url); + StreamReader reader = + readerFactory.createStreamReader(streamType, handler, + eventBuilder); + + StreamingEndpoint endpoint = new StreamingEndpoint(); + endpoint.setUrl(url); + StreamingConnection connection = new StreamingConnectionImpl(endpoint, reader, new Report()); + + return connection; + } +} diff --git a/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/Main3.java b/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/Main3.java new file mode 100644 index 0000000000..05ca2afbf8 --- /dev/null +++ b/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/Main3.java @@ -0,0 +1,121 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server.test; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.net.URLConnection; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.openide.util.Exceptions; + +public class Main3 extends HttpServlet { + + private static final String DGS_RESOURCE = "amazon_0201485419_400.dgs"; + private static final String JSON_RESOURCE = "graph.json"; + + @Override + public void service(HttpServletRequest request, HttpServletResponse response) { + + String operation = request.getParameter("operation"); + if (operation!=null && operation.equalsIgnoreCase("updateGraph")) { + return; + } + + long time = System.currentTimeMillis(); + + response.setHeader("Content-Type", "text/plain"); + response.setHeader("Server", "HelloWorld/1.0 (Simple 4.0)"); + response.setDateHeader("Date", time); + response.setDateHeader("Last-Modified", time); + + URL url = this.getClass().getResource(JSON_RESOURCE); + + try { + OutputStream out = response.getOutputStream(); + + URLConnection connection = url.openConnection(); + connection.connect(); + + InputStream inputStream = connection.getInputStream(); + + int i = 0; + int data; + while ((data = inputStream.read()) != -1) { + + out.write(data); + + i++; + if (i % 250 == 0) { + System.out.println("Sending data"); + out.flush(); + try { + Thread.sleep(400); + } catch (InterruptedException e) { + } + } + + } + out.flush(); + inputStream.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + public static void main(String[] list) throws Exception { + Server server = new Server(8080); + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + context.addServlet(new ServletHolder(new Main3()), "/*"); + server.setHandler(context); + server.start(); + server.join(); + } +} diff --git a/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/MainServer.java b/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/MainServer.java new file mode 100644 index 0000000000..f52e6b21ce --- /dev/null +++ b/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/MainServer.java @@ -0,0 +1,98 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server.test; + +import java.io.IOException; + +import org.gephi.data.attributes.api.AttributeController; +import org.gephi.graph.api.GraphController; +import org.gephi.graph.api.GraphModel; +import org.gephi.project.api.ProjectController; +import org.gephi.project.api.Workspace; +import org.gephi.streaming.server.AuthenticationFilter; +import org.gephi.streaming.server.StreamingServer; +import org.gephi.streaming.server.impl.BasicAuthenticationFilter; +import org.gephi.streaming.server.impl.ServerControllerImpl; +import org.junit.Test; +import org.openide.util.Lookup; + +/** + * From linux, you can connect to the server using:
+ * wget -qO- http://localhost:8080/graphstream + * + * @author panisson + * + */ +public class MainServer { + + @Test + public void testMainServer() throws IOException { + + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + projectController.newProject(); + Workspace workspace = projectController.newWorkspace(projectController.getCurrentProject()); + + AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); + ac.getModel(); + + GraphController graphController = Lookup.getDefault().lookup(GraphController.class); + GraphModel graphModel = graphController.getModel(); + + StreamingServer server = Lookup.getDefault().lookup(StreamingServer.class); + + AuthenticationFilter authenticationFilter = new BasicAuthenticationFilter(); + + authenticationFilter.setUser(server.getServerSettings().getUser()); + authenticationFilter.setPassword(server.getServerSettings().getPassword()); + authenticationFilter.setAuthenticationEnabled(server.getServerSettings().isBasicAuthentication()); + + ServerControllerImpl serverController = new ServerControllerImpl(graphModel.getHierarchicalMixedGraph()); + server.register(serverController, "/graphstream"); + + server.start(); + } + + public static void main(String[] list) throws Exception { + MainServer server = new MainServer(); + server.testMainServer(); + } +} \ No newline at end of file diff --git a/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/MainServer2.java b/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/MainServer2.java new file mode 100644 index 0000000000..b081dd27f4 --- /dev/null +++ b/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/MainServer2.java @@ -0,0 +1,152 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server.test; + +import java.io.IOException; +import java.io.InputStream; + +import javax.servlet.AsyncContext; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.gephi.data.attributes.api.AttributeController; +import org.gephi.graph.api.GraphController; +import org.gephi.graph.api.GraphModel; +import org.gephi.project.api.ProjectController; +import org.gephi.project.api.Workspace; +import org.gephi.streaming.api.GraphUpdaterEventHandler; +import org.gephi.streaming.api.StreamReader; +import org.gephi.streaming.api.StreamReaderFactory; +import org.gephi.streaming.api.event.GraphEventBuilder; +import org.gephi.streaming.server.ServerController; +import org.gephi.streaming.server.impl.ServerControllerImpl; +import org.openide.util.Lookup; + +/** + * From linux, you can connect to the server using:
+ * wget -qO- http://localhost:8080/graphstream + * + * @author panisson + * + */ +public class MainServer2 extends HttpServlet { + + private static final String DGS_RESOURCE = "amazon_0201485419_400.dgs"; + + private ServerController serverController; + + public MainServer2() { + + ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); + projectController.newProject(); + Workspace workspace = projectController.newWorkspace(projectController.getCurrentProject()); + + AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); + ac.getModel(); + + GraphController graphController = Lookup.getDefault().lookup(GraphController.class); + GraphModel graphModel = graphController.getModel(); + + serverController = new ServerControllerImpl(graphModel.getHierarchicalMixedGraph()); + + final InputStream fileInputStream = this.getClass().getResourceAsStream(DGS_RESOURCE); + + final InputStream inputStream = new InputStream() { + private int count = 0; + + @Override + public int read() throws IOException { + count++; + if (count%10 == 0) + try { + Thread.sleep(100); + } catch (InterruptedException e) { } + return fileInputStream.read(); + } + }; + + StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); + final StreamReader streamReader = factory.createStreamReader("DGS", new GraphUpdaterEventHandler(graphModel.getHierarchicalMixedGraph()), new GraphEventBuilder(this)); + + new Thread() { + @Override + public void run() { + + try { + streamReader.processStream(inputStream); + + } catch (IOException e) { + // Exception during processing + e.printStackTrace(); + } + } + }.start(); + } + + @Override + public void service(HttpServletRequest request, HttpServletResponse response) { + long time = System.currentTimeMillis(); + + response.setHeader("Content-Type", "text/plain"); + response.setHeader("Server", "Gephi/0.7 alpha4"); + response.setDateHeader("Date", time); + response.setDateHeader("Last-Modified", time); + serverController.handle(request, response); + AsyncContext aCtx = request.startAsync(); + aCtx.setTimeout(-1); + } + + public static void main(String[] list) throws Exception { + Server server = new Server(8080); + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + context.addServlet(new ServletHolder(new MainServer2()), "/*"); + server.setHandler(context); + server.start(); + server.join(); + + + } +} \ No newline at end of file diff --git a/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/StreamingServerTest.java b/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/StreamingServerTest.java new file mode 100644 index 0000000000..4efa50402f --- /dev/null +++ b/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/StreamingServerTest.java @@ -0,0 +1,119 @@ +/* +Copyright 2008-2010 Gephi +Authors : Mathieu Bastian , Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2011 Gephi Consortium. + */ +package org.gephi.streaming.server.test; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.junit.Ignore; + +@Ignore +public class StreamingServerTest extends HttpServlet { + + private static final String DGS_RESOURCE = "alt_add_remove.dgs"; + + @Override + public void service(HttpServletRequest request, HttpServletResponse response) { + long time = System.currentTimeMillis(); + + response.setHeader("Content-Type", "text/plain"); + response.setHeader("Server", "HelloWorld/1.0 (Simple 4.0)"); + response.setDateHeader("Date", time); + response.setDateHeader("Last-Modified", time); + + URL url = this.getClass().getResource(DGS_RESOURCE); + + try { + OutputStream out = response.getOutputStream(); + + URLConnection connection = url.openConnection(); + connection.connect(); + + InputStream inputStream = connection.getInputStream(); + + int i = 0; + int data; + while ((data = inputStream.read()) != -1) { + + out.write(data); + + i++; + if (i % 10 == 0) { + System.out.println("Sending data"); + out.flush(); + try { + Thread.sleep(400); + } catch (InterruptedException e) { + } + } + + } + out.flush(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + public static void main(String[] list) throws Exception { + Server server = new Server(8080); + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + context.addServlet(new ServletHolder(new StreamingServerTest()), "/*"); + server.setHandler(context); + server.start(); + server.join(); + } +} \ No newline at end of file diff --git a/modules/StreamingServer/src/test/resources/org/gephi/streaming/server/test/alt_add_remove.dgs b/modules/StreamingServer/src/test/resources/org/gephi/streaming/server/test/alt_add_remove.dgs new file mode 100644 index 0000000000..5c8191bc66 --- /dev/null +++ b/modules/StreamingServer/src/test/resources/org/gephi/streaming/server/test/alt_add_remove.dgs @@ -0,0 +1,673 @@ +DGS003 +"alt_add_remove.dgs" 0 0 +an "1" "size":2 "label":"1" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" +dn "2" +dn "3" +an "2" "size":2 "label":"2" +ae "1_2" "1" "2" +an "3" "size":2 "label":"3" +ae "2_3" "2" "3" \ No newline at end of file diff --git a/modules/StreamingServer/src/test/resources/org/gephi/streaming/server/test/amazon.json b/modules/StreamingServer/src/test/resources/org/gephi/streaming/server/test/amazon.json new file mode 100644 index 0000000000..ce9a7a5a9a --- /dev/null +++ b/modules/StreamingServer/src/test/resources/org/gephi/streaming/server/test/amazon.json @@ -0,0 +1,1405 @@ +{"an":{"0201485419":{}}} +{"cn":{"0201485419":{"label":"Art of Computer...","full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)"}}} +{"an":{"0321335708":{"label":"Art of Computer...","full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)"}}} +{"ae":{"0201485419_0321335708":{"source":"0201485419","directed":false,"target":"0321335708"}}} +{"an":{"0201558025":{"label":"Art of Computer...","full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)"}}} +{"ae":{"0201485419_0201558025":{"source":"0201485419","directed":false,"target":"0201558025"}}} +{"an":{"0262032937":{"label":"Art of Computer...","full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)"}}} +{"ae":{"0201485419_0262032937":{"source":"0201485419","directed":false,"target":"0262032937"}}} +{"an":{"0201853930":{"label":"Art of Computer...","full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)"}}} +{"ae":{"0201485419_0201853930":{"source":"0201485419","directed":false,"target":"0201853930"}}} +{"an":{"0201853949":{"label":"Art of Computer...","full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)"}}} +{"ae":{"0201485419_0201853949":{"source":"0201485419","directed":false,"target":"0201853949"}}} +{"cn":{"0321335708":{"label":"Art of Computer...","full-label":"Art of Computer Programming, Volume 4, Fascicle 4,The: Generating All Trees--History of Combinatorial Generation (Art of Computer Programming)"}}} +{"ae":{"0201853949_0321335708":{"source":"0201853949","directed":false,"target":"0321335708"}}} +{"ae":{"0201853930_0321335708":{"source":"0201853930","directed":false,"target":"0321335708"}}} +{"an":{"0201853922":{"label":"Art of Computer...","full-label":"Art of Computer Programming, Volume 4, Fascicle 4,The: Generating All Trees--History of Combinatorial Generation (Art of Computer Programming)"}}} +{"ae":{"0321335708_0201853922":{"source":"0321335708","directed":false,"target":"0201853922"}}} +{"an":{"0321534964":{"label":"Art of Computer...","full-label":"Art of Computer Programming, Volume 4, Fascicle 4,The: Generating All Trees--History of Combinatorial Generation (Art of Computer Programming)"}}} +{"ae":{"0321335708_0321534964":{"source":"0321335708","directed":false,"target":"0321534964"}}} +{"cn":{"0201558025":{"label":"Concrete Mathem...","full-label":"Concrete Mathematics: A Foundation for Computer Science (2nd Edition)"}}} +{"ae":{"0262032937_0201558025":{"source":"0262032937","directed":false,"target":"0201558025"}}} +{"an":{"0387948600":{"label":"Concrete Mathem...","full-label":"Concrete Mathematics: A Foundation for Computer Science (2nd Edition)"}}} +{"ae":{"0201558025_0387948600":{"source":"0201558025","directed":false,"target":"0387948600"}}} +{"an":{"0201657880":{"label":"Concrete Mathem...","full-label":"Concrete Mathematics: A Foundation for Computer Science (2nd Edition)"}}} +{"ae":{"0201558025_0201657880":{"source":"0201558025","directed":false,"target":"0201657880"}}} +{"ae":{"0321335708_0201558025":{"source":"0321335708","directed":false,"target":"0201558025"}}} +{"cn":{"0262032937":{"label":"Introduction to...","full-label":"Introduction to Algorithms"}}} +{"an":{"0137903952":{"label":"Introduction to...","full-label":"Introduction to Algorithms"}}} +{"ae":{"0262032937_0137903952":{"source":"0262032937","directed":false,"target":"0137903952"}}} +{"an":{"0534950973":{"label":"Introduction to...","full-label":"Introduction to Algorithms"}}} +{"ae":{"0262032937_0534950973":{"source":"0262032937","directed":false,"target":"0534950973"}}} +{"an":{"0471694665":{"label":"Introduction to...","full-label":"Introduction to Algorithms"}}} +{"ae":{"0262032937_0471694665":{"source":"0262032937","directed":false,"target":"0471694665"}}} +{"an":{"0123706068":{"label":"Introduction to...","full-label":"Introduction to Algorithms"}}} +{"ae":{"0262032937_0123706068":{"source":"0262032937","directed":false,"target":"0123706068"}}} +{"an":{"0201633612":{"label":"Introduction to...","full-label":"Introduction to Algorithms"}}} +{"ae":{"0262032937_0201633612":{"source":"0262032937","directed":false,"target":"0201633612"}}} +{"cn":{"0201853930":{"label":"The Art of Comp...","full-label":"The Art of Computer Programming, Volume 4, Fascicle 2: Generating All Tuples and Permutations (Art of Computer Programming)"}}} +{"ae":{"0201853949_0201853930":{"source":"0201853949","directed":false,"target":"0201853930"}}} +{"ae":{"0201853922_0201853930":{"source":"0201853922","directed":false,"target":"0201853930"}}} +{"ae":{"0321534964_0201853930":{"source":"0321534964","directed":false,"target":"0201853930"}}} +{"cn":{"0201853949":{"label":"The Art of Comp...","full-label":"The Art of Computer Programming, Volume 4, Fascicle 3: Generating All Combinations and Partitions (Art of Computer Programming)"}}} +{"ae":{"0201853922_0201853949":{"source":"0201853922","directed":false,"target":"0201853949"}}} +{"ae":{"0321534964_0201853949":{"source":"0321534964","directed":false,"target":"0201853949"}}} +{"cn":{"0201853922":{"label":"The Art of Comp...","full-label":"The Art of Computer Programming, Volume 1, Fascicle 1: MMIX -- A RISC Computer for the New Millennium (Art of Computer Programming)"}}} +{"ae":{"0321534964_0201853922":{"source":"0321534964","directed":false,"target":"0201853922"}}} +{"ae":{"0201485419_0201853922":{"source":"0201485419","directed":false,"target":"0201853922"}}} +{"cn":{"0321534964":{"label":"The Art of Comp...","full-label":"The Art of Computer Programming, Volume 4, Fascicle 0: Introduction to Combinatorial Algorithms and Boolean Functions (Art of Computer Programming)"}}} +{"ae":{"0201485419_0321534964":{"source":"0201485419","directed":false,"target":"0321534964"}}} +{"cn":{"0387948600":{"label":"The Algorithm D...","full-label":"The Algorithm Design Manual"}}} +{"an":{"0387001638":{"label":"The Algorithm D...","full-label":"The Algorithm Design Manual"}}} +{"ae":{"0387948600_0387001638":{"source":"0387948600","directed":false,"target":"0387001638"}}} +{"ae":{"0262032937_0387948600":{"source":"0262032937","directed":false,"target":"0387948600"}}} +{"an":{"0596529325":{"label":"The Algorithm D...","full-label":"The Algorithm Design Manual"}}} +{"ae":{"0387948600_0596529325":{"source":"0387948600","directed":false,"target":"0596529325"}}} +{"ae":{"0201657880_0387948600":{"source":"0201657880","directed":false,"target":"0387948600"}}} +{"an":{"0596510047":{"label":"The Algorithm D...","full-label":"The Algorithm Design Manual"}}} +{"ae":{"0387948600_0596510047":{"source":"0387948600","directed":false,"target":"0596510047"}}} +{"cn":{"0201657880":{"label":"Programming Pea...","full-label":"Programming Pearls (2nd Edition) (ACM Press)"}}} +{"an":{"047012167X":{"label":"Programming Pea...","full-label":"Programming Pearls (2nd Edition) (ACM Press)"}}} +{"ae":{"0201657880_047012167X":{"source":"0201657880","directed":false,"target":"047012167X"}}} +{"an":{"0735619670":{"label":"Programming Pea...","full-label":"Programming Pearls (2nd Edition) (ACM Press)"}}} +{"ae":{"0201657880_0735619670":{"source":"0201657880","directed":false,"target":"0735619670"}}} +{"an":{"020161586X":{"label":"Programming Pea...","full-label":"Programming Pearls (2nd Edition) (ACM Press)"}}} +{"ae":{"0201657880_020161586X":{"source":"0201657880","directed":false,"target":"020161586X"}}} +{"ae":{"0201633612_0201657880":{"source":"0201633612","directed":false,"target":"0201657880"}}} +{"an":{"020161622X":{"label":"Programming Pea...","full-label":"Programming Pearls (2nd Edition) (ACM Press)"}}} +{"ae":{"0201657880_020161622X":{"source":"0201657880","directed":false,"target":"020161622X"}}} +{"cn":{"0137903952":{"label":"Artificial Inte...","full-label":"Artificial Intelligence: A Modern Approach (2nd Edition) (Prentice Hall Series in Artificial Intelligence)"}}} +{"an":{"0387310738":{"label":"Artificial Inte...","full-label":"Artificial Intelligence: A Modern Approach (2nd Edition) (Prentice Hall Series in Artificial Intelligence)"}}} +{"ae":{"0137903952_0387310738":{"source":"0137903952","directed":false,"target":"0387310738"}}} +{"ae":{"0471694665_0137903952":{"source":"0471694665","directed":false,"target":"0137903952"}}} +{"an":{"0071154671":{"label":"Artificial Inte...","full-label":"Artificial Intelligence: A Modern Approach (2nd Edition) (Prentice Hall Series in Artificial Intelligence)"}}} +{"ae":{"0137903952_0071154671":{"source":"0137903952","directed":false,"target":"0071154671"}}} +{"ae":{"0534950973_0137903952":{"source":"0534950973","directed":false,"target":"0137903952"}}} +{"cn":{"0534950973":{"label":"Introduction to...","full-label":"Introduction to the Theory of Computation, Second Edition"}}} +{"ae":{"0471694665_0534950973":{"source":"0471694665","directed":false,"target":"0534950973"}}} +{"an":{"0716710455":{"label":"Introduction to...","full-label":"Introduction to the Theory of Computation, Second Edition"}}} +{"ae":{"0534950973_0716710455":{"source":"0534950973","directed":false,"target":"0716710455"}}} +{"an":{"0321486811":{"label":"Introduction to...","full-label":"Introduction to the Theory of Computation, Second Edition"}}} +{"ae":{"0534950973_0321486811":{"source":"0534950973","directed":false,"target":"0321486811"}}} +{"cn":{"0471694665":{"label":"Operating Syste...","full-label":"Operating System Concepts (7th Edition)"}}} +{"ae":{"0123706068_0471694665":{"source":"0123706068","directed":false,"target":"0471694665"}}} +{"an":{"0321497708":{"label":"Operating Syste...","full-label":"Operating System Concepts (7th Edition)"}}} +{"ae":{"0471694665_0321497708":{"source":"0471694665","directed":false,"target":"0321497708"}}} +{"cn":{"0123706068":{"label":"Computer Organi...","full-label":"Computer Organization and Design: The Hardware/Software Interface. Third Edition, Revised"}}} +{"an":{"0123704901":{"label":"Computer Organi...","full-label":"Computer Organization and Design: The Hardware/Software Interface. Third Edition, Revised"}}} +{"ae":{"0123706068_0123704901":{"source":"0123706068","directed":false,"target":"0123704901"}}} +{"an":{"0131103628":{"label":"Computer Organi...","full-label":"Computer Organization and Design: The Hardware/Software Interface. Third Edition, Revised"}}} +{"ae":{"0123706068_0131103628":{"source":"0123706068","directed":false,"target":"0131103628"}}} +{"ae":{"0137903952_0123706068":{"source":"0137903952","directed":false,"target":"0123706068"}}} +{"cn":{"0201633612":{"label":"Design Patterns...","full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)"}}} +{"an":{"0201485672":{"label":"Design Patterns...","full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201633612_0201485672":{"source":"0201633612","directed":false,"target":"0201485672"}}} +{"ae":{"0735619670_0201633612":{"source":"0735619670","directed":false,"target":"0201633612"}}} +{"an":{"0596007124":{"label":"Design Patterns...","full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201633612_0596007124":{"source":"0201633612","directed":false,"target":"0596007124"}}} +{"an":{"0321334876":{"label":"Design Patterns...","full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201633612_0321334876":{"source":"0201633612","directed":false,"target":"0321334876"}}} +{"an":{"0321193687":{"label":"Design Patterns...","full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201633612_0321193687":{"source":"0201633612","directed":false,"target":"0321193687"}}} +{"cn":{"0387001638":{"label":"Programming Cha...","full-label":"Programming Challenges"}}} +{"ae":{"047012167X_0387001638":{"source":"047012167X","directed":false,"target":"0387001638"}}} +{"ae":{"0201657880_0387001638":{"source":"0201657880","directed":false,"target":"0387001638"}}} +{"an":{"0470121688":{"label":"Programming Cha...","full-label":"Programming Challenges"}}} +{"ae":{"0387001638_0470121688":{"source":"0387001638","directed":false,"target":"0470121688"}}} +{"ae":{"0262032937_0387001638":{"source":"0262032937","directed":false,"target":"0387001638"}}} +{"cn":{"0596529325":{"label":"Programming Col...","full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications"}}} +{"an":{"0596529309":{"label":"Programming Col...","full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications"}}} +{"ae":{"0596529325_0596529309":{"source":"0596529325","directed":false,"target":"0596529309"}}} +{"an":{"0596529260":{"label":"Programming Col...","full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications"}}} +{"ae":{"0596529325_0596529260":{"source":"0596529325","directed":false,"target":"0596529260"}}} +{"an":{"0596102356":{"label":"Programming Col...","full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications"}}} +{"ae":{"0596529325_0596102356":{"source":"0596529325","directed":false,"target":"0596102356"}}} +{"ae":{"0596510047_0596529325":{"source":"0596510047","directed":false,"target":"0596529325"}}} +{"an":{"0596514557":{"label":"Programming Col...","full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications"}}} +{"ae":{"0596529325_0596514557":{"source":"0596529325","directed":false,"target":"0596514557"}}} +{"cn":{"0596510047":{"label":"Beautiful Code:...","full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))"}}} +{"an":{"1400082471":{"label":"Beautiful Code:...","full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))"}}} +{"ae":{"0596510047_1400082471":{"source":"0596510047","directed":false,"target":"1400082471"}}} +{"ae":{"0596529260_0596510047":{"source":"0596529260","directed":false,"target":"0596510047"}}} +{"an":{"193435600X":{"label":"Beautiful Code:...","full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))"}}} +{"ae":{"0596510047_193435600X":{"source":"0596510047","directed":false,"target":"193435600X"}}} +{"an":{"159059844X":{"label":"Beautiful Code:...","full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))"}}} +{"ae":{"0596510047_159059844X":{"source":"0596510047","directed":false,"target":"159059844X"}}} +{"cn":{"047012167X":{"label":"Programming Int...","full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)"}}} +{"an":{"B000JBY0RY":{"label":"Programming Int...","full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)"}}} +{"ae":{"047012167X_B000JBY0RY":{"source":"047012167X","directed":false,"target":"B000JBY0RY"}}} +{"an":{"B000ESSSN4":{"label":"Programming Int...","full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)"}}} +{"ae":{"047012167X_B000ESSSN4":{"source":"047012167X","directed":false,"target":"B000ESSSN4"}}} +{"ae":{"0470121688_047012167X":{"source":"0470121688","directed":false,"target":"047012167X"}}} +{"an":{"0071495789":{"label":"Programming Int...","full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)"}}} +{"ae":{"047012167X_0071495789":{"source":"047012167X","directed":false,"target":"0071495789"}}} +{"cn":{"0735619670":{"label":"Code Complete: ...","full-label":"Code Complete: A Practical Handbook of Software Construction"}}} +{"ae":{"020161622X_0735619670":{"source":"020161622X","directed":false,"target":"0735619670"}}} +{"an":{"1556159005":{"label":"Code Complete: ...","full-label":"Code Complete: A Practical Handbook of Software Construction"}}} +{"ae":{"0735619670_1556159005":{"source":"0735619670","directed":false,"target":"1556159005"}}} +{"an":{"0735605351":{"label":"Code Complete: ...","full-label":"Code Complete: A Practical Handbook of Software Construction"}}} +{"ae":{"0735619670_0735605351":{"source":"0735619670","directed":false,"target":"0735605351"}}} +{"an":{"0201835959":{"label":"Code Complete: ...","full-label":"Code Complete: A Practical Handbook of Software Construction"}}} +{"ae":{"0735619670_0201835959":{"source":"0735619670","directed":false,"target":"0201835959"}}} +{"cn":{"020161586X":{"label":"The Practice of...","full-label":"The Practice of Programming (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0735619670_020161586X":{"source":"0735619670","directed":false,"target":"020161586X"}}} +{"ae":{"0131103628_020161586X":{"source":"0131103628","directed":false,"target":"020161586X"}}} +{"ae":{"0201633612_020161586X":{"source":"0201633612","directed":false,"target":"020161586X"}}} +{"an":{"013937681X":{"label":"The Practice of...","full-label":"The Practice of Programming (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"020161586X_013937681X":{"source":"020161586X","directed":false,"target":"013937681X"}}} +{"cn":{"020161622X":{"label":"The Pragmatic P...","full-label":"The Pragmatic Programmer: From Journeyman to Master"}}} +{"ae":{"0201835959_020161622X":{"source":"0201835959","directed":false,"target":"020161622X"}}} +{"ae":{"0201485672_020161622X":{"source":"0201485672","directed":false,"target":"020161622X"}}} +{"ae":{"0201633612_020161622X":{"source":"0201633612","directed":false,"target":"020161622X"}}} +{"an":{"0932633439":{"label":"The Pragmatic P...","full-label":"The Pragmatic Programmer: From Journeyman to Master"}}} +{"ae":{"020161622X_0932633439":{"source":"020161622X","directed":false,"target":"0932633439"}}} +{"cn":{"0387310738":{"label":"Pattern Recogni...","full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)"}}} +{"an":{"0387952845":{"label":"Pattern Recogni...","full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)"}}} +{"ae":{"0387310738_0387952845":{"source":"0387310738","directed":false,"target":"0387952845"}}} +{"an":{"0471056693":{"label":"Pattern Recogni...","full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)"}}} +{"ae":{"0387310738_0471056693":{"source":"0387310738","directed":false,"target":"0471056693"}}} +{"an":{"0120884070":{"label":"Pattern Recogni...","full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)"}}} +{"ae":{"0387310738_0120884070":{"source":"0387310738","directed":false,"target":"0120884070"}}} +{"an":{"026218253X":{"label":"Pattern Recogni...","full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)"}}} +{"ae":{"0387310738_026218253X":{"source":"0387310738","directed":false,"target":"026218253X"}}} +{"ae":{"0596529325_0387310738":{"source":"0596529325","directed":false,"target":"0387310738"}}} +{"cn":{"0071154671":{"label":"Machine Learnin...","full-label":"Machine Learning (Mcgraw-Hill International Edit)"}}} +{"ae":{"0387310738_0071154671":{"source":"0387310738","directed":false,"target":"0071154671"}}} +{"ae":{"0120884070_0071154671":{"source":"0120884070","directed":false,"target":"0071154671"}}} +{"ae":{"0387952845_0071154671":{"source":"0387952845","directed":false,"target":"0071154671"}}} +{"ae":{"0471056693_0071154671":{"source":"0471056693","directed":false,"target":"0071154671"}}} +{"cn":{"0716710455":{"label":"Computers and I...","full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)"}}} +{"an":{"0486402584":{"label":"Computers and I...","full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)"}}} +{"ae":{"0716710455_0486402584":{"source":"0716710455","directed":false,"target":"0486402584"}}} +{"an":{"3540653678":{"label":"Computers and I...","full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)"}}} +{"ae":{"0716710455_3540653678":{"source":"0716710455","directed":false,"target":"3540653678"}}} +{"ae":{"0262032937_0716710455":{"source":"0262032937","directed":false,"target":"0716710455"}}} +{"an":{"0201530821":{"label":"Computers and I...","full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)"}}} +{"ae":{"0716710455_0201530821":{"source":"0716710455","directed":false,"target":"0201530821"}}} +{"cn":{"0321486811":{"label":"Compilers: Prin...","full-label":"Compilers: Principles, Techniques, and Tools (2nd Edition)"}}} +{"an":{"B00007FYCY":{"label":"Compilers: Prin...","full-label":"Compilers: Principles, Techniques, and Tools (2nd Edition)"}}} +{"ae":{"0321486811_B00007FYCY":{"source":"0321486811","directed":false,"target":"B00007FYCY"}}} +{"an":{"0126339511":{"label":"Compilers: Prin...","full-label":"Compilers: Principles, Techniques, and Tools (2nd Edition)"}}} +{"ae":{"0321486811_0126339511":{"source":"0321486811","directed":false,"target":"0126339511"}}} +{"ae":{"0262032937_0321486811":{"source":"0262032937","directed":false,"target":"0321486811"}}} +{"ae":{"0137903952_0321486811":{"source":"0137903952","directed":false,"target":"0321486811"}}} +{"ae":{"0596510047_0321486811":{"source":"0596510047","directed":false,"target":"0321486811"}}} +{"cn":{"0321497708":{"label":"Computer Networ...","full-label":"Computer Networking: A Top-Down Approach (4th Edition)"}}} +{"ae":{"0262032937_0321497708":{"source":"0262032937","directed":false,"target":"0321497708"}}} +{"ae":{"0123706068_0321497708":{"source":"0123706068","directed":false,"target":"0321497708"}}} +{"ae":{"0137903952_0321497708":{"source":"0137903952","directed":false,"target":"0321497708"}}} +{"an":{"0321369572":{"label":"Computer Networ...","full-label":"Computer Networking: A Top-Down Approach (4th Edition)"}}} +{"ae":{"0321497708_0321369572":{"source":"0321497708","directed":false,"target":"0321369572"}}} +{"cn":{"0123704901":{"label":"Computer Archit...","full-label":"Computer Architecture, Fourth Edition: A Quantitative Approach"}}} +{"ae":{"0262032937_0123704901":{"source":"0262032937","directed":false,"target":"0123704901"}}} +{"ae":{"0471694665_0123704901":{"source":"0471694665","directed":false,"target":"0123704901"}}} +{"an":{"1593271042":{"label":"Computer Archit...","full-label":"Computer Architecture, Fourth Edition: A Quantitative Approach"}}} +{"ae":{"0123704901_1593271042":{"source":"0123704901","directed":false,"target":"1593271042"}}} +{"ae":{"0321486811_0123704901":{"source":"0321486811","directed":false,"target":"0123704901"}}} +{"cn":{"0131103628":{"label":"C Programming L...","full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)"}}} +{"an":{"0201700735":{"label":"C Programming L...","full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)"}}} +{"ae":{"0131103628_0201700735":{"source":"0131103628","directed":false,"target":"0201700735"}}} +{"an":{"013089592X":{"label":"C Programming L...","full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)"}}} +{"ae":{"0131103628_013089592X":{"source":"0131103628","directed":false,"target":"013089592X"}}} +{"an":{"0672305100":{"label":"C Programming L...","full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)"}}} +{"ae":{"0131103628_0672305100":{"source":"0131103628","directed":false,"target":"0672305100"}}} +{"an":{"0201433079":{"label":"C Programming L...","full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)"}}} +{"ae":{"0131103628_0201433079":{"source":"0131103628","directed":false,"target":"0201433079"}}} +{"ae":{"013937681X_0131103628":{"source":"013937681X","directed":false,"target":"0131103628"}}} +{"cn":{"0201485672":{"label":"Refactoring: Im...","full-label":"Refactoring: Improving the Design of Existing Code (The Addison-Wesley Object Technology Series)"}}} +{"ae":{"0735619670_0201485672":{"source":"0735619670","directed":false,"target":"0201485672"}}} +{"an":{"0321146530":{"label":"Refactoring: Im...","full-label":"Refactoring: Improving the Design of Existing Code (The Addison-Wesley Object Technology Series)"}}} +{"ae":{"0201485672_0321146530":{"source":"0201485672","directed":false,"target":"0321146530"}}} +{"an":{"0321127420":{"label":"Refactoring: Im...","full-label":"Refactoring: Improving the Design of Existing Code (The Addison-Wesley Object Technology Series)"}}} +{"ae":{"0201485672_0321127420":{"source":"0201485672","directed":false,"target":"0321127420"}}} +{"cn":{"0596007124":{"label":"Head First Desi...","full-label":"Head First Design Patterns (Head First)"}}} +{"an":{"0596008678":{"label":"Head First Desi...","full-label":"Head First Design Patterns (Head First)"}}} +{"ae":{"0596007124_0596008678":{"source":"0596007124","directed":false,"target":"0596008678"}}} +{"an":{"0596009208":{"label":"Head First Desi...","full-label":"Head First Design Patterns (Head First)"}}} +{"ae":{"0596007124_0596009208":{"source":"0596007124","directed":false,"target":"0596009208"}}} +{"an":{"0596516681":{"label":"Head First Desi...","full-label":"Head First Design Patterns (Head First)"}}} +{"ae":{"0596007124_0596516681":{"source":"0596007124","directed":false,"target":"0596516681"}}} +{"an":{"0596102143":{"label":"Head First Desi...","full-label":"Head First Design Patterns (Head First)"}}} +{"ae":{"0596007124_0596102143":{"source":"0596007124","directed":false,"target":"0596102143"}}} +{"cn":{"0321334876":{"label":"Effective C++: ...","full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"an":{"020163371X":{"label":"Effective C++: ...","full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0321334876_020163371X":{"source":"0321334876","directed":false,"target":"020163371X"}}} +{"an":{"0201749629":{"label":"Effective C++: ...","full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0321334876_0201749629":{"source":"0321334876","directed":false,"target":"0201749629"}}} +{"ae":{"0201700735_0321334876":{"source":"0201700735","directed":false,"target":"0321334876"}}} +{"an":{"0201379260":{"label":"Effective C++: ...","full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0321334876_0201379260":{"source":"0321334876","directed":false,"target":"0201379260"}}} +{"cn":{"0321193687":{"label":"UML Distilled: ...","full-label":"UML Distilled: A Brief Guide to the Standard Object Modeling Language (3rd Edition) (The Addison-Wesley Object Technology Series)"}}} +{"an":{"0131489062":{"label":"UML Distilled: ...","full-label":"UML Distilled: A Brief Guide to the Standard Object Modeling Language (3rd Edition) (The Addison-Wesley Object Technology Series)"}}} +{"ae":{"0321193687_0131489062":{"source":"0321193687","directed":false,"target":"0131489062"}}} +{"ae":{"0201485672_0321193687":{"source":"0201485672","directed":false,"target":"0321193687"}}} +{"ae":{"0596007124_0321193687":{"source":"0596007124","directed":false,"target":"0321193687"}}} +{"ae":{"0735619670_0321193687":{"source":"0735619670","directed":false,"target":"0321193687"}}} +{"cn":{"0470121688":{"label":"Puzzles for Pro...","full-label":"Puzzles for Programmers and Pros"}}} +{"ae":{"0201657880_0470121688":{"source":"0201657880","directed":false,"target":"0470121688"}}} +{"ae":{"B000JBY0RY_0470121688":{"source":"B000JBY0RY","directed":false,"target":"0470121688"}}} +{"ae":{"B000ESSSN4_0470121688":{"source":"B000ESSSN4","directed":false,"target":"0470121688"}}} +{"an":{"0486281523":{"label":"Puzzles for Pro...","full-label":"Puzzles for Programmers and Pros"}}} +{"ae":{"0470121688_0486281523":{"source":"0470121688","directed":false,"target":"0486281523"}}} +{"cn":{"0596529309":{"label":"High Performanc...","full-label":"High Performance Web Sites: Essential Knowledge for Front-End Engineers"}}} +{"ae":{"0596102356_0596529309":{"source":"0596102356","directed":false,"target":"0596529309"}}} +{"ae":{"0596529260_0596529309":{"source":"0596529260","directed":false,"target":"0596529309"}}} +{"an":{"0596528108":{"label":"High Performanc...","full-label":"High Performance Web Sites: Essential Knowledge for Front-End Engineers"}}} +{"ae":{"0596529309_0596528108":{"source":"0596529309","directed":false,"target":"0596528108"}}} +{"ae":{"0596510047_0596529309":{"source":"0596510047","directed":false,"target":"0596529309"}}} +{"cn":{"0596529260":{"label":"RESTful Web Ser...","full-label":"RESTful Web Services"}}} +{"ae":{"0596102356_0596529260":{"source":"0596102356","directed":false,"target":"0596529260"}}} +{"an":{"0977616630":{"label":"RESTful Web Ser...","full-label":"RESTful Web Services"}}} +{"ae":{"0596529260_0977616630":{"source":"0596529260","directed":false,"target":"0977616630"}}} +{"cn":{"0596102356":{"label":"Building Scalab...","full-label":"Building Scalable Web Sites: Building, scaling, and optimizing the next generation of web applications"}}} +{"an":{"067232699X":{"label":"Building Scalab...","full-label":"Building Scalable Web Sites: Building, scaling, and optimizing the next generation of web applications"}}} +{"ae":{"0596102356_067232699X":{"source":"0596102356","directed":false,"target":"067232699X"}}} +{"an":{"0596003064":{"label":"Building Scalab...","full-label":"Building Scalable Web Sites: Building, scaling, and optimizing the next generation of web applications"}}} +{"ae":{"0596102356_0596003064":{"source":"0596102356","directed":false,"target":"0596003064"}}} +{"cn":{"0596514557":{"label":"Visualizing Dat...","full-label":"Visualizing Data"}}} +{"an":{"0262182629":{"label":"Visualizing Dat...","full-label":"Visualizing Data"}}} +{"ae":{"0596514557_0262182629":{"source":"0596514557","directed":false,"target":"0262182629"}}} +{"an":{"159059617X":{"label":"Visualizing Dat...","full-label":"Visualizing Data"}}} +{"ae":{"0596514557_159059617X":{"source":"0596514557","directed":false,"target":"159059617X"}}} +{"an":{"0596510519":{"label":"Visualizing Dat...","full-label":"Visualizing Data"}}} +{"ae":{"0596514557_0596510519":{"source":"0596514557","directed":false,"target":"0596510519"}}} +{"an":{"0321525655":{"label":"Visualizing Dat...","full-label":"Visualizing Data"}}} +{"ae":{"0596514557_0321525655":{"source":"0596514557","directed":false,"target":"0321525655"}}} +{"cn":{"1400082471":{"label":"Dreaming in Cod...","full-label":"Dreaming in Code: Two Dozen Programmers, Three Years, 4,732 Bugs, and One Quest for Transcendent Software"}}} +{"an":{"1590597141":{"label":"Dreaming in Cod...","full-label":"Dreaming in Code: Two Dozen Programmers, Three Years, 4,732 Bugs, and One Quest for Transcendent Software"}}} +{"ae":{"1400082471_1590597141":{"source":"1400082471","directed":false,"target":"1590597141"}}} +{"ae":{"0201835959_1400082471":{"source":"0201835959","directed":false,"target":"1400082471"}}} +{"ae":{"159059844X_1400082471":{"source":"159059844X","directed":false,"target":"1400082471"}}} +{"an":{"1590598385":{"label":"Dreaming in Cod...","full-label":"Dreaming in Code: Two Dozen Programmers, Three Years, 4,732 Bugs, and One Quest for Transcendent Software"}}} +{"ae":{"1400082471_1590598385":{"source":"1400082471","directed":false,"target":"1590598385"}}} +{"cn":{"193435600X":{"label":"Programming Erl...","full-label":"Programming Erlang: Software for a Concurrent World"}}} +{"ae":{"0596529325_193435600X":{"source":"0596529325","directed":false,"target":"193435600X"}}} +{"ae":{"0596529260_193435600X":{"source":"0596529260","directed":false,"target":"193435600X"}}} +{"an":{"0978739256":{"label":"Programming Erl...","full-label":"Programming Erlang: Software for a Concurrent World"}}} +{"ae":{"193435600X_0978739256":{"source":"193435600X","directed":false,"target":"0978739256"}}} +{"an":{"0521692695":{"label":"Programming Erl...","full-label":"Programming Erlang: Software for a Concurrent World"}}} +{"ae":{"193435600X_0521692695":{"source":"193435600X","directed":false,"target":"0521692695"}}} +{"cn":{"159059844X":{"label":"Managing Humans...","full-label":"Managing Humans: Biting and Humorous Tales of a Software Engineering Manager"}}} +{"ae":{"1590598385_159059844X":{"source":"1590598385","directed":false,"target":"159059844X"}}} +{"an":{"0596527055":{"label":"Managing Humans...","full-label":"Managing Humans: Biting and Humorous Tales of a Software Engineering Manager"}}} +{"ae":{"159059844X_0596527055":{"source":"159059844X","directed":false,"target":"0596527055"}}} +{"ae":{"1590597141_159059844X":{"source":"1590597141","directed":false,"target":"159059844X"}}} +{"cn":{"B000JBY0RY":{"label":"How Would You M...","full-label":"How Would You Move Mount Fuji? Microsoft's Cult of the Puzzle - How the World's Smartest Company Selects the Most Creative Thinkers"}}} +{"ae":{"0201657880_B000JBY0RY":{"source":"0201657880","directed":false,"target":"B000JBY0RY"}}} +{"an":{"0071440011":{"label":"How Would You M...","full-label":"How Would You Move Mount Fuji? Microsoft's Cult of the Puzzle - How the World's Smartest Company Selects the Most Creative Thinkers"}}} +{"ae":{"B000JBY0RY_0071440011":{"source":"B000JBY0RY","directed":false,"target":"0071440011"}}} +{"an":{"0385242719":{"label":"How Would You M...","full-label":"How Would You Move Mount Fuji? Microsoft's Cult of the Puzzle - How the World's Smartest Company Selects the Most Creative Thinkers"}}} +{"ae":{"B000JBY0RY_0385242719":{"source":"B000JBY0RY","directed":false,"target":"0385242719"}}} +{"cn":{"B000ESSSN4":{"label":"How Would You M...","full-label":"How Would You Move Mount Fuji? : Microsoft's Cult of the Puzzle -- How the World's Smartest Companies Select the Most Creative Thinkers"}}} +{"ae":{"0201657880_B000ESSSN4":{"source":"0201657880","directed":false,"target":"B000ESSSN4"}}} +{"ae":{"0071440011_B000ESSSN4":{"source":"0071440011","directed":false,"target":"B000ESSSN4"}}} +{"ae":{"0385242719_B000ESSSN4":{"source":"0385242719","directed":false,"target":"B000ESSSN4"}}} +{"an":{"0970055269":{"label":"How Would You M...","full-label":"How Would You Move Mount Fuji? : Microsoft's Cult of the Puzzle -- How the World's Smartest Companies Select the Most Creative Thinkers"}}} +{"ae":{"B000ESSSN4_0970055269":{"source":"B000ESSSN4","directed":false,"target":"0970055269"}}} +{"cn":{"0071495789":{"label":"Ace the IT Inte...","full-label":"Ace the IT Interview (Ace the It Job Interview)"}}} +{"an":{"0071492747":{"label":"Ace the IT Inte...","full-label":"Ace the IT Interview (Ace the It Job Interview)"}}} +{"ae":{"0071495789_0071492747":{"source":"0071495789","directed":false,"target":"0071492747"}}} +{"an":{"0071425470":{"label":"Ace the IT Inte...","full-label":"Ace the IT Interview (Ace the It Job Interview)"}}} +{"ae":{"0071495789_0071425470":{"source":"0071495789","directed":false,"target":"0071425470"}}} +{"an":{"0071387730":{"label":"Ace the IT Inte...","full-label":"Ace the IT Interview (Ace the It Job Interview)"}}} +{"ae":{"0071495789_0071387730":{"source":"0071495789","directed":false,"target":"0071387730"}}} +{"an":{"1402203853":{"label":"Ace the IT Inte...","full-label":"Ace the IT Interview (Ace the It Job Interview)"}}} +{"ae":{"0071495789_1402203853":{"source":"0071495789","directed":false,"target":"1402203853"}}} +{"cn":{"1556159005":{"label":"Rapid Developme...","full-label":"Rapid Development: Taming Wild Software Schedules"}}} +{"ae":{"0735605351_1556159005":{"source":"0735605351","directed":false,"target":"1556159005"}}} +{"an":{"1572316217":{"label":"Rapid Developme...","full-label":"Rapid Development: Taming Wild Software Schedules"}}} +{"ae":{"1556159005_1572316217":{"source":"1556159005","directed":false,"target":"1572316217"}}} +{"ae":{"0201835959_1556159005":{"source":"0201835959","directed":false,"target":"1556159005"}}} +{"ae":{"0932633439_1556159005":{"source":"0932633439","directed":false,"target":"1556159005"}}} +{"cn":{"0735605351":{"label":"Software Estima...","full-label":"Software Estimation: Demystifying the Black Art (Best Practices (Microsoft))"}}} +{"an":{"0735618798":{"label":"Software Estima...","full-label":"Software Estimation: Demystifying the Black Art (Best Practices (Microsoft))"}}} +{"ae":{"0735605351_0735618798":{"source":"0735605351","directed":false,"target":"0735618798"}}} +{"ae":{"0932633439_0735605351":{"source":"0932633439","directed":false,"target":"0735605351"}}} +{"an":{"0131479415":{"label":"Software Estima...","full-label":"Software Estimation: Demystifying the Black Art (Best Practices (Microsoft))"}}} +{"ae":{"0735605351_0131479415":{"source":"0735605351","directed":false,"target":"0131479415"}}} +{"cn":{"0201835959":{"label":"The Mythical Ma...","full-label":"The Mythical Man-Month: Essays on Software Engineering, Anniversary Edition (2nd Edition)"}}} +{"ae":{"0932633439_0201835959":{"source":"0932633439","directed":false,"target":"0201835959"}}} +{"ae":{"0201633612_0201835959":{"source":"0201633612","directed":false,"target":"0201835959"}}} +{"cn":{"013937681X":{"label":"Unix Programmin...","full-label":"Unix Programming Environment (Prentice-Hall Software Series)"}}} +{"an":{"0132017997":{"label":"Unix Programmin...","full-label":"Unix Programming Environment (Prentice-Hall Software Series)"}}} +{"ae":{"013937681X_0132017997":{"source":"013937681X","directed":false,"target":"0132017997"}}} +{"an":{"020103669X":{"label":"Unix Programmin...","full-label":"Unix Programming Environment (Prentice-Hall Software Series)"}}} +{"ae":{"013937681X_020103669X":{"source":"013937681X","directed":false,"target":"020103669X"}}} +{"ae":{"0201433079_013937681X":{"source":"0201433079","directed":false,"target":"013937681X"}}} +{"cn":{"0932633439":{"label":"Peopleware: Pro...","full-label":"Peopleware: Productive Projects and Teams (Second Edition)"}}} +{"ae":{"0735619670_0932633439":{"source":"0735619670","directed":false,"target":"0932633439"}}} +{"an":{"1590593898":{"label":"Peopleware: Pro...","full-label":"Peopleware: Productive Projects and Teams (Second Edition)"}}} +{"ae":{"0932633439_1590593898":{"source":"0932633439","directed":false,"target":"1590593898"}}} +{"cn":{"0387952845":{"label":"The Elements of...","full-label":"The Elements of Statistical Learning"}}} +{"ae":{"0471056693_0387952845":{"source":"0471056693","directed":false,"target":"0387952845"}}} +{"ae":{"0120884070_0387952845":{"source":"0120884070","directed":false,"target":"0387952845"}}} +{"an":{"0387402721":{"label":"The Elements of...","full-label":"The Elements of Statistical Learning"}}} +{"ae":{"0387952845_0387402721":{"source":"0387952845","directed":false,"target":"0387402721"}}} +{"an":{"158488388X":{"label":"The Elements of...","full-label":"The Elements of Statistical Learning"}}} +{"ae":{"0387952845_158488388X":{"source":"0387952845","directed":false,"target":"158488388X"}}} +{"cn":{"0471056693":{"label":"Pattern Classif...","full-label":"Pattern Classification (2nd Edition)"}}} +{"an":{"0471429775":{"label":"Pattern Classif...","full-label":"Pattern Classification (2nd Edition)"}}} +{"ae":{"0471056693_0471429775":{"source":"0471056693","directed":false,"target":"0471429775"}}} +{"ae":{"0120884070_0471056693":{"source":"0120884070","directed":false,"target":"0471056693"}}} +{"cn":{"0120884070":{"label":"Data Mining: Pr...","full-label":"Data Mining: Practical Machine Learning Tools and Techniques, Second Edition (Morgan Kaufmann Series in Data Management Systems)"}}} +{"an":{"1558609016":{"label":"Data Mining: Pr...","full-label":"Data Mining: Practical Machine Learning Tools and Techniques, Second Edition (Morgan Kaufmann Series in Data Management Systems)"}}} +{"ae":{"0120884070_1558609016":{"source":"0120884070","directed":false,"target":"1558609016"}}} +{"ae":{"0596529325_0120884070":{"source":"0596529325","directed":false,"target":"0120884070"}}} +{"an":{"0262012111":{"label":"Data Mining: Pr...","full-label":"Data Mining: Practical Machine Learning Tools and Techniques, Second Edition (Morgan Kaufmann Series in Data Management Systems)"}}} +{"ae":{"0120884070_0262012111":{"source":"0120884070","directed":false,"target":"0262012111"}}} +{"cn":{"026218253X":{"label":"Gaussian Proces...","full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)"}}} +{"an":{"0262194759":{"label":"Gaussian Proces...","full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)"}}} +{"ae":{"026218253X_0262194759":{"source":"026218253X","directed":false,"target":"0262194759"}}} +{"an":{"0262033585":{"label":"Gaussian Proces...","full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)"}}} +{"ae":{"026218253X_0262033585":{"source":"026218253X","directed":false,"target":"0262033585"}}} +{"ae":{"0387952845_026218253X":{"source":"0387952845","directed":false,"target":"026218253X"}}} +{"an":{"0521642981":{"label":"Gaussian Proces...","full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)"}}} +{"ae":{"026218253X_0521642981":{"source":"026218253X","directed":false,"target":"0521642981"}}} +{"cn":{"0486402584":{"label":"Combinatorial O...","full-label":"Combinatorial Optimization: Algorithms and Complexity"}}} +{"ae":{"3540653678_0486402584":{"source":"3540653678","directed":false,"target":"0486402584"}}} +{"an":{"0486247759":{"label":"Combinatorial O...","full-label":"Combinatorial Optimization: Algorithms and Complexity"}}} +{"ae":{"0486402584_0486247759":{"source":"0486402584","directed":false,"target":"0486247759"}}} +{"ae":{"0262032937_0486402584":{"source":"0262032937","directed":false,"target":"0486402584"}}} +{"an":{"0486678709":{"label":"Combinatorial O...","full-label":"Combinatorial Optimization: Algorithms and Complexity"}}} +{"ae":{"0486402584_0486678709":{"source":"0486402584","directed":false,"target":"0486678709"}}} +{"cn":{"3540653678":{"label":"Approximation A...","full-label":"Approximation Algorithms"}}} +{"an":{"0521474655":{"label":"Approximation A...","full-label":"Approximation Algorithms"}}} +{"ae":{"3540653678_0521474655":{"source":"3540653678","directed":false,"target":"0521474655"}}} +{"an":{"0521872820":{"label":"Approximation A...","full-label":"Approximation Algorithms"}}} +{"ae":{"3540653678_0521872820":{"source":"3540653678","directed":false,"target":"0521872820"}}} +{"an":{"0521835402":{"label":"Approximation A...","full-label":"Approximation Algorithms"}}} +{"ae":{"3540653678_0521835402":{"source":"3540653678","directed":false,"target":"0521835402"}}} +{"cn":{"0201530821":{"label":"Computational C...","full-label":"Computational Complexity"}}} +{"ae":{"0534950973_0201530821":{"source":"0534950973","directed":false,"target":"0201530821"}}} +{"ae":{"0486402584_0201530821":{"source":"0486402584","directed":false,"target":"0201530821"}}} +{"ae":{"3540653678_0201530821":{"source":"3540653678","directed":false,"target":"0201530821"}}} +{"ae":{"0262032937_0201530821":{"source":"0262032937","directed":false,"target":"0201530821"}}} +{"cn":{"B00007FYCY":{"label":"Lex & Yacc","full-label":"Lex & Yacc"}}} +{"an":{"0534939724":{"label":"Lex & Yacc","full-label":"Lex & Yacc"}}} +{"ae":{"B00007FYCY_0534939724":{"source":"B00007FYCY","directed":false,"target":"0534939724"}}} +{"ae":{"0262032937_B00007FYCY":{"source":"0262032937","directed":false,"target":"B00007FYCY"}}} +{"an":{"B00007FYIJ":{"label":"Lex & Yacc","full-label":"Lex & Yacc"}}} +{"ae":{"B00007FYCY_B00007FYIJ":{"source":"B00007FYCY","directed":false,"target":"B00007FYIJ"}}} +{"ae":{"013089592X_B00007FYCY":{"source":"013089592X","directed":false,"target":"B00007FYCY"}}} +{"cn":{"0126339511":{"label":"Programming Lan...","full-label":"Programming Language Pragmatics, Second Edition"}}} +{"ae":{"0262032937_0126339511":{"source":"0262032937","directed":false,"target":"0126339511"}}} +{"ae":{"0596510047_0126339511":{"source":"0596510047","directed":false,"target":"0126339511"}}} +{"an":{"155860698X":{"label":"Programming Lan...","full-label":"Programming Language Pragmatics, Second Edition"}}} +{"ae":{"0126339511_155860698X":{"source":"0126339511","directed":false,"target":"155860698X"}}} +{"ae":{"0534950973_0126339511":{"source":"0534950973","directed":false,"target":"0126339511"}}} +{"cn":{"0321369572":{"label":"Fundamentals of...","full-label":"Fundamentals of Database Systems (5th Edition)"}}} +{"an":{"0072253517":{"label":"Fundamentals of...","full-label":"Fundamentals of Database Systems (5th Edition)"}}} +{"ae":{"0321369572_0072253517":{"source":"0321369572","directed":false,"target":"0072253517"}}} +{"ae":{"0471694665_0321369572":{"source":"0471694665","directed":false,"target":"0321369572"}}} +{"ae":{"0262032937_0321369572":{"source":"0262032937","directed":false,"target":"0321369572"}}} +{"ae":{"0137903952_0321369572":{"source":"0137903952","directed":false,"target":"0321369572"}}} +{"cn":{"1593271042":{"label":"Inside the Mach...","full-label":"Inside the Machine: An Illustrated Introduction to Microprocessors and Computer Architecture"}}} +{"an":{"1593270038":{"label":"Inside the Mach...","full-label":"Inside the Machine: An Illustrated Introduction to Microprocessors and Computer Architecture"}}} +{"ae":{"1593271042_1593270038":{"source":"1593271042","directed":false,"target":"1593270038"}}} +{"ae":{"0596510047_1593271042":{"source":"0596510047","directed":false,"target":"1593271042"}}} +{"an":{"1593270658":{"label":"Inside the Mach...","full-label":"Inside the Machine: An Illustrated Introduction to Microprocessors and Computer Architecture"}}} +{"ae":{"1593271042_1593270658":{"source":"1593271042","directed":false,"target":"1593270658"}}} +{"ae":{"0123706068_1593271042":{"source":"0123706068","directed":false,"target":"1593271042"}}} +{"cn":{"0201700735":{"label":"The C++ Program...","full-label":"The C++ Programming Language: Special Edition (3rd Edition)"}}} +{"ae":{"0201379260_0201700735":{"source":"0201379260","directed":false,"target":"0201700735"}}} +{"ae":{"0201633612_0201700735":{"source":"0201633612","directed":false,"target":"0201700735"}}} +{"ae":{"020163371X_0201700735":{"source":"020163371X","directed":false,"target":"0201700735"}}} +{"cn":{"013089592X":{"label":"C: A Reference ...","full-label":"C: A Reference Manual (5th Edition)"}}} +{"an":{"0672326663":{"label":"C: A Reference ...","full-label":"C: A Reference Manual (5th Edition)"}}} +{"ae":{"013089592X_0672326663":{"source":"013089592X","directed":false,"target":"0672326663"}}} +{"an":{"0201179288":{"label":"C: A Reference ...","full-label":"C: A Reference Manual (5th Edition)"}}} +{"ae":{"013089592X_0201179288":{"source":"013089592X","directed":false,"target":"0201179288"}}} +{"an":{"0131774298":{"label":"C: A Reference ...","full-label":"C: A Reference Manual (5th Edition)"}}} +{"ae":{"013089592X_0131774298":{"source":"013089592X","directed":false,"target":"0131774298"}}} +{"ae":{"0201433079_013089592X":{"source":"0201433079","directed":false,"target":"013089592X"}}} +{"cn":{"0672305100":{"label":"Absolute Beginn...","full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)"}}} +{"ae":{"0672326663_0672305100":{"source":"0672326663","directed":false,"target":"0672305100"}}} +{"an":{"0764570684":{"label":"Absolute Beginn...","full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)"}}} +{"ae":{"0672305100_0764570684":{"source":"0672305100","directed":false,"target":"0764570684"}}} +{"an":{"0672326965":{"label":"Absolute Beginn...","full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)"}}} +{"ae":{"0672305100_0672326965":{"source":"0672305100","directed":false,"target":"0672326965"}}} +{"an":{"0764570692":{"label":"Absolute Beginn...","full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)"}}} +{"ae":{"0672305100_0764570692":{"source":"0672305100","directed":false,"target":"0764570692"}}} +{"cn":{"0201433079":{"label":"Advanced Progra...","full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"an":{"0131411551":{"label":"Advanced Progra...","full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201433079_0131411551":{"source":"0201433079","directed":false,"target":"0131411551"}}} +{"an":{"0130810819":{"label":"Advanced Progra...","full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201433079_0130810819":{"source":"0201433079","directed":false,"target":"0130810819"}}} +{"an":{"0131411543":{"label":"Advanced Progra...","full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201433079_0131411543":{"source":"0201433079","directed":false,"target":"0131411543"}}} +{"an":{"0596005652":{"label":"Advanced Progra...","full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201433079_0596005652":{"source":"0201433079","directed":false,"target":"0596005652"}}} +{"cn":{"0321146530":{"label":"Test Driven Dev...","full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)"}}} +{"an":{"0321278658":{"label":"Test Driven Dev...","full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)"}}} +{"ae":{"0321146530_0321278658":{"source":"0321146530","directed":false,"target":"0321278658"}}} +{"an":{"0321205685":{"label":"Test Driven Dev...","full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)"}}} +{"ae":{"0321146530_0321205685":{"source":"0321146530","directed":false,"target":"0321205685"}}} +{"an":{"0131177052":{"label":"Test Driven Dev...","full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)"}}} +{"ae":{"0321146530_0131177052":{"source":"0321146530","directed":false,"target":"0131177052"}}} +{"ae":{"0131479415_0321146530":{"source":"0131479415","directed":false,"target":"0321146530"}}} +{"cn":{"0321127420":{"label":"Patterns of Ent...","full-label":"Patterns of Enterprise Application Architecture (The Addison-Wesley Signature Series)"}}} +{"an":{"0321200683":{"label":"Patterns of Ent...","full-label":"Patterns of Enterprise Application Architecture (The Addison-Wesley Signature Series)"}}} +{"ae":{"0321127420_0321200683":{"source":"0321127420","directed":false,"target":"0321200683"}}} +{"an":{"0321125215":{"label":"Patterns of Ent...","full-label":"Patterns of Enterprise Application Architecture (The Addison-Wesley Signature Series)"}}} +{"ae":{"0321127420_0321125215":{"source":"0321127420","directed":false,"target":"0321125215"}}} +{"ae":{"0201633612_0321127420":{"source":"0201633612","directed":false,"target":"0321127420"}}} +{"ae":{"0596007124_0321127420":{"source":"0596007124","directed":false,"target":"0321127420"}}} +{"cn":{"0596008678":{"label":"Head First Obje...","full-label":"Head First Object-Oriented Analysis and Design (Head First)"}}} +{"ae":{"0596009208_0596008678":{"source":"0596009208","directed":false,"target":"0596008678"}}} +{"an":{"0596527357":{"label":"Head First Obje...","full-label":"Head First Object-Oriented Analysis and Design (Head First)"}}} +{"ae":{"0596008678_0596527357":{"source":"0596008678","directed":false,"target":"0596527357"}}} +{"ae":{"0596102143_0596008678":{"source":"0596102143","directed":false,"target":"0596008678"}}} +{"an":{"059610197X":{"label":"Head First Obje...","full-label":"Head First Object-Oriented Analysis and Design (Head First)"}}} +{"ae":{"0596008678_059610197X":{"source":"0596008678","directed":false,"target":"059610197X"}}} +{"cn":{"0596009208":{"label":"Head First Java...","full-label":"Head First Java, 2nd Edition"}}} +{"ae":{"0596516681_0596009208":{"source":"0596516681","directed":false,"target":"0596009208"}}} +{"ae":{"059610197X_0596009208":{"source":"059610197X","directed":false,"target":"0596009208"}}} +{"an":{"0596005717":{"label":"Head First Java...","full-label":"Head First Java, 2nd Edition"}}} +{"ae":{"0596009208_0596005717":{"source":"0596009208","directed":false,"target":"0596005717"}}} +{"cn":{"0596516681":{"label":"Head First Serv...","full-label":"Head First Servlets and JSP: Passing the Sun Certified Web Component Developer Exam (Brain-Friendly Guides)"}}} +{"ae":{"0596005717_0596516681":{"source":"0596005717","directed":false,"target":"0596516681"}}} +{"an":{"0072253606":{"label":"Head First Serv...","full-label":"Head First Servlets and JSP: Passing the Sun Certified Web Component Developer Exam (Brain-Friendly Guides)"}}} +{"ae":{"0596516681_0072253606":{"source":"0596516681","directed":false,"target":"0072253606"}}} +{"ae":{"059610197X_0596516681":{"source":"059610197X","directed":false,"target":"0596516681"}}} +{"cn":{"0596102143":{"label":"Head First Desi...","full-label":"Head First Design Patterns Poster (Head First)"}}} +{"ae":{"0596009208_0596102143":{"source":"0596009208","directed":false,"target":"0596102143"}}} +{"ae":{"0596527357_0596102143":{"source":"0596527357","directed":false,"target":"0596102143"}}} +{"ae":{"059610197X_0596102143":{"source":"059610197X","directed":false,"target":"0596102143"}}} +{"cn":{"020163371X":{"label":"More Effective ...","full-label":"More Effective C++: 35 New Ways to Improve Your Programs and Designs (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201749629_020163371X":{"source":"0201749629","directed":false,"target":"020163371X"}}} +{"ae":{"0201633612_020163371X":{"source":"0201633612","directed":false,"target":"020163371X"}}} +{"ae":{"0201379260_020163371X":{"source":"0201379260","directed":false,"target":"020163371X"}}} +{"cn":{"0201749629":{"label":"Effective STL: ...","full-label":"Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0201379260_0201749629":{"source":"0201379260","directed":false,"target":"0201749629"}}} +{"ae":{"0201633612_0201749629":{"source":"0201633612","directed":false,"target":"0201749629"}}} +{"ae":{"0201700735_0201749629":{"source":"0201700735","directed":false,"target":"0201749629"}}} +{"cn":{"0201379260":{"label":"The C++ Standar...","full-label":"The C++ Standard Library: A Tutorial and Reference"}}} +{"an":{"0201734842":{"label":"The C++ Standar...","full-label":"The C++ Standard Library: A Tutorial and Reference"}}} +{"ae":{"0201379260_0201734842":{"source":"0201379260","directed":false,"target":"0201734842"}}} +{"ae":{"0201633612_0201379260":{"source":"0201633612","directed":false,"target":"0201379260"}}} +{"cn":{"0131489062":{"label":"Applying UML an...","full-label":"Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development (3rd Edition)"}}} +{"ae":{"0596007124_0131489062":{"source":"0596007124","directed":false,"target":"0131489062"}}} +{"ae":{"0201633612_0131489062":{"source":"0201633612","directed":false,"target":"0131489062"}}} +{"an":{"0201702258":{"label":"Applying UML an...","full-label":"Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development (3rd Edition)"}}} +{"ae":{"0131489062_0201702258":{"source":"0131489062","directed":false,"target":"0201702258"}}} +{"ae":{"0201485672_0131489062":{"source":"0201485672","directed":false,"target":"0131489062"}}} +{"cn":{"0486281523":{"label":"My Best Mathema...","full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)"}}} +{"an":{"0486252116":{"label":"My Best Mathema...","full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)"}}} +{"ae":{"0486281523_0486252116":{"source":"0486281523","directed":false,"target":"0486252116"}}} +{"an":{"0486270785":{"label":"My Best Mathema...","full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)"}}} +{"ae":{"0486281523_0486270785":{"source":"0486281523","directed":false,"target":"0486270785"}}} +{"an":{"0486256375":{"label":"My Best Mathema...","full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)"}}} +{"ae":{"0486281523_0486256375":{"source":"0486281523","directed":false,"target":"0486256375"}}} +{"an":{"0486427552":{"label":"My Best Mathema...","full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)"}}} +{"ae":{"0486281523_0486427552":{"source":"0486281523","directed":false,"target":"0486427552"}}} +{"an":{"0970825315":{"label":"My Best Mathema...","full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)"}}} +{"ae":{"0486281523_0970825315":{"source":"0486281523","directed":false,"target":"0970825315"}}} +{"cn":{"0596528108":{"label":"Designing Web N...","full-label":"Designing Web Navigation: Optimizing the User Experience"}}} +{"an":{"0596527349":{"label":"Designing Web N...","full-label":"Designing Web Navigation: Optimizing the User Experience"}}} +{"ae":{"0596528108_0596527349":{"source":"0596528108","directed":false,"target":"0596527349"}}} +{"ae":{"0596529325_0596528108":{"source":"0596529325","directed":false,"target":"0596528108"}}} +{"an":{"0321344758":{"label":"Designing Web N...","full-label":"Designing Web Navigation: Optimizing the User Experience"}}} +{"ae":{"0596528108_0321344758":{"source":"0596528108","directed":false,"target":"0321344758"}}} +{"an":{"0596008031":{"label":"Designing Web N...","full-label":"Designing Web Navigation: Optimizing the User Experience"}}} +{"ae":{"0596528108_0596008031":{"source":"0596528108","directed":false,"target":"0596008031"}}} +{"cn":{"0977616630":{"label":"Agile Web Devel...","full-label":"Agile Web Development with Rails, 2nd Edition"}}} +{"an":{"0974514055":{"label":"Agile Web Devel...","full-label":"Agile Web Development with Rails, 2nd Edition"}}} +{"ae":{"0977616630_0974514055":{"source":"0977616630","directed":false,"target":"0974514055"}}} +{"an":{"0977616606":{"label":"Agile Web Devel...","full-label":"Agile Web Development with Rails, 2nd Edition"}}} +{"ae":{"0977616630_0977616606":{"source":"0977616630","directed":false,"target":"0977616606"}}} +{"an":{"0596527446":{"label":"Agile Web Devel...","full-label":"Agile Web Development with Rails, 2nd Edition"}}} +{"ae":{"0977616630_0596527446":{"source":"0977616630","directed":false,"target":"0596527446"}}} +{"an":{"0596523696":{"label":"Agile Web Devel...","full-label":"Agile Web Development with Rails, 2nd Edition"}}} +{"ae":{"0977616630_0596523696":{"source":"0977616630","directed":false,"target":"0596523696"}}} +{"an":{"1932394699":{"label":"Agile Web Devel...","full-label":"Agile Web Development with Rails, 2nd Edition"}}} +{"ae":{"0977616630_1932394699":{"source":"0977616630","directed":false,"target":"1932394699"}}} +{"cn":{"067232699X":{"label":"Scalable Intern...","full-label":"Scalable Internet Architectures (Developer's Library)"}}} +{"ae":{"0596529309_067232699X":{"source":"0596529309","directed":false,"target":"067232699X"}}} +{"ae":{"0596529325_067232699X":{"source":"0596529325","directed":false,"target":"067232699X"}}} +{"ae":{"0596529260_067232699X":{"source":"0596529260","directed":false,"target":"067232699X"}}} +{"ae":{"0596003064_067232699X":{"source":"0596003064","directed":false,"target":"067232699X"}}} +{"cn":{"0596003064":{"label":"High Performanc...","full-label":"High Performance MySQL"}}} +{"an":{"059652708X":{"label":"High Performanc...","full-label":"High Performance MySQL"}}} +{"ae":{"0596003064_059652708X":{"source":"0596003064","directed":false,"target":"059652708X"}}} +{"an":{"0596100892":{"label":"High Performanc...","full-label":"High Performance MySQL"}}} +{"ae":{"0596003064_0596100892":{"source":"0596003064","directed":false,"target":"0596100892"}}} +{"ae":{"0596529309_0596003064":{"source":"0596529309","directed":false,"target":"0596003064"}}} +{"an":{"159059505X":{"label":"High Performanc...","full-label":"High Performance MySQL"}}} +{"ae":{"0596003064_159059505X":{"source":"0596003064","directed":false,"target":"159059505X"}}} +{"cn":{"0262182629":{"label":"Processing: A P...","full-label":"Processing: A Programming Handbook for Visual Designers and Artists"}}} +{"ae":{"159059617X_0262182629":{"source":"159059617X","directed":false,"target":"0262182629"}}} +{"ae":{"0596510519_0262182629":{"source":"0596510519","directed":false,"target":"0262182629"}}} +{"an":{"159200346X":{"label":"Processing: A P...","full-label":"Processing: A Programming Handbook for Visual Designers and Artists"}}} +{"ae":{"0262182629_159200346X":{"source":"0262182629","directed":false,"target":"159200346X"}}} +{"ae":{"0596529325_0262182629":{"source":"0596529325","directed":false,"target":"0262182629"}}} +{"cn":{"159059617X":{"label":"Processing: Cre...","full-label":"Processing: Creative Coding and Computational Art (Foundation)"}}} +{"ae":{"0596510519_159059617X":{"source":"0596510519","directed":false,"target":"159059617X"}}} +{"ae":{"159200346X_159059617X":{"source":"159200346X","directed":false,"target":"159059617X"}}} +{"an":{"0500285179":{"label":"Processing: Cre...","full-label":"Processing: Creative Coding and Computational Art (Foundation)"}}} +{"ae":{"159059617X_0500285179":{"source":"159059617X","directed":false,"target":"0500285179"}}} +{"cn":{"0596510519":{"label":"Making Things T...","full-label":"Making Things Talk: Practical Methods for Connecting Physical Objects"}}} +{"ae":{"159200346X_0596510519":{"source":"159200346X","directed":false,"target":"0596510519"}}} +{"an":{"059651428X":{"label":"Making Things T...","full-label":"Making Things Talk: Practical Methods for Connecting Physical Objects"}}} +{"ae":{"0596510519_059651428X":{"source":"0596510519","directed":false,"target":"059651428X"}}} +{"cn":{"0321525655":{"label":"Presentation Ze...","full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)"}}} +{"an":{"1591841992":{"label":"Presentation Ze...","full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)"}}} +{"ae":{"0321525655_1591841992":{"source":"0321525655","directed":false,"target":"1591841992"}}} +{"an":{"0735623872":{"label":"Presentation Ze...","full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)"}}} +{"ae":{"0321525655_0735623872":{"source":"0321525655","directed":false,"target":"0735623872"}}} +{"an":{"0195320697":{"label":"Presentation Ze...","full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)"}}} +{"ae":{"0321525655_0195320697":{"source":"0321525655","directed":false,"target":"0195320697"}}} +{"an":{"0979777704":{"label":"Presentation Ze...","full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)"}}} +{"ae":{"0321525655_0979777704":{"source":"0321525655","directed":false,"target":"0979777704"}}} +{"an":{"1400064287":{"label":"Presentation Ze...","full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)"}}} +{"ae":{"0321525655_1400064287":{"source":"0321525655","directed":false,"target":"1400064287"}}} +{"cn":{"1590597141":{"label":"Founders at Wor...","full-label":"Founders at Work: Stories of Startups' Early Days"}}} +{"an":{"1591840562":{"label":"Founders at Wor...","full-label":"Founders at Work: Stories of Startups' Early Days"}}} +{"ae":{"1590597141_1591840562":{"source":"1590597141","directed":false,"target":"1591840562"}}} +{"ae":{"0596527055_1590597141":{"source":"0596527055","directed":false,"target":"1590597141"}}} +{"ae":{"0596510047_1590597141":{"source":"0596510047","directed":false,"target":"1590597141"}}} +{"ae":{"0596529325_1590597141":{"source":"0596529325","directed":false,"target":"1590597141"}}} +{"cn":{"1590598385":{"label":"Smart and Gets ...","full-label":"Smart and Gets Things Done: Joel Spolsky's Concise Guide to Finding the Best Technical Talent"}}} +{"ae":{"1590593898_1590598385":{"source":"1590593898","directed":false,"target":"1590598385"}}} +{"ae":{"0596510047_1590598385":{"source":"0596510047","directed":false,"target":"1590598385"}}} +{"ae":{"1590597141_1590598385":{"source":"1590597141","directed":false,"target":"1590598385"}}} +{"ae":{"0932633439_1590598385":{"source":"0932633439","directed":false,"target":"1590598385"}}} +{"cn":{"0978739256":{"label":"The Definitive ...","full-label":"The Definitive ANTLR Reference: Building Domain-Specific Languages (Pragmatic Programmers)"}}} +{"ae":{"0596510047_0978739256":{"source":"0596510047","directed":false,"target":"0978739256"}}} +{"ae":{"0596529325_0978739256":{"source":"0596529325","directed":false,"target":"0978739256"}}} +{"ae":{"0596529260_0978739256":{"source":"0596529260","directed":false,"target":"0978739256"}}} +{"an":{"0321349601":{"label":"The Definitive ...","full-label":"The Definitive ANTLR Reference: Building Domain-Specific Languages (Pragmatic Programmers)"}}} +{"ae":{"0978739256_0321349601":{"source":"0978739256","directed":false,"target":"0321349601"}}} +{"cn":{"0521692695":{"label":"Programming in ...","full-label":"Programming in Haskell"}}} +{"an":{"0521644089":{"label":"Programming in ...","full-label":"Programming in Haskell"}}} +{"ae":{"0521692695_0521644089":{"source":"0521692695","directed":false,"target":"0521644089"}}} +{"an":{"0954300696":{"label":"Programming in ...","full-label":"Programming in Haskell"}}} +{"ae":{"0521692695_0954300696":{"source":"0521692695","directed":false,"target":"0954300696"}}} +{"ae":{"0596510047_0521692695":{"source":"0596510047","directed":false,"target":"0521692695"}}} +{"an":{"0521663504":{"label":"Programming in ...","full-label":"Programming in Haskell"}}} +{"ae":{"0521692695_0521663504":{"source":"0521692695","directed":false,"target":"0521663504"}}} +{"cn":{"0596527055":{"label":"The Myths of In...","full-label":"The Myths of Innovation"}}} +{"ae":{"0596510047_0596527055":{"source":"0596510047","directed":false,"target":"0596527055"}}} +{"ae":{"0596529325_0596527055":{"source":"0596529325","directed":false,"target":"0596527055"}}} +{"ae":{"0321525655_0596527055":{"source":"0321525655","directed":false,"target":"0596527055"}}} +{"cn":{"0071440011":{"label":"How to Ace the ...","full-label":"How to Ace the Brainteaser Interview"}}} +{"ae":{"047012167X_0071440011":{"source":"047012167X","directed":false,"target":"0071440011"}}} +{"ae":{"0071387730_0071440011":{"source":"0071387730","directed":false,"target":"0071440011"}}} +{"an":{"0971015848":{"label":"How to Ace the ...","full-label":"How to Ace the Brainteaser Interview"}}} +{"ae":{"0071440011_0971015848":{"source":"0071440011","directed":false,"target":"0971015848"}}} +{"cn":{"0385242719":{"label":"Labyrinths of R...","full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge"}}} +{"an":{"038541580X":{"label":"Labyrinths of R...","full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge"}}} +{"ae":{"0385242719_038541580X":{"source":"0385242719","directed":false,"target":"038541580X"}}} +{"an":{"0809045990":{"label":"Labyrinths of R...","full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge"}}} +{"ae":{"0385242719_0809045990":{"source":"0385242719","directed":false,"target":"0809045990"}}} +{"an":{"0521483476":{"label":"Labyrinths of R...","full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge"}}} +{"ae":{"0385242719_0521483476":{"source":"0385242719","directed":false,"target":"0521483476"}}} +{"cn":{"0970055269":{"label":"Heard on the St...","full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews"}}} +{"an":{"0131499084":{"label":"Heard on the St...","full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews"}}} +{"ae":{"0970055269_0131499084":{"source":"0970055269","directed":false,"target":"0131499084"}}} +{"an":{"1581311729":{"label":"Heard on the St...","full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews"}}} +{"ae":{"0970055269_1581311729":{"source":"0970055269","directed":false,"target":"1581311729"}}} +{"an":{"0387401016":{"label":"Heard on the St...","full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews"}}} +{"ae":{"0970055269_0387401016":{"source":"0970055269","directed":false,"target":"0387401016"}}} +{"an":{"0470192739":{"label":"Heard on the St...","full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews"}}} +{"ae":{"0970055269_0470192739":{"source":"0970055269","directed":false,"target":"0470192739"}}} +{"an":{"1432706810":{"label":"Heard on the St...","full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews"}}} +{"ae":{"0970055269_1432706810":{"source":"0970055269","directed":false,"target":"1432706810"}}} +{"cn":{"0071492747":{"label":"ACE the IT Resu...","full-label":"ACE the IT Resume (Ace the It Resume)"}}} +{"ae":{"047012167X_0071492747":{"source":"047012167X","directed":false,"target":"0071492747"}}} +{"an":{"1593573111":{"label":"ACE the IT Resu...","full-label":"ACE the IT Resume (Ace the It Resume)"}}} +{"ae":{"0071492747_1593573111":{"source":"0071492747","directed":false,"target":"1593573111"}}} +{"an":{"1593571275":{"label":"ACE the IT Resu...","full-label":"ACE the IT Resume (Ace the It Resume)"}}} +{"ae":{"0071492747_1593571275":{"source":"0071492747","directed":false,"target":"1593571275"}}} +{"an":{"1587131560":{"label":"ACE the IT Resu...","full-label":"ACE the IT Resume (Ace the It Resume)"}}} +{"ae":{"0071492747_1587131560":{"source":"0071492747","directed":false,"target":"1587131560"}}} +{"cn":{"0071425470":{"label":"Boost Your Inte...","full-label":"Boost Your Interview IQ"}}} +{"an":{"0071449825":{"label":"Boost Your Inte...","full-label":"Boost Your Interview IQ"}}} +{"ae":{"0071425470_0071449825":{"source":"0071425470","directed":false,"target":"0071449825"}}} +{"an":{"0970901224":{"label":"Boost Your Inte...","full-label":"Boost Your Interview IQ"}}} +{"ae":{"0071425470_0970901224":{"source":"0071425470","directed":false,"target":"0970901224"}}} +{"ae":{"1402203853_0071425470":{"source":"1402203853","directed":false,"target":"0071425470"}}} +{"an":{"1564148696":{"label":"Boost Your Inte...","full-label":"Boost Your Interview IQ"}}} +{"ae":{"0071425470_1564148696":{"source":"0071425470","directed":false,"target":"1564148696"}}} +{"cn":{"0071387730":{"label":"201 Best Questi...","full-label":"201 Best Questions To Ask On Your Interview"}}} +{"an":{"007016357X":{"label":"201 Best Questi...","full-label":"201 Best Questions To Ask On Your Interview"}}} +{"ae":{"0071387730_007016357X":{"source":"0071387730","directed":false,"target":"007016357X"}}} +{"ae":{"1402203853_0071387730":{"source":"1402203853","directed":false,"target":"0071387730"}}} +{"ae":{"1593573111_0071387730":{"source":"1593573111","directed":false,"target":"0071387730"}}} +{"an":{"007141827X":{"label":"201 Best Questi...","full-label":"201 Best Questions To Ask On Your Interview"}}} +{"ae":{"0071387730_007141827X":{"source":"0071387730","directed":false,"target":"007141827X"}}} +{"an":{"1418040002":{"label":"201 Best Questi...","full-label":"201 Best Questions To Ask On Your Interview"}}} +{"ae":{"0071387730_1418040002":{"source":"0071387730","directed":false,"target":"1418040002"}}} +{"cn":{"1402203853":{"label":"301 Smart Answe...","full-label":"301 Smart Answers to Tough Interview Questions"}}} +{"ae":{"007016357X_1402203853":{"source":"007016357X","directed":false,"target":"1402203853"}}} +{"an":{"1564147789":{"label":"301 Smart Answe...","full-label":"301 Smart Answers to Tough Interview Questions"}}} +{"ae":{"1402203853_1564147789":{"source":"1402203853","directed":false,"target":"1564147789"}}} +{"ae":{"1564148696_1402203853":{"source":"1564148696","directed":false,"target":"1402203853"}}} +{"ae":{"007141827X_1402203853":{"source":"007141827X","directed":false,"target":"1402203853"}}} +{"cn":{"1572316217":{"label":"Software Projec...","full-label":"Software Project Survival Guide (Pro -- Best Practices)"}}} +{"ae":{"0735605351_1572316217":{"source":"0735605351","directed":false,"target":"1572316217"}}} +{"ae":{"0735619670_1572316217":{"source":"0735619670","directed":false,"target":"1572316217"}}} +{"ae":{"0201835959_1572316217":{"source":"0201835959","directed":false,"target":"1572316217"}}} +{"ae":{"0735618798_1572316217":{"source":"0735618798","directed":false,"target":"1572316217"}}} +{"cn":{"0735618798":{"label":"Software Requir...","full-label":"Software Requirements, Second Edition (Pro-Best Practices)"}}} +{"an":{"0735622671":{"label":"Software Requir...","full-label":"Software Requirements, Second Edition (Pro-Best Practices)"}}} +{"ae":{"0735618798_0735622671":{"source":"0735618798","directed":false,"target":"0735622671"}}} +{"ae":{"0201702258_0735618798":{"source":"0201702258","directed":false,"target":"0735618798"}}} +{"an":{"1576810607":{"label":"Software Requir...","full-label":"Software Requirements, Second Edition (Pro-Best Practices)"}}} +{"ae":{"0735618798_1576810607":{"source":"0735618798","directed":false,"target":"1576810607"}}} +{"ae":{"0735619670_0735618798":{"source":"0735619670","directed":false,"target":"0735618798"}}} +{"cn":{"0131479415":{"label":"Agile Estimatin...","full-label":"Agile Estimating and Planning (Robert C. Martin Series)"}}} +{"ae":{"0321205685_0131479415":{"source":"0321205685","directed":false,"target":"0131479415"}}} +{"an":{"073561993X":{"label":"Agile Estimatin...","full-label":"Agile Estimating and Planning (Robert C. Martin Series)"}}} +{"ae":{"0131479415_073561993X":{"source":"0131479415","directed":false,"target":"073561993X"}}} +{"an":{"0130676349":{"label":"Agile Estimatin...","full-label":"Agile Estimating and Planning (Robert C. Martin Series)"}}} +{"ae":{"0131479415_0130676349":{"source":"0131479415","directed":false,"target":"0130676349"}}} +{"an":{"0131111558":{"label":"Agile Estimatin...","full-label":"Agile Estimating and Planning (Robert C. Martin Series)"}}} +{"ae":{"0131479415_0131111558":{"source":"0131479415","directed":false,"target":"0131111558"}}} +{"an":{"0977616649":{"label":"Agile Estimatin...","full-label":"Agile Estimating and Planning (Robert C. Martin Series)"}}} +{"ae":{"0131479415_0977616649":{"source":"0131479415","directed":false,"target":"0977616649"}}} +{"cn":{"0132017997":{"label":"Design of the U...","full-label":"Design of the UNIX Operating System (Prentice Hall Software Series)"}}} +{"ae":{"0131103628_0132017997":{"source":"0131103628","directed":false,"target":"0132017997"}}} +{"an":{"0131019082":{"label":"Design of the U...","full-label":"Design of the UNIX Operating System (Prentice Hall Software Series)"}}} +{"ae":{"0132017997_0131019082":{"source":"0132017997","directed":false,"target":"0131019082"}}} +{"an":{"0131429388":{"label":"Design of the U...","full-label":"Design of the UNIX Operating System (Prentice Hall Software Series)"}}} +{"ae":{"0132017997_0131429388":{"source":"0132017997","directed":false,"target":"0131429388"}}} +{"ae":{"0201433079_0132017997":{"source":"0201433079","directed":false,"target":"0132017997"}}} +{"cn":{"020103669X":{"label":"Software Tools","full-label":"Software Tools"}}} +{"ae":{"020161586X_020103669X":{"source":"020161586X","directed":false,"target":"020103669X"}}} +{"ae":{"0131103628_020103669X":{"source":"0131103628","directed":false,"target":"020103669X"}}} +{"an":{"020107981X":{"label":"Software Tools","full-label":"Software Tools"}}} +{"ae":{"020103669X_020107981X":{"source":"020103669X","directed":false,"target":"020107981X"}}} +{"ae":{"0132017997_020103669X":{"source":"0132017997","directed":false,"target":"020103669X"}}} +{"cn":{"1590593898":{"label":"Joel on Softwar...","full-label":"Joel on Software: And on Diverse and Occasionally Related Matters That Will Prove of Interest to Software Developers, Designers, and Managers, and to Those Who, Whether by Good Fortune or Ill Luck, Work with Them in Some Capacity"}}} +{"an":{"1590595009":{"label":"Joel on Softwar...","full-label":"Joel on Software: And on Diverse and Occasionally Related Matters That Will Prove of Interest to Software Developers, Designers, and Managers, and to Those Who, Whether by Good Fortune or Ill Luck, Work with Them in Some Capacity"}}} +{"ae":{"1590593898_1590595009":{"source":"1590593898","directed":false,"target":"1590595009"}}} +{"ae":{"0201835959_1590593898":{"source":"0201835959","directed":false,"target":"1590593898"}}} +{"an":{"1893115941":{"label":"Joel on Softwar...","full-label":"Joel on Software: And on Diverse and Occasionally Related Matters That Will Prove of Interest to Software Developers, Designers, and Managers, and to Those Who, Whether by Good Fortune or Ill Luck, Work with Them in Some Capacity"}}} +{"ae":{"1590593898_1893115941":{"source":"1590593898","directed":false,"target":"1893115941"}}} +{"cn":{"0387402721":{"label":"All of Statisti...","full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)"}}} +{"an":{"0387251456":{"label":"All of Statisti...","full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)"}}} +{"ae":{"0387402721_0387251456":{"source":"0387402721","directed":false,"target":"0387251456"}}} +{"ae":{"0387310738_0387402721":{"source":"0387310738","directed":false,"target":"0387402721"}}} +{"an":{"0534243126":{"label":"All of Statisti...","full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)"}}} +{"ae":{"0387402721_0534243126":{"source":"0387402721","directed":false,"target":"0534243126"}}} +{"an":{"052168689X":{"label":"All of Statisti...","full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)"}}} +{"ae":{"0387402721_052168689X":{"source":"0387402721","directed":false,"target":"052168689X"}}} +{"cn":{"158488388X":{"label":"Bayesian Data A...","full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)"}}} +{"ae":{"052168689X_158488388X":{"source":"052168689X","directed":false,"target":"158488388X"}}} +{"an":{"0387212396":{"label":"Bayesian Data A...","full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)"}}} +{"ae":{"158488388X_0387212396":{"source":"158488388X","directed":false,"target":"0387212396"}}} +{"an":{"0387713840":{"label":"Bayesian Data A...","full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)"}}} +{"ae":{"158488388X_0387713840":{"source":"158488388X","directed":false,"target":"0387713840"}}} +{"an":{"0340814055":{"label":"Bayesian Data A...","full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)"}}} +{"ae":{"158488388X_0340814055":{"source":"158488388X","directed":false,"target":"0340814055"}}} +{"cn":{"0471429775":{"label":"Computer Manual...","full-label":"Computer Manual in MATLAB to Accompany Pattern Classification, Second Edition"}}} +{"ae":{"0387310738_0471429775":{"source":"0387310738","directed":false,"target":"0471429775"}}} +{"ae":{"0387952845_0471429775":{"source":"0387952845","directed":false,"target":"0471429775"}}} +{"an":{"0130085197":{"label":"Computer Manual...","full-label":"Computer Manual in MATLAB to Accompany Pattern Classification, Second Edition"}}} +{"ae":{"0471429775_0130085197":{"source":"0471429775","directed":false,"target":"0130085197"}}} +{"an":{"0198538642":{"label":"Computer Manual...","full-label":"Computer Manual in MATLAB to Accompany Pattern Classification, Second Edition"}}} +{"ae":{"0471429775_0198538642":{"source":"0471429775","directed":false,"target":"0198538642"}}} +{"cn":{"1558609016":{"label":"Data Mining: Co...","full-label":"Data Mining: Concepts and Techniques, Second Edition (The Morgan Kaufmann Series in Data Management Systems)"}}} +{"an":{"0321321367":{"label":"Data Mining: Co...","full-label":"Data Mining: Concepts and Techniques, Second Edition (The Morgan Kaufmann Series in Data Management Systems)"}}} +{"ae":{"1558609016_0321321367":{"source":"1558609016","directed":false,"target":"0321321367"}}} +{"an":{"0321356985":{"label":"Data Mining: Co...","full-label":"Data Mining: Concepts and Techniques, Second Edition (The Morgan Kaufmann Series in Data Management Systems)"}}} +{"ae":{"1558609016_0321356985":{"source":"1558609016","directed":false,"target":"0321356985"}}} +{"ae":{"0387952845_1558609016":{"source":"0387952845","directed":false,"target":"1558609016"}}} +{"ae":{"0596529325_1558609016":{"source":"0596529325","directed":false,"target":"1558609016"}}} +{"cn":{"0262012111":{"label":"Introduction to...","full-label":"Introduction to Machine Learning (Adaptive Computation and Machine Learning)"}}} +{"ae":{"0387310738_0262012111":{"source":"0387310738","directed":false,"target":"0262012111"}}} +{"ae":{"0071154671_0262012111":{"source":"0071154671","directed":false,"target":"0262012111"}}} +{"ae":{"0387952845_0262012111":{"source":"0387952845","directed":false,"target":"0262012111"}}} +{"ae":{"0471056693_0262012111":{"source":"0471056693","directed":false,"target":"0262012111"}}} +{"cn":{"0262194759":{"label":"Learning with K...","full-label":"Learning with Kernels: Support Vector Machines, Regularization, Optimization, and Beyond (Adaptive Computation and Machine Learning)"}}} +{"ae":{"0387310738_0262194759":{"source":"0387310738","directed":false,"target":"0262194759"}}} +{"ae":{"0387952845_0262194759":{"source":"0387952845","directed":false,"target":"0262194759"}}} +{"an":{"0521780195":{"label":"Learning with K...","full-label":"Learning with Kernels: Support Vector Machines, Regularization, Optimization, and Beyond (Adaptive Computation and Machine Learning)"}}} +{"ae":{"0262194759_0521780195":{"source":"0262194759","directed":false,"target":"0521780195"}}} +{"an":{"0521813972":{"label":"Learning with K...","full-label":"Learning with Kernels: Support Vector Machines, Regularization, Optimization, and Beyond (Adaptive Computation and Machine Learning)"}}} +{"ae":{"0262194759_0521813972":{"source":"0262194759","directed":false,"target":"0521813972"}}} +{"cn":{"0262033585":{"label":"Semi-Supervised...","full-label":"Semi-Supervised Learning (Adaptive Computation and Machine Learning)"}}} +{"ae":{"0387310738_0262033585":{"source":"0387310738","directed":false,"target":"0262033585"}}} +{"ae":{"0262194759_0262033585":{"source":"0262194759","directed":false,"target":"0262033585"}}} +{"an":{"0262026171":{"label":"Semi-Supervised...","full-label":"Semi-Supervised Learning (Adaptive Computation and Machine Learning)"}}} +{"ae":{"0262033585_0262026171":{"source":"0262033585","directed":false,"target":"0262026171"}}} +{"an":{"0262026252":{"label":"Semi-Supervised...","full-label":"Semi-Supervised Learning (Adaptive Computation and Machine Learning)"}}} +{"ae":{"0262033585_0262026252":{"source":"0262033585","directed":false,"target":"0262026252"}}} +{"cn":{"0521642981":{"label":"Information The...","full-label":"Information Theory, Inference & Learning Algorithms"}}} +{"ae":{"0387310738_0521642981":{"source":"0387310738","directed":false,"target":"0521642981"}}} +{"an":{"0521592712":{"label":"Information The...","full-label":"Information Theory, Inference & Learning Algorithms"}}} +{"ae":{"0521642981_0521592712":{"source":"0521642981","directed":false,"target":"0521592712"}}} +{"an":{"0471241954":{"label":"Information The...","full-label":"Information Theory, Inference & Learning Algorithms"}}} +{"ae":{"0521642981_0471241954":{"source":"0521642981","directed":false,"target":"0471241954"}}} +{"ae":{"0387952845_0521642981":{"source":"0387952845","directed":false,"target":"0521642981"}}} +{"cn":{"0486247759":{"label":"Introductory Gr...","full-label":"Introductory Graph Theory"}}} +{"ae":{"0486678709_0486247759":{"source":"0486678709","directed":false,"target":"0486247759"}}} +{"an":{"0387984887":{"label":"Introductory Gr...","full-label":"Introductory Graph Theory"}}} +{"ae":{"0486247759_0387984887":{"source":"0486247759","directed":false,"target":"0387984887"}}} +{"an":{"0486432327":{"label":"Introductory Gr...","full-label":"Introductory Graph Theory"}}} +{"ae":{"0486247759_0486432327":{"source":"0486247759","directed":false,"target":"0486432327"}}} +{"an":{"0070054894":{"label":"Introductory Gr...","full-label":"Introductory Graph Theory"}}} +{"ae":{"0486247759_0070054894":{"source":"0486247759","directed":false,"target":"0070054894"}}} +{"cn":{"0486678709":{"label":"Introduction to...","full-label":"Introduction to Graph Theory (Dover Books on Advanced Mathematics)"}}} +{"an":{"0199218420":{"label":"Introduction to...","full-label":"Introduction to Graph Theory (Dover Books on Advanced Mathematics)"}}} +{"ae":{"0486678709_0199218420":{"source":"0486678709","directed":false,"target":"0199218420"}}} +{"ae":{"0070054894_0486678709":{"source":"0070054894","directed":false,"target":"0486678709"}}} +{"ae":{"0486432327_0486678709":{"source":"0486432327","directed":false,"target":"0486678709"}}} +{"cn":{"0521474655":{"label":"Randomized Algo...","full-label":"Randomized Algorithms"}}} +{"ae":{"0521835402_0521474655":{"source":"0521835402","directed":false,"target":"0521474655"}}} +{"ae":{"0486402584_0521474655":{"source":"0486402584","directed":false,"target":"0521474655"}}} +{"ae":{"0716710455_0521474655":{"source":"0716710455","directed":false,"target":"0521474655"}}} +{"ae":{"0521872820_0521474655":{"source":"0521872820","directed":false,"target":"0521474655"}}} +{"cn":{"0521872820":{"label":"Algorithmic Gam...","full-label":"Algorithmic Game Theory"}}} +{"an":{"0674341163":{"label":"Algorithmic Gam...","full-label":"Algorithmic Game Theory"}}} +{"ae":{"0521872820_0674341163":{"source":"0521872820","directed":false,"target":"0674341163"}}} +{"an":{"0262033429":{"label":"Algorithmic Gam...","full-label":"Algorithmic Game Theory"}}} +{"ae":{"0521872820_0262033429":{"source":"0521872820","directed":false,"target":"0262033429"}}} +{"ae":{"0486402584_0521872820":{"source":"0486402584","directed":false,"target":"0521872820"}}} +{"an":{"0262650401":{"label":"Algorithmic Gam...","full-label":"Algorithmic Game Theory"}}} +{"ae":{"0521872820_0262650401":{"source":"0521872820","directed":false,"target":"0262650401"}}} +{"cn":{"0521835402":{"label":"Probability and...","full-label":"Probability and Computing: Randomized Algorithms and Probabilistic Analysis"}}} +{"ae":{"0521872820_0521835402":{"source":"0521872820","directed":false,"target":"0521835402"}}} +{"ae":{"0262032937_0521835402":{"source":"0262032937","directed":false,"target":"0521835402"}}} +{"ae":{"0486402584_0521835402":{"source":"0486402584","directed":false,"target":"0521835402"}}} +{"cn":{"0534939724":{"label":"Compiler Constr...","full-label":"Compiler Construction: Principles and Practice"}}} +{"ae":{"0321486811_0534939724":{"source":"0321486811","directed":false,"target":"0534939724"}}} +{"ae":{"0126339511_0534939724":{"source":"0126339511","directed":false,"target":"0534939724"}}} +{"ae":{"0534950973_0534939724":{"source":"0534950973","directed":false,"target":"0534939724"}}} +{"ae":{"013089592X_0534939724":{"source":"013089592X","directed":false,"target":"0534939724"}}} +{"cn":{"B00007FYIJ":{"label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)"}}} +{"an":{"0596005954":{"label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)"}}} +{"ae":{"B00007FYIJ_0596005954":{"source":"B00007FYIJ","directed":false,"target":"0596005954"}}} +{"an":{"0596528124":{"label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)"}}} +{"ae":{"B00007FYIJ_0596528124":{"source":"B00007FYIJ","directed":false,"target":"0596528124"}}} +{"an":{"0596009658":{"label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)"}}} +{"ae":{"B00007FYIJ_0596009658":{"source":"B00007FYIJ","directed":false,"target":"0596009658"}}} +{"an":{"B00007FYGT":{"label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)"}}} +{"ae":{"B00007FYIJ_B00007FYGT":{"source":"B00007FYIJ","directed":false,"target":"B00007FYGT"}}} +{"an":{"0596003528":{"label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)"}}} +{"ae":{"B00007FYIJ_0596003528":{"source":"B00007FYIJ","directed":false,"target":"0596003528"}}} +{"cn":{"155860698X":{"label":"Engineering a C...","full-label":"Engineering a Compiler"}}} +{"ae":{"0321486811_155860698X":{"source":"0321486811","directed":false,"target":"155860698X"}}} +{"an":{"1558603204":{"label":"Engineering a C...","full-label":"Engineering a Compiler"}}} +{"ae":{"155860698X_1558603204":{"source":"155860698X","directed":false,"target":"1558603204"}}} +{"an":{"1558602860":{"label":"Engineering a C...","full-label":"Engineering a Compiler"}}} +{"ae":{"155860698X_1558602860":{"source":"155860698X","directed":false,"target":"1558602860"}}} +{"ae":{"B00007FYCY_155860698X":{"source":"B00007FYCY","directed":false,"target":"155860698X"}}} +{"cn":{"0072253517":{"label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)"}}} +{"an":{"0072231459":{"label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)"}}} +{"ae":{"0072253517_0072231459":{"source":"0072253517","directed":false,"target":"0072231459"}}} +{"an":{"0072230665":{"label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)"}}} +{"ae":{"0072253517_0072230665":{"source":"0072253517","directed":false,"target":"0072230665"}}} +{"an":{"0072263172":{"label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)"}}} +{"ae":{"0072253517_0072263172":{"source":"0072253517","directed":false,"target":"0072263172"}}} +{"an":{"0072263059":{"label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)"}}} +{"ae":{"0072253517_0072263059":{"source":"0072253517","directed":false,"target":"0072263059"}}} +{"an":{"0072230789":{"label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)"}}} +{"ae":{"0072253517_0072230789":{"source":"0072253517","directed":false,"target":"0072230789"}}} +{"cn":{"1593270038":{"label":"Write Great Cod...","full-label":"Write Great Code: Volume 1: Understanding the Machine"}}} +{"ae":{"1593270658_1593270038":{"source":"1593270658","directed":false,"target":"1593270038"}}} +{"an":{"1886411972":{"label":"Write Great Cod...","full-label":"Write Great Code: Volume 1: Understanding the Machine"}}} +{"ae":{"1593270038_1886411972":{"source":"1593270038","directed":false,"target":"1886411972"}}} +{"an":{"0521520436":{"label":"Write Great Cod...","full-label":"Write Great Code: Volume 1: Understanding the Machine"}}} +{"ae":{"1593270038_0521520436":{"source":"1593270038","directed":false,"target":"0521520436"}}} +{"ae":{"0735619670_1593270038":{"source":"0735619670","directed":false,"target":"1593270038"}}} +{"cn":{"1593270658":{"label":"Write Great Cod...","full-label":"Write Great Code, Volume 2: Thinking Low-Level, Writing High-Level"}}} +{"ae":{"1886411972_1593270658":{"source":"1886411972","directed":false,"target":"1593270658"}}} +{"ae":{"0521520436_1593270658":{"source":"0521520436","directed":false,"target":"1593270658"}}} +{"an":{"1593270569":{"label":"Write Great Cod...","full-label":"Write Great Code, Volume 2: Thinking Low-Level, Writing High-Level"}}} +{"ae":{"1593270658_1593270569":{"source":"1593270658","directed":false,"target":"1593270569"}}} +{"cn":{"0672326663":{"label":"Programming in ...","full-label":"Programming in C (3rd Edition) (Developer's Library)"}}} +{"ae":{"0131103628_0672326663":{"source":"0131103628","directed":false,"target":"0672326663"}}} +{"ae":{"0672326965_0672326663":{"source":"0672326965","directed":false,"target":"0672326663"}}} +{"an":{"0672325861":{"label":"Programming in ...","full-label":"Programming in C (3rd Edition) (Developer's Library)"}}} +{"ae":{"0672326663_0672325861":{"source":"0672326663","directed":false,"target":"0672325861"}}} +{"cn":{"0201179288":{"label":"C Traps and Pit...","full-label":"C Traps and Pitfalls"}}} +{"ae":{"0131774298_0201179288":{"source":"0131774298","directed":false,"target":"0201179288"}}} +{"an":{"0201604612":{"label":"C Traps and Pit...","full-label":"C Traps and Pitfalls"}}} +{"ae":{"0201179288_0201604612":{"source":"0201179288","directed":false,"target":"0201604612"}}} +{"an":{"0673999866":{"label":"C Traps and Pit...","full-label":"C Traps and Pitfalls"}}} +{"ae":{"0201179288_0673999866":{"source":"0201179288","directed":false,"target":"0673999866"}}} +{"ae":{"0131103628_0201179288":{"source":"0131103628","directed":false,"target":"0201179288"}}} +{"cn":{"0131774298":{"label":"Expert C Progra...","full-label":"Expert C Programming"}}} +{"ae":{"0131103628_0131774298":{"source":"0131103628","directed":false,"target":"0131774298"}}} +{"ae":{"0201604612_0131774298":{"source":"0201604612","directed":false,"target":"0131774298"}}} +{"ae":{"0673999866_0131774298":{"source":"0673999866","directed":false,"target":"0131774298"}}} +{"cn":{"0764570684":{"label":"C For Dummies, ...","full-label":"C For Dummies, 2nd Edition"}}} +{"ae":{"0764570692_0764570684":{"source":"0764570692","directed":false,"target":"0764570684"}}} +{"an":{"0764568523":{"label":"C For Dummies, ...","full-label":"C For Dummies, 2nd Edition"}}} +{"ae":{"0764570684_0764568523":{"source":"0764570684","directed":false,"target":"0764568523"}}} +{"ae":{"0131103628_0764570684":{"source":"0131103628","directed":false,"target":"0764570684"}}} +{"an":{"0470088702":{"label":"C For Dummies, ...","full-label":"C For Dummies, 2nd Edition"}}} +{"ae":{"0764570684_0470088702":{"source":"0764570684","directed":false,"target":"0470088702"}}} +{"cn":{"0672326965":{"label":"C Primer Plus (...","full-label":"C Primer Plus (5th Edition)"}}} +{"an":{"0672326973":{"label":"C Primer Plus (...","full-label":"C Primer Plus (5th Edition)"}}} +{"ae":{"0672326965_0672326973":{"source":"0672326965","directed":false,"target":"0672326973"}}} +{"ae":{"0131103628_0672326965":{"source":"0131103628","directed":false,"target":"0672326965"}}} +{"ae":{"013089592X_0672326965":{"source":"013089592X","directed":false,"target":"0672326965"}}} +{"cn":{"0764570692":{"label":"C All-in-One De...","full-label":"C All-in-One Desk Reference for Dummies"}}} +{"an":{"0764517953":{"label":"C All-in-One De...","full-label":"C All-in-One Desk Reference for Dummies"}}} +{"ae":{"0764570692_0764517953":{"source":"0764570692","directed":false,"target":"0764517953"}}} +{"ae":{"0131103628_0764570692":{"source":"0131103628","directed":false,"target":"0764570692"}}} +{"an":{"0470124512":{"label":"C All-in-One De...","full-label":"C All-in-One Desk Reference for Dummies"}}} +{"ae":{"0764570692_0470124512":{"source":"0764570692","directed":false,"target":"0470124512"}}} +{"cn":{"0131411551":{"label":"Unix Network Pr...","full-label":"Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0130810819_0131411551":{"source":"0130810819","directed":false,"target":"0131411551"}}} +{"an":{"020163354X":{"label":"Unix Network Pr...","full-label":"Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0131411551_020163354X":{"source":"0131411551","directed":false,"target":"020163354X"}}} +{"an":{"0201633469":{"label":"Unix Network Pr...","full-label":"Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0131411551_0201633469":{"source":"0131411551","directed":false,"target":"0201633469"}}} +{"ae":{"0596005652_0131411551":{"source":"0596005652","directed":false,"target":"0131411551"}}} +{"cn":{"0130810819":{"label":"UNIX Network Pr...","full-label":"UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition) (The Unix Networking Reference Series , Vol 2)"}}} +{"ae":{"020163354X_0130810819":{"source":"020163354X","directed":false,"target":"0130810819"}}} +{"an":{"013490012X":{"label":"UNIX Network Pr...","full-label":"UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition) (The Unix Networking Reference Series , Vol 2)"}}} +{"ae":{"0130810819_013490012X":{"source":"0130810819","directed":false,"target":"013490012X"}}} +{"an":{"0201633922":{"label":"UNIX Network Pr...","full-label":"UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition) (The Unix Networking Reference Series , Vol 2)"}}} +{"ae":{"0130810819_0201633922":{"source":"0130810819","directed":false,"target":"0201633922"}}} +{"cn":{"0131411543":{"label":"Advanced UNIX P...","full-label":"Advanced UNIX Programming (2nd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0131411551_0131411543":{"source":"0131411551","directed":false,"target":"0131411543"}}} +{"ae":{"0130810819_0131411543":{"source":"0130810819","directed":false,"target":"0131411543"}}} +{"ae":{"0131103628_0131411543":{"source":"0131103628","directed":false,"target":"0131411543"}}} +{"an":{"0130424110":{"label":"Advanced UNIX P...","full-label":"Advanced UNIX Programming (2nd Edition) (Addison-Wesley Professional Computing Series)"}}} +{"ae":{"0131411543_0130424110":{"source":"0131411543","directed":false,"target":"0130424110"}}} +{"cn":{"0596005652":{"label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition"}}} +{"an":{"B00006AVQ0":{"label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition"}}} +{"ae":{"0596005652_B00006AVQ0":{"source":"0596005652","directed":false,"target":"B00006AVQ0"}}} +{"an":{"0596002556":{"label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition"}}} +{"ae":{"0596005652_0596002556":{"source":"0596005652","directed":false,"target":"0596002556"}}} +{"an":{"0672327201":{"label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition"}}} +{"ae":{"0596005652_0672327201":{"source":"0596005652","directed":false,"target":"0672327201"}}} +{"an":{"0131679848":{"label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition"}}} +{"ae":{"0596005652_0131679848":{"source":"0596005652","directed":false,"target":"0131679848"}}} +{"an":{"059600222X":{"label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition"}}} +{"ae":{"0596005652_059600222X":{"source":"0596005652","directed":false,"target":"059600222X"}}} +{"cn":{"0321278658":{"label":"Extreme Program...","full-label":"Extreme Programming Explained: Embrace Change (2nd Edition) (The XP Series)"}}} +{"ae":{"0130676349_0321278658":{"source":"0130676349","directed":false,"target":"0321278658"}}} +{"an":{"0201710919":{"label":"Extreme Program...","full-label":"Extreme Programming Explained: Embrace Change (2nd Edition) (The XP Series)"}}} +{"ae":{"0321278658_0201710919":{"source":"0321278658","directed":false,"target":"0201710919"}}} +{"ae":{"073561993X_0321278658":{"source":"073561993X","directed":false,"target":"0321278658"}}} +{"ae":{"0201485672_0321278658":{"source":"0201485672","directed":false,"target":"0321278658"}}} +{"cn":{"0321205685":{"label":"User Stories Ap...","full-label":"User Stories Applied: For Agile Software Development (The Addison-Wesley Signature Series)"}}} +{"ae":{"073561993X_0321205685":{"source":"073561993X","directed":false,"target":"0321205685"}}} +{"ae":{"0130676349_0321205685":{"source":"0130676349","directed":false,"target":"0321205685"}}} +{"ae":{"0977616649_0321205685":{"source":"0977616649","directed":false,"target":"0321205685"}}} +{"ae":{"0131111558_0321205685":{"source":"0131111558","directed":false,"target":"0321205685"}}} +{"cn":{"0131177052":{"label":"Working Effecti...","full-label":"Working Effectively with Legacy Code (Robert C. Martin Series)"}}} +{"ae":{"0201485672_0131177052":{"source":"0201485672","directed":false,"target":"0131177052"}}} +{"an":{"0131495054":{"label":"Working Effecti...","full-label":"Working Effectively with Legacy Code (Robert C. Martin Series)"}}} +{"ae":{"0131177052_0131495054":{"source":"0131177052","directed":false,"target":"0131495054"}}} +{"an":{"0321213351":{"label":"Working Effecti...","full-label":"Working Effectively with Legacy Code (Robert C. Martin Series)"}}} +{"ae":{"0131177052_0321213351":{"source":"0131177052","directed":false,"target":"0321213351"}}} +{"ae":{"020161622X_0131177052":{"source":"020161622X","directed":false,"target":"0131177052"}}} +{"cn":{"0321200683":{"label":"Enterprise Inte...","full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)"}}} +{"an":{"0131858580":{"label":"Enterprise Inte...","full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)"}}} +{"ae":{"0321200683_0131858580":{"source":"0321200683","directed":false,"target":"0131858580"}}} +{"an":{"0596006756":{"label":"Enterprise Inte...","full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)"}}} +{"ae":{"0321200683_0596006756":{"source":"0321200683","directed":false,"target":"0596006756"}}} +{"ae":{"0321125215_0321200683":{"source":"0321125215","directed":false,"target":"0321200683"}}} +{"an":{"0132344823":{"label":"Enterprise Inte...","full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)"}}} +{"ae":{"0321200683_0132344823":{"source":"0321200683","directed":false,"target":"0132344823"}}} +{"cn":{"0321125215":{"label":"Domain-Driven D...","full-label":"Domain-Driven Design: Tackling Complexity in the Heart of Software"}}} +{"an":{"0321268202":{"label":"Domain-Driven D...","full-label":"Domain-Driven Design: Tackling Complexity in the Heart of Software"}}} +{"ae":{"0321125215_0321268202":{"source":"0321125215","directed":false,"target":"0321268202"}}} +{"ae":{"0201485672_0321125215":{"source":"0201485672","directed":false,"target":"0321125215"}}} +{"ae":{"0321146530_0321125215":{"source":"0321146530","directed":false,"target":"0321125215"}}} +{"cn":{"0596527357":{"label":"Head First Soft...","full-label":"Head First Software Development (Brain-Friendly Guides)"}}} +{"an":{"0596526849":{"label":"Head First Soft...","full-label":"Head First Software Development (Brain-Friendly Guides)"}}} +{"ae":{"0596527357_0596526849":{"source":"0596527357","directed":false,"target":"0596526849"}}} +{"an":{"0596514824":{"label":"Head First Soft...","full-label":"Head First Software Development (Brain-Friendly Guides)"}}} +{"ae":{"0596527357_0596514824":{"source":"0596527357","directed":false,"target":"0596514824"}}} +{"ae":{"0596007124_0596527357":{"source":"0596007124","directed":false,"target":"0596527357"}}} +{"an":{"0596527748":{"label":"Head First Soft...","full-label":"Head First Software Development (Brain-Friendly Guides)"}}} +{"ae":{"0596527357_0596527748":{"source":"0596527357","directed":false,"target":"0596527748"}}} +{"cn":{"059610197X":{"label":"Head First HTML...","full-label":"Head First HTML with CSS & XHTML"}}} +{"ae":{"0596527748_059610197X":{"source":"0596527748","directed":false,"target":"059610197X"}}} +{"an":{"0596102259":{"label":"Head First HTML...","full-label":"Head First HTML with CSS & XHTML"}}} +{"ae":{"059610197X_0596102259":{"source":"059610197X","directed":false,"target":"0596102259"}}} +{"ae":{"0596526849_059610197X":{"source":"0596526849","directed":false,"target":"059610197X"}}} +{"ae":{"0596007124_059610197X":{"source":"0596007124","directed":false,"target":"059610197X"}}} +{"cn":{"0596005717":{"label":"Head First EJB ...","full-label":"Head First EJB (Brain-Friendly Study Guides; Enterprise JavaBeans)"}}} +{"ae":{"0596007124_0596005717":{"source":"0596007124","directed":false,"target":"0596005717"}}} +{"ae":{"0596008678_0596005717":{"source":"0596008678","directed":false,"target":"0596005717"}}} +{"ae":{"0072253606_0596005717":{"source":"0072253606","directed":false,"target":"0596005717"}}} +{"cn":{"0072253606":{"label":"SCJP Sun Certif...","full-label":"SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press Study Guides)"}}} +{"an":{"1590596978":{"label":"SCJP Sun Certif...","full-label":"SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press Study Guides)"}}} +{"ae":{"0072253606_1590596978":{"source":"0072253606","directed":false,"target":"1590596978"}}} +{"ae":{"0596009208_0072253606":{"source":"0596009208","directed":false,"target":"0072253606"}}} +{"ae":{"0596007124_0072253606":{"source":"0596007124","directed":false,"target":"0072253606"}}} +{"an":{"1933988134":{"label":"SCJP Sun Certif...","full-label":"SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press Study Guides)"}}} +{"ae":{"0072253606_1933988134":{"source":"0072253606","directed":false,"target":"1933988134"}}} +{"cn":{"0201734842":{"label":"C++ Templates: ...","full-label":"C++ Templates: The Complete Guide"}}} +{"an":{"0201704315":{"label":"C++ Templates: ...","full-label":"C++ Templates: The Complete Guide"}}} +{"ae":{"0201734842_0201704315":{"source":"0201734842","directed":false,"target":"0201704315"}}} +{"ae":{"0321334876_0201734842":{"source":"0321334876","directed":false,"target":"0201734842"}}} +{"ae":{"0201749629_0201734842":{"source":"0201749629","directed":false,"target":"0201734842"}}} +{"ae":{"0201700735_0201734842":{"source":"0201700735","directed":false,"target":"0201734842"}}} +{"cn":{"0201702258":{"label":"Writing Effecti...","full-label":"Writing Effective Use Cases (The Agile Software Development Series)"}}} +{"an":{"1592009123":{"label":"Writing Effecti...","full-label":"Writing Effective Use Cases (The Agile Software Development Series)"}}} +{"ae":{"0201702258_1592009123":{"source":"0201702258","directed":false,"target":"1592009123"}}} +{"ae":{"1576810607_0201702258":{"source":"1576810607","directed":false,"target":"0201702258"}}} +{"an":{"0321419499":{"label":"Writing Effecti...","full-label":"Writing Effective Use Cases (The Agile Software Development Series)"}}} +{"ae":{"0201702258_0321419499":{"source":"0201702258","directed":false,"target":"0321419499"}}} +{"ae":{"0735622671_0201702258":{"source":"0735622671","directed":false,"target":"0201702258"}}} +{"cn":{"0486252116":{"label":"Entertaining Ma...","full-label":"Entertaining Mathematical Puzzles"}}} +{"ae":{"0486256375_0486252116":{"source":"0486256375","directed":false,"target":"0486252116"}}} +{"ae":{"0486270785_0486252116":{"source":"0486270785","directed":false,"target":"0486252116"}}} +{"ae":{"0486427552_0486252116":{"source":"0486427552","directed":false,"target":"0486252116"}}} +{"ae":{"0970825315_0486252116":{"source":"0970825315","directed":false,"target":"0486252116"}}} +{"cn":{"0486270785":{"label":"The Moscow Puzz...","full-label":"The Moscow Puzzles: 359 Mathematical Recreations (Math & Logic Puzzles)"}}} +{"ae":{"0486256375_0486270785":{"source":"0486256375","directed":false,"target":"0486270785"}}} +{"an":{"0393061140":{"label":"The Moscow Puzz...","full-label":"The Moscow Puzzles: 359 Mathematical Recreations (Math & Logic Puzzles)"}}} +{"ae":{"0486270785_0393061140":{"source":"0486270785","directed":false,"target":"0393061140"}}} +{"ae":{"0486427552_0486270785":{"source":"0486427552","directed":false,"target":"0486270785"}}} +{"cn":{"0486256375":{"label":"Perplexing Puzz...","full-label":"Perplexing Puzzles and Tantalizing Teasers (Math & Logic Puzzles)"}}} +{"ae":{"0970825315_0486256375":{"source":"0970825315","directed":false,"target":"0486256375"}}} +{"ae":{"0486427552_0486256375":{"source":"0486427552","directed":false,"target":"0486256375"}}} +{"cn":{"0486427552":{"label":"Brain Busters! ...","full-label":"Brain Busters! Mind-Stretching Puzzles in Math and Logic"}}} +{"ae":{"0970825315_0486427552":{"source":"0970825315","directed":false,"target":"0486427552"}}} +{"an":{"0970825307":{"label":"Brain Busters! ...","full-label":"Brain Busters! Mind-Stretching Puzzles in Math and Logic"}}} +{"ae":{"0486427552_0970825307":{"source":"0486427552","directed":false,"target":"0970825307"}}} +{"cn":{"0970825315":{"label":"So You Think Yo...","full-label":"So You Think You're Smart: 150 Fun and Challenging Brain Teasers"}}} +{"ae":{"0970825307_0970825315":{"source":"0970825307","directed":false,"target":"0970825315"}}} +{"an":{"1885003994":{"label":"So You Think Yo...","full-label":"So You Think You're Smart: 150 Fun and Challenging Brain Teasers"}}} +{"ae":{"0970825315_1885003994":{"source":"0970825315","directed":false,"target":"1885003994"}}} +{"an":{"0140318755":{"label":"So You Think Yo...","full-label":"So You Think You're Smart: 150 Fun and Challenging Brain Teasers"}}} +{"ae":{"0970825315_0140318755":{"source":"0970825315","directed":false,"target":"0140318755"}}} +{"cn":{"0596527349":{"label":"Information Arc...","full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites"}}} +{"ae":{"0321344758_0596527349":{"source":"0321344758","directed":false,"target":"0596527349"}}} +{"ae":{"0596008031_0596527349":{"source":"0596008031","directed":false,"target":"0596527349"}}} +{"an":{"0321392353":{"label":"Information Arc...","full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites"}}} +{"ae":{"0596527349_0321392353":{"source":"0596527349","directed":false,"target":"0321392353"}}} +{"an":{"0596007655":{"label":"Information Arc...","full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites"}}} +{"ae":{"0596527349_0596007655":{"source":"0596527349","directed":false,"target":"0596007655"}}} +{"an":{"0735712026":{"label":"Information Arc...","full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites"}}} +{"ae":{"0596527349_0735712026":{"source":"0596527349","directed":false,"target":"0735712026"}}} +{"cn":{"0321344758":{"label":"Don't Make Me T...","full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition"}}} +{"ae":{"0596008031_0321344758":{"source":"0596008031","directed":false,"target":"0321344758"}}} +{"an":{"0321350316":{"label":"Don't Make Me T...","full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition"}}} +{"ae":{"0321344758_0321350316":{"source":"0321344758","directed":false,"target":"0321350316"}}} +{"an":{"0465067107":{"label":"Don't Make Me T...","full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition"}}} +{"ae":{"0321344758_0465067107":{"source":"0321344758","directed":false,"target":"0465067107"}}} +{"an":{"156205810X":{"label":"Don't Make Me T...","full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition"}}} +{"ae":{"0321344758_156205810X":{"source":"0321344758","directed":false,"target":"156205810X"}}} +{"cn":{"0596008031":{"label":"Designing Inter...","full-label":"Designing Interfaces: Patterns for Effective Interaction Design"}}} +{"ae":{"0596007655_0596008031":{"source":"0596007655","directed":false,"target":"0596008031"}}} +{"an":{"0262134748":{"label":"Designing Inter...","full-label":"Designing Interfaces: Patterns for Effective Interaction Design"}}} +{"ae":{"0596008031_0262134748":{"source":"0596008031","directed":false,"target":"0262134748"}}} +{"ae":{"0321392353_0596008031":{"source":"0321392353","directed":false,"target":"0596008031"}}} +{"cn":{"0974514055":{"label":"Programming Rub...","full-label":"Programming Ruby: The Pragmatic Programmers' Guide, Second Edition"}}} +{"ae":{"0596523696_0974514055":{"source":"0596523696","directed":false,"target":"0974514055"}}} +{"ae":{"0977616606_0974514055":{"source":"0977616606","directed":false,"target":"0974514055"}}} +{"an":{"0672328844":{"label":"Programming Rub...","full-label":"Programming Ruby: The Pragmatic Programmers' Guide, Second Edition"}}} +{"ae":{"0974514055_0672328844":{"source":"0974514055","directed":false,"target":"0672328844"}}} +{"ae":{"1932394699_0974514055":{"source":"1932394699","directed":false,"target":"0974514055"}}} +{"cn":{"0977616606":{"label":"Rails Recipes (...","full-label":"Rails Recipes (Pragmatic Programmers)"}}} +{"ae":{"1932394699_0977616606":{"source":"1932394699","directed":false,"target":"0977616606"}}} +{"ae":{"0596523696_0977616606":{"source":"0596523696","directed":false,"target":"0977616606"}}} +{"ae":{"0596527446_0977616606":{"source":"0596527446","directed":false,"target":"0977616606"}}} +{"cn":{"0596527446":{"label":"Ajax on Rails","full-label":"Ajax on Rails"}}} +{"an":{"0596527314":{"label":"Ajax on Rails","full-label":"Ajax on Rails"}}} +{"ae":{"0596527446_0596527314":{"source":"0596527446","directed":false,"target":"0596527314"}}} +{"ae":{"0596523696_0596527446":{"source":"0596523696","directed":false,"target":"0596527446"}}} +{"ae":{"0974514055_0596527446":{"source":"0974514055","directed":false,"target":"0596527446"}}} +{"cn":{"0596523696":{"label":"Ruby Cookbook (...","full-label":"Ruby Cookbook (Cookbooks (O'Reilly))"}}} +{"ae":{"0596527314_0596523696":{"source":"0596527314","directed":false,"target":"0596523696"}}} +{"ae":{"0672328844_0596523696":{"source":"0672328844","directed":false,"target":"0596523696"}}} +{"cn":{"1932394699":{"label":"Ruby for Rails:...","full-label":"Ruby for Rails: Ruby Techniques for Rails Developers"}}} +{"ae":{"0596523696_1932394699":{"source":"0596523696","directed":false,"target":"1932394699"}}} +{"ae":{"0596527446_1932394699":{"source":"0596527446","directed":false,"target":"1932394699"}}} +{"cn":{"059652708X":{"label":"MySQL Cookbook","full-label":"MySQL Cookbook"}}} +{"an":{"0596101015":{"label":"MySQL Cookbook","full-label":"MySQL Cookbook"}}} +{"ae":{"059652708X_0596101015":{"source":"059652708X","directed":false,"target":"0596101015"}}} +{"ae":{"0596100892_059652708X":{"source":"0596100892","directed":false,"target":"059652708X"}}} +{"an":{"0596008643":{"label":"MySQL Cookbook","full-label":"MySQL Cookbook"}}} +{"ae":{"059652708X_0596008643":{"source":"059652708X","directed":false,"target":"0596008643"}}} +{"an":{"0672327120":{"label":"MySQL Cookbook","full-label":"MySQL Cookbook"}}} +{"ae":{"059652708X_0672327120":{"source":"059652708X","directed":false,"target":"0672327120"}}} +{"cn":{"0596100892":{"label":"MySQL Stored Pr...","full-label":"MySQL Stored Procedure Programming"}}} +{"ae":{"159059505X_0596100892":{"source":"159059505X","directed":false,"target":"0596100892"}}} +{"an":{"0672328704":{"label":"MySQL Stored Pr...","full-label":"MySQL Stored Procedure Programming"}}} +{"ae":{"0596100892_0672328704":{"source":"0596100892","directed":false,"target":"0672328704"}}} +{"an":{"1590595351":{"label":"MySQL Stored Pr...","full-label":"MySQL Stored Procedure Programming"}}} +{"ae":{"0596100892_1590595351":{"source":"0596100892","directed":false,"target":"1590595351"}}} +{"cn":{"159059505X":{"label":"Pro MySQL (Expe...","full-label":"Pro MySQL (Expert's Voice in Open Source)"}}} +{"ae":{"059652708X_159059505X":{"source":"059652708X","directed":false,"target":"159059505X"}}} +{"ae":{"1590595351_159059505X":{"source":"1590595351","directed":false,"target":"159059505X"}}} +{"ae":{"0596102356_159059505X":{"source":"0596102356","directed":false,"target":"159059505X"}}} +{"cn":{"159200346X":{"label":"Physical Comput...","full-label":"Physical Computing: Sensing and Controlling the Physical World with Computers"}}} +{"an":{"0071452818":{"label":"Physical Comput...","full-label":"Physical Computing: Sensing and Controlling the Physical World with Computers"}}} +{"ae":{"159200346X_0071452818":{"source":"159200346X","directed":false,"target":"0071452818"}}} +{"an":{"0945053312":{"label":"Physical Comput...","full-label":"Physical Computing: Sensing and Controlling the Physical World with Computers"}}} +{"ae":{"159200346X_0945053312":{"source":"159200346X","directed":false,"target":"0945053312"}}} +{"cn":{"0500285179":{"label":"Creative Code: ...","full-label":"Creative Code: Aesthetics + Computation"}}} +{"an":{"0262134721":{"label":"Creative Code: ...","full-label":"Creative Code: Aesthetics + Computation"}}} +{"ae":{"0500285179_0262134721":{"source":"0500285179","directed":false,"target":"0262134721"}}} +{"ae":{"0262182629_0500285179":{"source":"0262182629","directed":false,"target":"0500285179"}}} +{"an":{"0262632446":{"label":"Creative Code: ...","full-label":"Creative Code: Aesthetics + Computation"}}} +{"ae":{"0500285179_0262632446":{"source":"0500285179","directed":false,"target":"0262632446"}}} +{"an":{"0789305259":{"label":"Creative Code: ...","full-label":"Creative Code: Aesthetics + Computation"}}} +{"ae":{"0500285179_0789305259":{"source":"0500285179","directed":false,"target":"0789305259"}}} +{"cn":{"059651428X":{"label":"The Best of MAK...","full-label":"The Best of MAKE (Make)"}}} +{"an":{"0596514921":{"label":"The Best of MAK...","full-label":"The Best of MAKE (Make)"}}} +{"ae":{"059651428X_0596514921":{"source":"059651428X","directed":false,"target":"0596514921"}}} +{"an":{"0596510543":{"label":"The Best of MAK...","full-label":"The Best of MAKE (Make)"}}} +{"ae":{"059651428X_0596510543":{"source":"059651428X","directed":false,"target":"0596510543"}}} +{"an":{"B0007RNI5K":{"label":"The Best of MAK...","full-label":"The Best of MAKE (Make)"}}} +{"ae":{"059651428X_B0007RNI5K":{"source":"059651428X","directed":false,"target":"B0007RNI5K"}}} +{"ae":{"159200346X_059651428X":{"source":"159200346X","directed":false,"target":"059651428X"}}} +{"cn":{"1591841992":{"label":"The Back of the...","full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures"}}} +{"an":{"0142005207":{"label":"The Back of the...","full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures"}}} +{"ae":{"1591841992_0142005207":{"source":"1591841992","directed":false,"target":"0142005207"}}} +{"ae":{"0979777704_1591841992":{"source":"0979777704","directed":false,"target":"1591841992"}}} +{"an":{"1594482918":{"label":"The Back of the...","full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures"}}} +{"ae":{"1591841992_1594482918":{"source":"1591841992","directed":false,"target":"1594482918"}}} +{"an":{"006135323X":{"label":"The Back of the...","full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures"}}} +{"ae":{"1591841992_006135323X":{"source":"1591841992","directed":false,"target":"006135323X"}}} +{"cn":{"0735623872":{"label":"Beyond Bullet P...","full-label":"Beyond Bullet Points: Using Microsoft® Office PowerPoint® 2007 to Create Presentations That Inform, Motivate, and Inspire"}}} +{"an":{"0131875108":{"label":"Beyond Bullet P...","full-label":"Beyond Bullet Points: Using Microsoft® Office PowerPoint® 2007 to Create Presentations That Inform, Motivate, and Inspire"}}} +{"ae":{"0735623872_0131875108":{"source":"0735623872","directed":false,"target":"0131875108"}}} +{"ae":{"1591841992_0735623872":{"source":"1591841992","directed":false,"target":"0735623872"}}} +{"ae":{"0195320697_0735623872":{"source":"0195320697","directed":false,"target":"0735623872"}}} +{"an":{"0615142230":{"label":"Beyond Bullet P...","full-label":"Beyond Bullet Points: Using Microsoft® Office PowerPoint® 2007 to Create Presentations That Inform, Motivate, and Inspire"}}} +{"ae":{"0735623872_0615142230":{"source":"0735623872","directed":false,"target":"0615142230"}}} +{"cn":{"0195320697":{"label":"Clear and to th...","full-label":"Clear and to the Point: 8 Psychological Principles for Compelling PowerPoint Presentations"}}} +{"ae":{"1591841992_0195320697":{"source":"1591841992","directed":false,"target":"0195320697"}}} +{"ae":{"0615142230_0195320697":{"source":"0615142230","directed":false,"target":"0195320697"}}} +{"ae":{"1400064287_0195320697":{"source":"1400064287","directed":false,"target":"0195320697"}}} +{"cn":{"0979777704":{"label":"Brain Rules: 12...","full-label":"Brain Rules: 12 Principles for Surviving and Thriving at Work, Home, and School (Book & DVD)"}}} +{"an":{"0316113506":{"label":"Brain Rules: 12...","full-label":"Brain Rules: 12 Principles for Surviving and Thriving at Work, Home, and School (Book & DVD)"}}} +{"ae":{"0979777704_0316113506":{"source":"0979777704","directed":false,"target":"0316113506"}}} +{"an":{"1596912839":{"label":"Brain Rules: 12...","full-label":"Brain Rules: 12 Principles for Surviving and Thriving at Work, Home, and School (Book & DVD)"}}} +{"ae":{"0979777704_1596912839":{"source":"0979777704","directed":false,"target":"1596912839"}}} +{"ae":{"1594482918_0979777704":{"source":"1594482918","directed":false,"target":"0979777704"}}} +{"cn":{"1400064287":{"label":"Made to Stick: ...","full-label":"Made to Stick: Why Some Ideas Survive and Others Die"}}} +{"ae":{"1591841992_1400064287":{"source":"1591841992","directed":false,"target":"1400064287"}}} +{"an":{"1591841666":{"label":"Made to Stick: ...","full-label":"Made to Stick: Why Some Ideas Survive and Others Die"}}} +{"ae":{"1400064287_1591841666":{"source":"1400064287","directed":false,"target":"1591841666"}}} +{"an":{"0307341518":{"label":"Made to Stick: ...","full-label":"Made to Stick: Why Some Ideas Survive and Others Die"}}} +{"ae":{"1400064287_0307341518":{"source":"1400064287","directed":false,"target":"0307341518"}}} +{"an":{"1591841437":{"label":"Made to Stick: ...","full-label":"Made to Stick: Why Some Ideas Survive and Others Die"}}} +{"ae":{"1400064287_1591841437":{"source":"1400064287","directed":false,"target":"1591841437"}}} +{"cn":{"1591840562":{"label":"The Art of the ...","full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything"}}} +{"an":{"088730995X":{"label":"The Art of the ...","full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything"}}} +{"ae":{"1591840562_088730995X":{"source":"1591840562","directed":false,"target":"088730995X"}}} +{"an":{"0060517123":{"label":"The Art of the ...","full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything"}}} +{"ae":{"1591840562_0060517123":{"source":"1591840562","directed":false,"target":"0060517123"}}} +{"ae":{"0321525655_1591840562":{"source":"0321525655","directed":false,"target":"1591840562"}}} +{"an":{"0971187304":{"label":"The Art of the ...","full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything"}}} +{"ae":{"1591840562_0971187304":{"source":"1591840562","directed":false,"target":"0971187304"}}} +{"cn":{"0321349601":{"label":"Java Concurrenc...","full-label":"Java Concurrency in Practice"}}} +{"an":{"0596527756":{"label":"Java Concurrenc...","full-label":"Java Concurrency in Practice"}}} +{"ae":{"0321349601_0596527756":{"source":"0321349601","directed":false,"target":"0596527756"}}} +{"an":{"0321356683":{"label":"Java Concurrenc...","full-label":"Java Concurrency in Practice"}}} +{"ae":{"0321349601_0321356683":{"source":"0321349601","directed":false,"target":"0321356683"}}} +{"an":{"032133678X":{"label":"Java Concurrenc...","full-label":"Java Concurrency in Practice"}}} +{"ae":{"0321349601_032133678X":{"source":"0321349601","directed":false,"target":"032133678X"}}} +{"an":{"1932394885":{"label":"Java Concurrenc...","full-label":"Java Concurrency in Practice"}}} +{"ae":{"0321349601_1932394885":{"source":"0321349601","directed":false,"target":"1932394885"}}} +{"an":{"0201310058":{"label":"Java Concurrenc...","full-label":"Java Concurrency in Practice"}}} +{"ae":{"0321349601_0201310058":{"source":"0321349601","directed":false,"target":"0201310058"}}} +{"cn":{"0521644089":{"label":"The Haskell Sch...","full-label":"The Haskell School of Expression: Learning Functional Programming through Multimedia"}}} +{"ae":{"0954300696_0521644089":{"source":"0954300696","directed":false,"target":"0521644089"}}} +{"ae":{"193435600X_0521644089":{"source":"193435600X","directed":false,"target":"0521644089"}}} +{"ae":{"0521663504_0521644089":{"source":"0521663504","directed":false,"target":"0521644089"}}} +{"an":{"0201342758":{"label":"The Haskell Sch...","full-label":"The Haskell School of Expression: Learning Functional Programming through Multimedia"}}} +{"ae":{"0521644089_0201342758":{"source":"0521644089","directed":false,"target":"0201342758"}}} +{"cn":{"0954300696":{"label":"The Haskell Roa...","full-label":"The Haskell Road to Logic, Maths and Programming (Texts in Computing S.)"}}} +{"an":{"0954300653":{"label":"The Haskell Roa...","full-label":"The Haskell Road to Logic, Maths and Programming (Texts in Computing S.)"}}} +{"ae":{"0954300696_0954300653":{"source":"0954300696","directed":false,"target":"0954300653"}}} +{"ae":{"193435600X_0954300696":{"source":"193435600X","directed":false,"target":"0954300696"}}} +{"ae":{"0521663504_0954300696":{"source":"0521663504","directed":false,"target":"0954300696"}}} +{"cn":{"0521663504":{"label":"Purely Function...","full-label":"Purely Functional Data Structures"}}} +{"ae":{"193435600X_0521663504":{"source":"193435600X","directed":false,"target":"0521663504"}}} +{"ae":{"0596510047_0521663504":{"source":"0596510047","directed":false,"target":"0521663504"}}} +{"cn":{"0971015848":{"label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition"}}} +{"an":{"1581313055":{"label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition"}}} +{"ae":{"0971015848_1581313055":{"source":"0971015848","directed":false,"target":"1581313055"}}} +{"an":{"0471757225":{"label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition"}}} +{"ae":{"0971015848_0471757225":{"source":"0971015848","directed":false,"target":"0471757225"}}} +{"an":{"0070534489":{"label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition"}}} +{"ae":{"0971015848_0070534489":{"source":"0971015848","directed":false,"target":"0070534489"}}} +{"an":{"0979003903":{"label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition"}}} +{"ae":{"0971015848_0979003903":{"source":"0971015848","directed":false,"target":"0979003903"}}} +{"an":{"0471444014":{"label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition"}}} +{"ae":{"0971015848_0471444014":{"source":"0971015848","directed":false,"target":"0471444014"}}} +{"cn":{"038541580X":{"label":"Prisoner's Dile...","full-label":"Prisoner's Dilemma"}}} +{"an":{"0486296725":{"label":"Prisoner's Dile...","full-label":"Prisoner's Dilemma"}}} +{"ae":{"038541580X_0486296725":{"source":"038541580X","directed":false,"target":"0486296725"}}} +{"ae":{"0809045990_038541580X":{"source":"0809045990","directed":false,"target":"038541580X"}}} +{"an":{"0486251012":{"label":"Prisoner's Dile...","full-label":"Prisoner's Dilemma"}}} +{"ae":{"038541580X_0486251012":{"source":"038541580X","directed":false,"target":"0486251012"}}} +{"an":{"0465005640":{"label":"Prisoner's Dile...","full-label":"Prisoner's Dilemma"}}} +{"ae":{"038541580X_0465005640":{"source":"038541580X","directed":false,"target":"0465005640"}}} +{"cn":{"0809045990":{"label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street"}}} +{"an":{"0812975219":{"label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street"}}} +{"ae":{"0809045990_0812975219":{"source":"0809045990","directed":false,"target":"0812975219"}}} +{"an":{"047004389X":{"label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street"}}} +{"ae":{"0809045990_047004389X":{"source":"0809045990","directed":false,"target":"047004389X"}}} +{"an":{"0231143729":{"label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street"}}} +{"ae":{"0809045990_0231143729":{"source":"0809045990","directed":false,"target":"0231143729"}}} +{"an":{"1400063515":{"label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street"}}} +{"ae":{"0809045990_1400063515":{"source":"0809045990","directed":false,"target":"1400063515"}}} +{"an":{"0471794473":{"label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street"}}} +{"ae":{"0809045990_0471794473":{"source":"0809045990","directed":false,"target":"0471794473"}}} +{"cn":{"0521483476":{"label":"Paradoxes","full-label":"Paradoxes"}}} +{"an":{"0691121273":{"label":"Paradoxes","full-label":"Paradoxes"}}} +{"ae":{"0521483476_0691121273":{"source":"0521483476","directed":false,"target":"0691121273"}}} +{"an":{"0415228093":{"label":"Paradoxes","full-label":"Paradoxes"}}} +{"ae":{"0521483476_0415228093":{"source":"0521483476","directed":false,"target":"0415228093"}}} +{"an":{"0195179862":{"label":"Paradoxes","full-label":"Paradoxes"}}} +{"ae":{"0521483476_0195179862":{"source":"0521483476","directed":false,"target":"0195179862"}}} +{"an":{"0486296644":{"label":"Paradoxes","full-label":"Paradoxes"}}} +{"ae":{"0521483476_0486296644":{"source":"0521483476","directed":false,"target":"0486296644"}}} +{"cn":{"0131499084":{"label":"Options, Future...","full-label":"Options, Futures and Other Derivatives (6th Edition)"}}} +{"an":{"0131499068":{"label":"Options, Future...","full-label":"Options, Futures and Other Derivatives (6th Edition)"}}} +{"ae":{"0131499084_0131499068":{"source":"0131499084","directed":false,"target":"0131499068"}}} +{"ae":{"0387401016_0131499084":{"source":"0387401016","directed":false,"target":"0131499084"}}} +{"an":{"0387249680":{"label":"Options, Future...","full-label":"Options, Futures and Other Derivatives (6th Edition)"}}} +{"ae":{"0131499084_0387249680":{"source":"0131499084","directed":false,"target":"0387249680"}}} +{"an":{"155738486X":{"label":"Options, Future...","full-label":"Options, Futures and Other Derivatives (6th Edition)"}}} +{"ae":{"0131499084_155738486X":{"source":"0131499084","directed":false,"target":"155738486X"}}} +{"cn":{"1581311729":{"label":"Vault Guide to ...","full-label":"Vault Guide to Advanced Finance & Quantitative Interviews"}}} +{"an":{"1581313047":{"label":"Vault Guide to ...","full-label":"Vault Guide to Advanced Finance & Quantitative Interviews"}}} +{"ae":{"1581311729_1581313047":{"source":"1581311729","directed":false,"target":"1581313047"}}} +{"an":{"1581311702":{"label":"Vault Guide to ...","full-label":"Vault Guide to Advanced Finance & Quantitative Interviews"}}} +{"ae":{"1581311729_1581311702":{"source":"1581311729","directed":false,"target":"1581311702"}}} +{"an":{"1581315295":{"label":"Vault Guide to ...","full-label":"Vault Guide to Advanced Finance & Quantitative Interviews"}}} +{"ae":{"1581311729_1581315295":{"source":"1581311729","directed":false,"target":"1581315295"}}} +{"ae":{"0131499084_1581311729":{"source":"0131499084","directed":false,"target":"1581311729"}}} +{"cn":{"0387401016":{"label":"Stochastic Calc...","full-label":"Stochastic Calculus for Finance II: Continuous-Time Models (Springer Finance)"}}} +{"ae":{"0387249680_0387401016":{"source":"0387249680","directed":false,"target":"0387401016"}}} +{"an":{"0387004513":{"label":"Stochastic Calc...","full-label":"Stochastic Calculus for Finance II: Continuous-Time Models (Springer Finance)"}}} +{"ae":{"0387401016_0387004513":{"source":"0387401016","directed":false,"target":"0387004513"}}} +{"an":{"3540221492":{"label":"Stochastic Calc...","full-label":"Stochastic Calculus for Finance II: Continuous-Time Models (Springer Finance)"}}} +{"ae":{"0387401016_3540221492":{"source":"0387401016","directed":false,"target":"3540221492"}}} +{"cn":{"0470192739":{"label":"My Life as a Qu...","full-label":"My Life as a Quant: Reflections on Physics and Finance"}}} +{"an":{"0470050624":{"label":"My Life as a Qu...","full-label":"My Life as a Quant: Reflections on Physics and Finance"}}} +{"ae":{"0470192739_0470050624":{"source":"0470192739","directed":false,"target":"0470050624"}}} +{"an":{"0375758259":{"label":"My Life as a Qu...","full-label":"My Life as a Quant: Reflections on Physics and Finance"}}} +{"ae":{"0470192739_0375758259":{"source":"0470192739","directed":false,"target":"0375758259"}}} +{"an":{"0471457329":{"label":"My Life as a Qu...","full-label":"My Life as a Quant: Reflections on Physics and Finance"}}} +{"ae":{"0470192739_0471457329":{"source":"0470192739","directed":false,"target":"0471457329"}}} +{"ae":{"0812975219_0470192739":{"source":"0812975219","directed":false,"target":"0470192739"}}} +{"cn":{"1432706810":{"label":"Starting Your C...","full-label":"Starting Your Career as a Wall Street Quant: A Practical, No-BS Guide to Getting a Job in Quantitative Finance and Launching a Lucrative Career"}}} +{"ae":{"0470050624_1432706810":{"source":"0470050624","directed":false,"target":"1432706810"}}} +{"ae":{"0470192739_1432706810":{"source":"0470192739","directed":false,"target":"1432706810"}}} +{"an":{"0470058269":{"label":"Starting Your C...","full-label":"Starting Your Career as a Wall Street Quant: A Practical, No-BS Guide to Getting a Job in Quantitative Finance and Launching a Lucrative Career"}}} +{"ae":{"1432706810_0470058269":{"source":"1432706810","directed":false,"target":"0470058269"}}} +{"ae":{"0131499084_1432706810":{"source":"0131499084","directed":false,"target":"1432706810"}}} +{"cn":{"1593573111":{"label":"Resume Magic: T...","full-label":"Resume Magic: Trade Secrets of a Professional Resume Writer (Resume Magic Trade Secrets of a Professional Resume Writer)"}}} +{"an":{"1593573642":{"label":"Resume Magic: T...","full-label":"Resume Magic: Trade Secrets of a Professional Resume Writer (Resume Magic Trade Secrets of a Professional Resume Writer)"}}} +{"ae":{"1593573111_1593573642":{"source":"1593573111","directed":false,"target":"1593573642"}}} +{"an":{"081447280X":{"label":"Resume Magic: T...","full-label":"Resume Magic: Trade Secrets of a Professional Resume Writer (Resume Magic Trade Secrets of a Professional Resume Writer)"}}} +{"ae":{"1593573111_081447280X":{"source":"1593573111","directed":false,"target":"081447280X"}}} +{"ae":{"1402203853_1593573111":{"source":"1402203853","directed":false,"target":"1593573111"}}} +{"ae":{"007016357X_1593573111":{"source":"007016357X","directed":false,"target":"1593573111"}}} +{"cn":{"1593571275":{"label":"Expert Resumes ...","full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)"}}} +{"an":{"1593570007":{"label":"Expert Resumes ...","full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)"}}} +{"ae":{"1593571275_1593570007":{"source":"1593571275","directed":false,"target":"1593570007"}}} +{"an":{"1593570929":{"label":"Expert Resumes ...","full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)"}}} +{"ae":{"1593571275_1593570929":{"source":"1593571275","directed":false,"target":"1593570929"}}} +{"ae":{"1593573111_1593571275":{"source":"1593573111","directed":false,"target":"1593571275"}}} +{"an":{"1593573669":{"label":"Expert Resumes ...","full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)"}}} +{"ae":{"1593571275_1593573669":{"source":"1593571275","directed":false,"target":"1593573669"}}} +{"cn":{"1587131560":{"label":"The IT Career B...","full-label":"The IT Career Builder's Toolkit"}}} +{"ae":{"0071495789_1587131560":{"source":"0071495789","directed":false,"target":"1587131560"}}} +{"an":{"0072126833":{"label":"The IT Career B...","full-label":"The IT Career Builder's Toolkit"}}} +{"ae":{"1587131560_0072126833":{"source":"1587131560","directed":false,"target":"0072126833"}}} +{"an":{"0132253569":{"label":"The IT Career B...","full-label":"The IT Career Builder's Toolkit"}}} +{"ae":{"1587131560_0132253569":{"source":"1587131560","directed":false,"target":"0132253569"}}} +{"an":{"1933639261":{"label":"The IT Career B...","full-label":"The IT Career Builder's Toolkit"}}} +{"ae":{"1587131560_1933639261":{"source":"1587131560","directed":false,"target":"1933639261"}}} +{"cn":{"0071449825":{"label":"Perfect Phrases...","full-label":"Perfect Phrases for the Perfect Interview: Hundreds of Ready-to-Use Phrases That Succinctly Demonstrate Your Skills, Your Experience and Your Value in Any Interview Situation (Perfect Phrases)"}}} +{"ae":{"1402203853_0071449825":{"source":"1402203853","directed":false,"target":"0071449825"}}} +{"an":{"0071454055":{"label":"Perfect Phrases...","full-label":"Perfect Phrases for the Perfect Interview: Hundreds of Ready-to-Use Phrases That Succinctly Demonstrate Your Skills, Your Experience and Your Value in Any Interview Situation (Perfect Phrases)"}}} +{"ae":{"0071449825_0071454055":{"source":"0071449825","directed":false,"target":"0071454055"}}} +{"ae":{"0071387730_0071449825":{"source":"0071387730","directed":false,"target":"0071449825"}}} +{"an":{"0071454063":{"label":"Perfect Phrases...","full-label":"Perfect Phrases for the Perfect Interview: Hundreds of Ready-to-Use Phrases That Succinctly Demonstrate Your Skills, Your Experience and Your Value in Any Interview Situation (Perfect Phrases)"}}} +{"ae":{"0071449825_0071454063":{"source":"0071449825","directed":false,"target":"0071454063"}}} +{"cn":{"0970901224":{"label":"Interview Fitne...","full-label":"Interview Fitness Training: A Workout with Carole Martin, the Interview Coach"}}} +{"ae":{"0071449825_0970901224":{"source":"0071449825","directed":false,"target":"0970901224"}}} +{"ae":{"1402203853_0970901224":{"source":"1402203853","directed":false,"target":"0970901224"}}} +{"ae":{"1564148696_0970901224":{"source":"1564148696","directed":false,"target":"0970901224"}}} +{"ae":{"0071387730_0970901224":{"source":"0071387730","directed":false,"target":"0970901224"}}} +{"cn":{"1564148696":{"label":"Competency-Base...","full-label":"Competency-Based Interviews: Master the Tough New Interview Style And Give Them the Answers That Will Win You the Job"}}} +{"an":{"156414772X":{"label":"Competency-Base...","full-label":"Competency-Based Interviews: Master the Tough New Interview Style And Give Them the Answers That Will Win You the Job"}}} +{"ae":{"1564148696_156414772X":{"source":"1564148696","directed":false,"target":"156414772X"}}} +{"ae":{"1564147789_1564148696":{"source":"1564147789","directed":false,"target":"1564148696"}}} +{"an":{"0814473016":{"label":"Competency-Base...","full-label":"Competency-Based Interviews: Master the Tough New Interview Style And Give Them the Answers That Will Win You the Job"}}} +{"ae":{"1564148696_0814473016":{"source":"1564148696","directed":false,"target":"0814473016"}}} +{"ae":{"0071387730_1564148696":{"source":"0071387730","directed":false,"target":"1564148696"}}} +{"cn":{"007016357X":{"label":"Best Answers to...","full-label":"Best Answers to the 201 Most Frequently Asked Interview Questions"}}} +{"an":{"0425166864":{"label":"Best Answers to...","full-label":"Best Answers to the 201 Most Frequently Asked Interview Questions"}}} +{"ae":{"007016357X_0425166864":{"source":"007016357X","directed":false,"target":"0425166864"}}} +{"ae":{"007141827X_007016357X":{"source":"007141827X","directed":false,"target":"007016357X"}}} +{"cn":{"007141827X":{"label":"How to Intervie...","full-label":"How to Interview Like a Top MBA: Job-Winning Strategies From Headhunters, Fortune 100 Recruiters, and Career Counselors"}}} +{"ae":{"0971015848_007141827X":{"source":"0971015848","directed":false,"target":"007141827X"}}} +{"ae":{"1564148696_007141827X":{"source":"1564148696","directed":false,"target":"007141827X"}}} +{"cn":{"1418040002":{"label":"101 Great Answe...","full-label":"101 Great Answers to the Toughest Interview Questions"}}} +{"an":{"1418040010":{"label":"101 Great Answe...","full-label":"101 Great Answers to the Toughest Interview Questions"}}} +{"ae":{"1418040002_1418040010":{"source":"1418040002","directed":false,"target":"1418040010"}}} +{"ae":{"1402203853_1418040002":{"source":"1402203853","directed":false,"target":"1418040002"}}} +{"ae":{"007016357X_1418040002":{"source":"007016357X","directed":false,"target":"1418040002"}}} +{"ae":{"1593573111_1418040002":{"source":"1593573111","directed":false,"target":"1418040002"}}} +{"cn":{"1564147789":{"label":"Winning Job Int...","full-label":"Winning Job Interviews: Reduce Interview Anxiety / Outprepare the Other Candidates / Land the Job You Love"}}} +{"ae":{"0071387730_1564147789":{"source":"0071387730","directed":false,"target":"1564147789"}}} +{"ae":{"1593573111_1564147789":{"source":"1593573111","directed":false,"target":"1564147789"}}} +{"ae":{"007016357X_1564147789":{"source":"007016357X","directed":false,"target":"1564147789"}}} +{"cn":{"0735622671":{"label":"More About Soft...","full-label":"More About Software Requirements: Thorny Issues and Practical Advice"}}} +{"ae":{"1576810607_0735622671":{"source":"1576810607","directed":false,"target":"0735622671"}}} +{"ae":{"0735605351_0735622671":{"source":"0735605351","directed":false,"target":"0735622671"}}} +{"an":{"0735623988":{"label":"More About Soft...","full-label":"More About Software Requirements: Thorny Issues and Practical Advice"}}} +{"ae":{"0735622671_0735623988":{"source":"0735622671","directed":false,"target":"0735623988"}}} +{"cn":{"1576810607":{"label":"The Software Re...","full-label":"The Software Requirements Memory Jogger: A Pocket Guide to Help Software And Business Teams Develop And Manage Requirements (Memory Jogger) (Memory Jogger)"}}} +{"an":{"0201786060":{"label":"The Software Re...","full-label":"The Software Requirements Memory Jogger: A Pocket Guide to Help Software And Business Teams Develop And Manage Requirements (Memory Jogger) (Memory Jogger)"}}} +{"ae":{"1576810607_0201786060":{"source":"1576810607","directed":false,"target":"0201786060"}}} +{"ae":{"1592009123_1576810607":{"source":"1592009123","directed":false,"target":"1576810607"}}} +{"cn":{"073561993X":{"label":"Agile Project M...","full-label":"Agile Project Management with Scrum (Microsoft Professional)"}}} +{"ae":{"0130676349_073561993X":{"source":"0130676349","directed":false,"target":"073561993X"}}} +{"an":{"0735623376":{"label":"Agile Project M...","full-label":"Agile Project Management with Scrum (Microsoft Professional)"}}} +{"ae":{"073561993X_0735623376":{"source":"073561993X","directed":false,"target":"0735623376"}}} +{"ae":{"0131111558_073561993X":{"source":"0131111558","directed":false,"target":"073561993X"}}} +{"cn":{"0130676349":{"label":"Agile Software ...","full-label":"Agile Software Development with SCRUM (Series in Agile Software Development)"}}} +{"ae":{"0131111558_0130676349":{"source":"0131111558","directed":false,"target":"0130676349"}}} +{"ae":{"0735623376_0130676349":{"source":"0735623376","directed":false,"target":"0130676349"}}} +{"cn":{"0131111558":{"label":"Agile and Itera...","full-label":"Agile and Iterative Development: A Manager's Guide (The Agile Software Development Series)"}}} +{"an":{"0321219775":{"label":"Agile and Itera...","full-label":"Agile and Iterative Development: A Manager's Guide (The Agile Software Development Series)"}}} +{"ae":{"0131111558_0321219775":{"source":"0131111558","directed":false,"target":"0321219775"}}} +{"cn":{"0977616649":{"label":"Agile Retrospec...","full-label":"Agile Retrospectives: Making Good Teams Great"}}} +{"ae":{"073561993X_0977616649":{"source":"073561993X","directed":false,"target":"0977616649"}}} +{"an":{"0976694026":{"label":"Agile Retrospec...","full-label":"Agile Retrospectives: Making Good Teams Great"}}} +{"ae":{"0977616649_0976694026":{"source":"0977616649","directed":false,"target":"0976694026"}}} +{"an":{"097451408X":{"label":"Agile Retrospec...","full-label":"Agile Retrospectives: Making Good Teams Great"}}} +{"ae":{"0977616649_097451408X":{"source":"0977616649","directed":false,"target":"097451408X"}}} +{"cn":{"0131019082":{"label":"UNIX Internals:...","full-label":"UNIX Internals: The New Frontiers"}}} +{"ae":{"0201433079_0131019082":{"source":"0201433079","directed":false,"target":"0131019082"}}} +{"an":{"0201633388":{"label":"UNIX Internals:...","full-label":"UNIX Internals: The New Frontiers"}}} +{"ae":{"0131019082_0201633388":{"source":"0131019082","directed":false,"target":"0201633388"}}} +{"an":{"0471164836":{"label":"UNIX Internals:...","full-label":"UNIX Internals: The New Frontiers"}}} +{"ae":{"0131019082_0471164836":{"source":"0131019082","directed":false,"target":"0471164836"}}} +{"an":{"1573980137":{"label":"UNIX Internals:...","full-label":"UNIX Internals: The New Frontiers"}}} +{"ae":{"0131019082_1573980137":{"source":"0131019082","directed":false,"target":"1573980137"}}} +{"cn":{"0131429388":{"label":"Operating Syste...","full-label":"Operating Systems Design and Implementation (3rd Edition) (Prentice Hall Software Series)"}}} +{"an":{"0136006639":{"label":"Operating Syste...","full-label":"Operating Systems Design and Implementation (3rd Edition) (Prentice Hall Software Series)"}}} +{"ae":{"0131429388_0136006639":{"source":"0131429388","directed":false,"target":"0136006639"}}} +{"an":{"0130661023":{"label":"Operating Syste...","full-label":"Operating Systems Design and Implementation (3rd Edition) (Prentice Hall Software Series)"}}} +{"ae":{"0131429388_0130661023":{"source":"0131429388","directed":false,"target":"0130661023"}}} +{"ae":{"0321486811_0131429388":{"source":"0321486811","directed":false,"target":"0131429388"}}} +{"ae":{"0596005652_0131429388":{"source":"0596005652","directed":false,"target":"0131429388"}}} +{"cn":{"020107981X":{"label":"The AWK Program...","full-label":"The AWK Programming Language"}}} +{"an":{"0596000707":{"label":"The AWK Program...","full-label":"The AWK Programming Language"}}} +{"ae":{"020107981X_0596000707":{"source":"020107981X","directed":false,"target":"0596000707"}}} +{"ae":{"B00007FYIJ_020107981X":{"source":"B00007FYIJ","directed":false,"target":"020107981X"}}} +{"ae":{"013937681X_020107981X":{"source":"013937681X","directed":false,"target":"020107981X"}}} +{"ae":{"0596528124_020107981X":{"source":"0596528124","directed":false,"target":"020107981X"}}} +{"cn":{"1590595009":{"label":"The Best Softwa...","full-label":"The Best Software Writing I: Selected and Introduced by Joel Spolsky"}}} +{"ae":{"1590598385_1590595009":{"source":"1590598385","directed":false,"target":"1590595009"}}} +{"ae":{"1893115941_1590595009":{"source":"1893115941","directed":false,"target":"1590595009"}}} +{"ae":{"0932633439_1590595009":{"source":"0932633439","directed":false,"target":"1590595009"}}} +{"ae":{"0201835959_1590595009":{"source":"0201835959","directed":false,"target":"1590595009"}}} +{"cn":{"1893115941":{"label":"User Interface ...","full-label":"User Interface Design for Programmers"}}} +{"ae":{"1590598385_1893115941":{"source":"1590598385","directed":false,"target":"1893115941"}}} +{"ae":{"0321344758_1893115941":{"source":"0321344758","directed":false,"target":"1893115941"}}} +{"ae":{"0932633439_1893115941":{"source":"0932633439","directed":false,"target":"1893115941"}}} +{"cn":{"0387251456":{"label":"All of Nonparam...","full-label":"All of Nonparametric Statistics (Springer Texts in Statistics)"}}} +{"ae":{"0387310738_0387251456":{"source":"0387310738","directed":false,"target":"0387251456"}}} +{"ae":{"0387952845_0387251456":{"source":"0387952845","directed":false,"target":"0387251456"}}} +{"an":{"0387988645":{"label":"All of Nonparam...","full-label":"All of Nonparametric Statistics (Springer Texts in Statistics)"}}} +{"ae":{"0387251456_0387988645":{"source":"0387251456","directed":false,"target":"0387988645"}}} +{"ae":{"052168689X_0387251456":{"source":"052168689X","directed":false,"target":"0387251456"}}} +{"cn":{"0534243126":{"label":"Statistical Inf...","full-label":"Statistical Inference"}}} +{"an":{"0195073401":{"label":"Statistical Inf...","full-label":"Statistical Inference"}}} +{"ae":{"0534243126_0195073401":{"source":"0534243126","directed":false,"target":"0195073401"}}} +{"an":{"0393957330":{"label":"Statistical Inf...","full-label":"Statistical Inference"}}} +{"ae":{"0534243126_0393957330":{"source":"0534243126","directed":false,"target":"0393957330"}}} +{"an":{"0691010188":{"label":"Statistical Inf...","full-label":"Statistical Inference"}}} +{"ae":{"0534243126_0691010188":{"source":"0534243126","directed":false,"target":"0691010188"}}} +{"an":{"026212274X":{"label":"Statistical Inf...","full-label":"Statistical Inference"}}} +{"ae":{"0534243126_026212274X":{"source":"0534243126","directed":false,"target":"026212274X"}}} +{"an":{"0393957357":{"label":"Statistical Inf...","full-label":"Statistical Inference"}}} +{"ae":{"0534243126_0393957357":{"source":"0534243126","directed":false,"target":"0393957357"}}} +{"cn":{"052168689X":{"label":"Data Analysis U...","full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models"}}} +{"an":{"159385191X":{"label":"Data Analysis U...","full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models"}}} +{"ae":{"052168689X_159385191X":{"source":"052168689X","directed":false,"target":"159385191X"}}} +{"ae":{"0387713840_052168689X":{"source":"0387713840","directed":false,"target":"052168689X"}}} +{"an":{"0521671930":{"label":"Data Analysis U...","full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models"}}} +{"ae":{"052168689X_0521671930":{"source":"052168689X","directed":false,"target":"0521671930"}}} +{"an":{"0470510242":{"label":"Data Analysis U...","full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models"}}} +{"ae":{"052168689X_0470510242":{"source":"052168689X","directed":false,"target":"0470510242"}}} +{"cn":{"0387212396":{"label":"Monte Carlo Sta...","full-label":"Monte Carlo Statistical Methods (Springer Texts in Statistics)"}}} +{"an":{"0387763694":{"label":"Monte Carlo Sta...","full-label":"Monte Carlo Statistical Methods (Springer Texts in Statistics)"}}} +{"ae":{"0387212396_0387763694":{"source":"0387212396","directed":false,"target":"0387763694"}}} +{"ae":{"052168689X_0387212396":{"source":"052168689X","directed":false,"target":"0387212396"}}} +{"ae":{"0387952845_0387212396":{"source":"0387952845","directed":false,"target":"0387212396"}}} +{"an":{"0387715983":{"label":"Monte Carlo Sta...","full-label":"Monte Carlo Statistical Methods (Springer Texts in Statistics)"}}} +{"ae":{"0387212396_0387715983":{"source":"0387212396","directed":false,"target":"0387715983"}}} +{"cn":{"0387713840":{"label":"Bayesian Comput...","full-label":"Bayesian Computation with R (Use R)"}}} +{"ae":{"0470510242_0387713840":{"source":"0470510242","directed":false,"target":"0387713840"}}} +{"an":{"0387389792":{"label":"Bayesian Comput...","full-label":"Bayesian Computation with R (Use R)"}}} +{"ae":{"0387713840_0387389792":{"source":"0387713840","directed":false,"target":"0387389792"}}} +{"an":{"0387759689":{"label":"Bayesian Comput...","full-label":"Bayesian Computation with R (Use R)"}}} +{"ae":{"0387713840_0387759689":{"source":"0387713840","directed":false,"target":"0387759689"}}} +{"an":{"0387747303":{"label":"Bayesian Comput...","full-label":"Bayesian Computation with R (Use R)"}}} +{"ae":{"0387713840_0387747303":{"source":"0387713840","directed":false,"target":"0387747303"}}} +{"cn":{"0340814055":{"label":"Bayesian Statis...","full-label":"Bayesian Statistics: An Introduction (Arnold Publication)"}}} +{"an":{"0198568320":{"label":"Bayesian Statis...","full-label":"Bayesian Statistics: An Introduction (Arnold Publication)"}}} +{"ae":{"0340814055_0198568320":{"source":"0340814055","directed":false,"target":"0198568320"}}} +{"ae":{"052168689X_0340814055":{"source":"052168689X","directed":false,"target":"0340814055"}}} +{"ae":{"0387212396_0340814055":{"source":"0387212396","directed":false,"target":"0340814055"}}} +{"an":{"0470141158":{"label":"Bayesian Statis...","full-label":"Bayesian Statistics: An Introduction (Arnold Publication)"}}} +{"ae":{"0340814055_0470141158":{"source":"0340814055","directed":false,"target":"0470141158"}}} +{"cn":{"0130085197":{"label":"Digital Image P...","full-label":"Digital Image Processing Using MATLAB(R)"}}} +{"an":{"013168728X":{"label":"Digital Image P...","full-label":"Digital Image Processing Using MATLAB(R)"}}} +{"ae":{"0130085197_013168728X":{"source":"0130085197","directed":false,"target":"013168728X"}}} +{"ae":{"0471056693_0130085197":{"source":"0471056693","directed":false,"target":"0130085197"}}} +{"an":{"0534400116":{"label":"Digital Image P...","full-label":"Digital Image Processing Using MATLAB(R)"}}} +{"ae":{"0130085197_0534400116":{"source":"0130085197","directed":false,"target":"0534400116"}}} +{"an":{"0470108770":{"label":"Digital Image P...","full-label":"Digital Image Processing Using MATLAB(R)"}}} +{"ae":{"0130085197_0470108770":{"source":"0130085197","directed":false,"target":"0470108770"}}} +{"cn":{"0198538642":{"label":"Neural Networks...","full-label":"Neural Networks for Pattern Recognition"}}} +{"ae":{"0387310738_0198538642":{"source":"0387310738","directed":false,"target":"0198538642"}}} +{"ae":{"0471056693_0198538642":{"source":"0471056693","directed":false,"target":"0198538642"}}} +{"an":{"0132733501":{"label":"Neural Networks...","full-label":"Neural Networks for Pattern Recognition"}}} +{"ae":{"0198538642_0132733501":{"source":"0198538642","directed":false,"target":"0132733501"}}} +{"an":{"0521717701":{"label":"Neural Networks...","full-label":"Neural Networks for Pattern Recognition"}}} +{"ae":{"0198538642_0521717701":{"source":"0198538642","directed":false,"target":"0521717701"}}} +{"cn":{"0321321367":{"label":"Introduction to...","full-label":"Introduction to Data Mining"}}} +{"ae":{"0120884070_0321321367":{"source":"0120884070","directed":false,"target":"0321321367"}}} +{"ae":{"0387310738_0321321367":{"source":"0387310738","directed":false,"target":"0321321367"}}} +{"ae":{"0387952845_0321321367":{"source":"0387952845","directed":false,"target":"0321321367"}}} +{"ae":{"0596529325_0321321367":{"source":"0596529325","directed":false,"target":"0321321367"}}} +{"cn":{"0321356985":{"label":"Practical Busin...","full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)"}}} +{"an":{"1590598342":{"label":"Practical Busin...","full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)"}}} +{"ae":{"0321356985_1590598342":{"source":"0321356985","directed":false,"target":"1590598342"}}} +{"an":{"0471267155":{"label":"Practical Busin...","full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)"}}} +{"ae":{"0321356985_0471267155":{"source":"0321356985","directed":false,"target":"0471267155"}}} +{"an":{"0072260904":{"label":"Practical Busin...","full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)"}}} +{"ae":{"0321356985_0072260904":{"source":"0321356985","directed":false,"target":"0072260904"}}} +{"an":{"0672327821":{"label":"Practical Busin...","full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)"}}} +{"ae":{"0321356985_0672327821":{"source":"0321356985","directed":false,"target":"0672327821"}}} +{"cn":{"0521780195":{"label":"An Introduction...","full-label":"An Introduction to Support Vector Machines and Other Kernel-based Learning Methods"}}} +{"ae":{"0521813972_0521780195":{"source":"0521813972","directed":false,"target":"0521780195"}}} +{"ae":{"0387310738_0521780195":{"source":"0387310738","directed":false,"target":"0521780195"}}} +{"ae":{"0387952845_0521780195":{"source":"0387952845","directed":false,"target":"0521780195"}}} +{"ae":{"0120884070_0521780195":{"source":"0120884070","directed":false,"target":"0521780195"}}} +{"cn":{"0521813972":{"label":"Kernel Methods ...","full-label":"Kernel Methods for Pattern Analysis"}}} +{"ae":{"0387310738_0521813972":{"source":"0387310738","directed":false,"target":"0521813972"}}} +{"ae":{"0387952845_0521813972":{"source":"0387952845","directed":false,"target":"0521813972"}}} +{"ae":{"0471056693_0521813972":{"source":"0471056693","directed":false,"target":"0521813972"}}} +{"cn":{"0262026171":{"label":"Predicting Stru...","full-label":"Predicting Structured Data (Neural Information Processing)"}}} +{"an":{"0262072882":{"label":"Predicting Stru...","full-label":"Predicting Structured Data (Neural Information Processing)"}}} +{"ae":{"0262026171_0262072882":{"source":"0262026171","directed":false,"target":"0262072882"}}} +{"ae":{"0262026252_0262026171":{"source":"0262026252","directed":false,"target":"0262026171"}}} +{"ae":{"0387310738_0262026171":{"source":"0387310738","directed":false,"target":"0262026171"}}} +{"ae":{"0596529325_0262026171":{"source":"0596529325","directed":false,"target":"0262026171"}}} +{"cn":{"0262026252":{"label":"Large-Scale Ker...","full-label":"Large-Scale Kernel Machines (Neural Information Processing)"}}} +{"ae":{"0262072882_0262026252":{"source":"0262072882","directed":false,"target":"0262026252"}}} +{"ae":{"0596529325_0262026252":{"source":"0596529325","directed":false,"target":"0262026252"}}} +{"an":{"0262072815":{"label":"Large-Scale Ker...","full-label":"Large-Scale Kernel Machines (Neural Information Processing)"}}} +{"ae":{"0262026252_0262072815":{"source":"0262026252","directed":false,"target":"0262072815"}}} +{"cn":{"0521592712":{"label":"Probability The...","full-label":"Probability Theory: The Logic of Science"}}} +{"an":{"080186982X":{"label":"Probability The...","full-label":"Probability Theory: The Logic of Science"}}} +{"ae":{"0521592712_080186982X":{"source":"0521592712","directed":false,"target":"080186982X"}}} +{"ae":{"0198568320_0521592712":{"source":"0198568320","directed":false,"target":"0521592712"}}} +{"ae":{"0387310738_0521592712":{"source":"0387310738","directed":false,"target":"0521592712"}}} +{"an":{"1602063281":{"label":"Probability The...","full-label":"Probability Theory: The Logic of Science"}}} +{"ae":{"0521592712_1602063281":{"source":"0521592712","directed":false,"target":"1602063281"}}} diff --git a/modules/StreamingServer/src/test/resources/org/gephi/streaming/server/test/amazon_0201485419_400.dgs b/modules/StreamingServer/src/test/resources/org/gephi/streaming/server/test/amazon_0201485419_400.dgs new file mode 100644 index 0000000000..5d750da318 --- /dev/null +++ b/modules/StreamingServer/src/test/resources/org/gephi/streaming/server/test/amazon_0201485419_400.dgs @@ -0,0 +1,1407 @@ +DGS003 +"amazon_0201485419_400.dgs" 0 0 +an "0201485419" +cn "0201485419" "label":"Art of Computer..." "full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)" +an "0321335708" "label":"Art of Computer..." "full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)" +ae "0201485419_0321335708" "0201485419" "0321335708" +an "0201558025" "label":"Art of Computer..." "full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)" +ae "0201485419_0201558025" "0201485419" "0201558025" +an "0262032937" "label":"Art of Computer..." "full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)" +ae "0201485419_0262032937" "0201485419" "0262032937" +an "0201853930" "label":"Art of Computer..." "full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)" +ae "0201485419_0201853930" "0201485419" "0201853930" +an "0201853949" "label":"Art of Computer..." "full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)" +ae "0201485419_0201853949" "0201485419" "0201853949" +cn "0321335708" "label":"Art of Computer..." "full-label":"Art of Computer Programming, Volume 4, Fascicle 4,The: Generating All Trees--History of Combinatorial Generation (Art of Computer Programming)" +ae "0201853949_0321335708" "0201853949" "0321335708" +ae "0201853930_0321335708" "0201853930" "0321335708" +an "0201853922" "label":"Art of Computer..." "full-label":"Art of Computer Programming, Volume 4, Fascicle 4,The: Generating All Trees--History of Combinatorial Generation (Art of Computer Programming)" +ae "0321335708_0201853922" "0321335708" "0201853922" +an "0321534964" "label":"Art of Computer..." "full-label":"Art of Computer Programming, Volume 4, Fascicle 4,The: Generating All Trees--History of Combinatorial Generation (Art of Computer Programming)" +ae "0321335708_0321534964" "0321335708" "0321534964" +cn "0201558025" "label":"Concrete Mathem..." "full-label":"Concrete Mathematics: A Foundation for Computer Science (2nd Edition)" +ae "0262032937_0201558025" "0262032937" "0201558025" +an "0387948600" "label":"Concrete Mathem..." "full-label":"Concrete Mathematics: A Foundation for Computer Science (2nd Edition)" +ae "0201558025_0387948600" "0201558025" "0387948600" +an "0201657880" "label":"Concrete Mathem..." "full-label":"Concrete Mathematics: A Foundation for Computer Science (2nd Edition)" +ae "0201558025_0201657880" "0201558025" "0201657880" +ae "0321335708_0201558025" "0321335708" "0201558025" +cn "0262032937" "label":"Introduction to..." "full-label":"Introduction to Algorithms" +an "0137903952" "label":"Introduction to..." "full-label":"Introduction to Algorithms" +ae "0262032937_0137903952" "0262032937" "0137903952" +an "0534950973" "label":"Introduction to..." "full-label":"Introduction to Algorithms" +ae "0262032937_0534950973" "0262032937" "0534950973" +an "0471694665" "label":"Introduction to..." "full-label":"Introduction to Algorithms" +ae "0262032937_0471694665" "0262032937" "0471694665" +an "0123706068" "label":"Introduction to..." "full-label":"Introduction to Algorithms" +ae "0262032937_0123706068" "0262032937" "0123706068" +an "0201633612" "label":"Introduction to..." "full-label":"Introduction to Algorithms" +ae "0262032937_0201633612" "0262032937" "0201633612" +cn "0201853930" "label":"The Art of Comp..." "full-label":"The Art of Computer Programming, Volume 4, Fascicle 2: Generating All Tuples and Permutations (Art of Computer Programming)" +ae "0201853949_0201853930" "0201853949" "0201853930" +ae "0201853922_0201853930" "0201853922" "0201853930" +ae "0321534964_0201853930" "0321534964" "0201853930" +cn "0201853949" "label":"The Art of Comp..." "full-label":"The Art of Computer Programming, Volume 4, Fascicle 3: Generating All Combinations and Partitions (Art of Computer Programming)" +ae "0201853922_0201853949" "0201853922" "0201853949" +ae "0321534964_0201853949" "0321534964" "0201853949" +cn "0201853922" "label":"The Art of Comp..." "full-label":"The Art of Computer Programming, Volume 1, Fascicle 1: MMIX -- A RISC Computer for the New Millennium (Art of Computer Programming)" +ae "0321534964_0201853922" "0321534964" "0201853922" +ae "0201485419_0201853922" "0201485419" "0201853922" +cn "0321534964" "label":"The Art of Comp..." "full-label":"The Art of Computer Programming, Volume 4, Fascicle 0: Introduction to Combinatorial Algorithms and Boolean Functions (Art of Computer Programming)" +ae "0201485419_0321534964" "0201485419" "0321534964" +cn "0387948600" "label":"The Algorithm D..." "full-label":"The Algorithm Design Manual" +an "0387001638" "label":"The Algorithm D..." "full-label":"The Algorithm Design Manual" +ae "0387948600_0387001638" "0387948600" "0387001638" +ae "0262032937_0387948600" "0262032937" "0387948600" +an "0596529325" "label":"The Algorithm D..." "full-label":"The Algorithm Design Manual" +ae "0387948600_0596529325" "0387948600" "0596529325" +ae "0201657880_0387948600" "0201657880" "0387948600" +an "0596510047" "label":"The Algorithm D..." "full-label":"The Algorithm Design Manual" +ae "0387948600_0596510047" "0387948600" "0596510047" +cn "0201657880" "label":"Programming Pea..." "full-label":"Programming Pearls (2nd Edition) (ACM Press)" +an "047012167X" "label":"Programming Pea..." "full-label":"Programming Pearls (2nd Edition) (ACM Press)" +ae "0201657880_047012167X" "0201657880" "047012167X" +an "0735619670" "label":"Programming Pea..." "full-label":"Programming Pearls (2nd Edition) (ACM Press)" +ae "0201657880_0735619670" "0201657880" "0735619670" +an "020161586X" "label":"Programming Pea..." "full-label":"Programming Pearls (2nd Edition) (ACM Press)" +ae "0201657880_020161586X" "0201657880" "020161586X" +ae "0201633612_0201657880" "0201633612" "0201657880" +an "020161622X" "label":"Programming Pea..." "full-label":"Programming Pearls (2nd Edition) (ACM Press)" +ae "0201657880_020161622X" "0201657880" "020161622X" +cn "0137903952" "label":"Artificial Inte..." "full-label":"Artificial Intelligence: A Modern Approach (2nd Edition) (Prentice Hall Series in Artificial Intelligence)" +an "0387310738" "label":"Artificial Inte..." "full-label":"Artificial Intelligence: A Modern Approach (2nd Edition) (Prentice Hall Series in Artificial Intelligence)" +ae "0137903952_0387310738" "0137903952" "0387310738" +ae "0471694665_0137903952" "0471694665" "0137903952" +an "0071154671" "label":"Artificial Inte..." "full-label":"Artificial Intelligence: A Modern Approach (2nd Edition) (Prentice Hall Series in Artificial Intelligence)" +ae "0137903952_0071154671" "0137903952" "0071154671" +ae "0534950973_0137903952" "0534950973" "0137903952" +cn "0534950973" "label":"Introduction to..." "full-label":"Introduction to the Theory of Computation, Second Edition" +ae "0471694665_0534950973" "0471694665" "0534950973" +an "0716710455" "label":"Introduction to..." "full-label":"Introduction to the Theory of Computation, Second Edition" +ae "0534950973_0716710455" "0534950973" "0716710455" +an "0321486811" "label":"Introduction to..." "full-label":"Introduction to the Theory of Computation, Second Edition" +ae "0534950973_0321486811" "0534950973" "0321486811" +cn "0471694665" "label":"Operating Syste..." "full-label":"Operating System Concepts (7th Edition)" +ae "0123706068_0471694665" "0123706068" "0471694665" +an "0321497708" "label":"Operating Syste..." "full-label":"Operating System Concepts (7th Edition)" +ae "0471694665_0321497708" "0471694665" "0321497708" +cn "0123706068" "label":"Computer Organi..." "full-label":"Computer Organization and Design: The Hardware/Software Interface. Third Edition, Revised" +an "0123704901" "label":"Computer Organi..." "full-label":"Computer Organization and Design: The Hardware/Software Interface. Third Edition, Revised" +ae "0123706068_0123704901" "0123706068" "0123704901" +an "0131103628" "label":"Computer Organi..." "full-label":"Computer Organization and Design: The Hardware/Software Interface. Third Edition, Revised" +ae "0123706068_0131103628" "0123706068" "0131103628" +ae "0137903952_0123706068" "0137903952" "0123706068" +cn "0201633612" "label":"Design Patterns..." "full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)" +an "0201485672" "label":"Design Patterns..." "full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)" +ae "0201633612_0201485672" "0201633612" "0201485672" +ae "0735619670_0201633612" "0735619670" "0201633612" +an "0596007124" "label":"Design Patterns..." "full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)" +ae "0201633612_0596007124" "0201633612" "0596007124" +an "0321334876" "label":"Design Patterns..." "full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)" +ae "0201633612_0321334876" "0201633612" "0321334876" +an "0321193687" "label":"Design Patterns..." "full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)" +ae "0201633612_0321193687" "0201633612" "0321193687" +cn "0387001638" "label":"Programming Cha..." "full-label":"Programming Challenges" +ae "047012167X_0387001638" "047012167X" "0387001638" +ae "0201657880_0387001638" "0201657880" "0387001638" +an "0470121688" "label":"Programming Cha..." "full-label":"Programming Challenges" +ae "0387001638_0470121688" "0387001638" "0470121688" +ae "0262032937_0387001638" "0262032937" "0387001638" +cn "0596529325" "label":"Programming Col..." "full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications" +an "0596529309" "label":"Programming Col..." "full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications" +ae "0596529325_0596529309" "0596529325" "0596529309" +an "0596529260" "label":"Programming Col..." "full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications" +ae "0596529325_0596529260" "0596529325" "0596529260" +an "0596102356" "label":"Programming Col..." "full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications" +ae "0596529325_0596102356" "0596529325" "0596102356" +ae "0596510047_0596529325" "0596510047" "0596529325" +an "0596514557" "label":"Programming Col..." "full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications" +ae "0596529325_0596514557" "0596529325" "0596514557" +cn "0596510047" "label":"Beautiful Code:..." "full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))" +an "1400082471" "label":"Beautiful Code:..." "full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))" +ae "0596510047_1400082471" "0596510047" "1400082471" +ae "0596529260_0596510047" "0596529260" "0596510047" +an "193435600X" "label":"Beautiful Code:..." "full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))" +ae "0596510047_193435600X" "0596510047" "193435600X" +an "159059844X" "label":"Beautiful Code:..." "full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))" +ae "0596510047_159059844X" "0596510047" "159059844X" +cn "047012167X" "label":"Programming Int..." "full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)" +an "B000JBY0RY" "label":"Programming Int..." "full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)" +ae "047012167X_B000JBY0RY" "047012167X" "B000JBY0RY" +an "B000ESSSN4" "label":"Programming Int..." "full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)" +ae "047012167X_B000ESSSN4" "047012167X" "B000ESSSN4" +ae "0470121688_047012167X" "0470121688" "047012167X" +an "0071495789" "label":"Programming Int..." "full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)" +ae "047012167X_0071495789" "047012167X" "0071495789" +cn "0735619670" "label":"Code Complete: ..." "full-label":"Code Complete: A Practical Handbook of Software Construction" +ae "020161622X_0735619670" "020161622X" "0735619670" +an "1556159005" "label":"Code Complete: ..." "full-label":"Code Complete: A Practical Handbook of Software Construction" +ae "0735619670_1556159005" "0735619670" "1556159005" +an "0735605351" "label":"Code Complete: ..." "full-label":"Code Complete: A Practical Handbook of Software Construction" +ae "0735619670_0735605351" "0735619670" "0735605351" +an "0201835959" "label":"Code Complete: ..." "full-label":"Code Complete: A Practical Handbook of Software Construction" +ae "0735619670_0201835959" "0735619670" "0201835959" +cn "020161586X" "label":"The Practice of..." "full-label":"The Practice of Programming (Addison-Wesley Professional Computing Series)" +ae "0735619670_020161586X" "0735619670" "020161586X" +ae "0131103628_020161586X" "0131103628" "020161586X" +ae "0201633612_020161586X" "0201633612" "020161586X" +an "013937681X" "label":"The Practice of..." "full-label":"The Practice of Programming (Addison-Wesley Professional Computing Series)" +ae "020161586X_013937681X" "020161586X" "013937681X" +cn "020161622X" "label":"The Pragmatic P..." "full-label":"The Pragmatic Programmer: From Journeyman to Master" +ae "0201835959_020161622X" "0201835959" "020161622X" +ae "0201485672_020161622X" "0201485672" "020161622X" +ae "0201633612_020161622X" "0201633612" "020161622X" +an "0932633439" "label":"The Pragmatic P..." "full-label":"The Pragmatic Programmer: From Journeyman to Master" +ae "020161622X_0932633439" "020161622X" "0932633439" +cn "0387310738" "label":"Pattern Recogni..." "full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)" +an "0387952845" "label":"Pattern Recogni..." "full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)" +ae "0387310738_0387952845" "0387310738" "0387952845" +an "0471056693" "label":"Pattern Recogni..." "full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)" +ae "0387310738_0471056693" "0387310738" "0471056693" +an "0120884070" "label":"Pattern Recogni..." "full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)" +ae "0387310738_0120884070" "0387310738" "0120884070" +an "026218253X" "label":"Pattern Recogni..." "full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)" +ae "0387310738_026218253X" "0387310738" "026218253X" +ae "0596529325_0387310738" "0596529325" "0387310738" +cn "0071154671" "label":"Machine Learnin..." "full-label":"Machine Learning (Mcgraw-Hill International Edit)" +ae "0387310738_0071154671" "0387310738" "0071154671" +ae "0120884070_0071154671" "0120884070" "0071154671" +ae "0387952845_0071154671" "0387952845" "0071154671" +ae "0471056693_0071154671" "0471056693" "0071154671" +cn "0716710455" "label":"Computers and I..." "full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)" +an "0486402584" "label":"Computers and I..." "full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)" +ae "0716710455_0486402584" "0716710455" "0486402584" +an "3540653678" "label":"Computers and I..." "full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)" +ae "0716710455_3540653678" "0716710455" "3540653678" +ae "0262032937_0716710455" "0262032937" "0716710455" +an "0201530821" "label":"Computers and I..." "full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)" +ae "0716710455_0201530821" "0716710455" "0201530821" +cn "0321486811" "label":"Compilers: Prin..." "full-label":"Compilers: Principles, Techniques, and Tools (2nd Edition)" +an "B00007FYCY" "label":"Compilers: Prin..." "full-label":"Compilers: Principles, Techniques, and Tools (2nd Edition)" +ae "0321486811_B00007FYCY" "0321486811" "B00007FYCY" +an "0126339511" "label":"Compilers: Prin..." "full-label":"Compilers: Principles, Techniques, and Tools (2nd Edition)" +ae "0321486811_0126339511" "0321486811" "0126339511" +ae "0262032937_0321486811" "0262032937" "0321486811" +ae "0137903952_0321486811" "0137903952" "0321486811" +ae "0596510047_0321486811" "0596510047" "0321486811" +cn "0321497708" "label":"Computer Networ..." "full-label":"Computer Networking: A Top-Down Approach (4th Edition)" +ae "0262032937_0321497708" "0262032937" "0321497708" +ae "0123706068_0321497708" "0123706068" "0321497708" +ae "0137903952_0321497708" "0137903952" "0321497708" +an "0321369572" "label":"Computer Networ..." "full-label":"Computer Networking: A Top-Down Approach (4th Edition)" +ae "0321497708_0321369572" "0321497708" "0321369572" +cn "0123704901" "label":"Computer Archit..." "full-label":"Computer Architecture, Fourth Edition: A Quantitative Approach" +ae "0262032937_0123704901" "0262032937" "0123704901" +ae "0471694665_0123704901" "0471694665" "0123704901" +an "1593271042" "label":"Computer Archit..." "full-label":"Computer Architecture, Fourth Edition: A Quantitative Approach" +ae "0123704901_1593271042" "0123704901" "1593271042" +ae "0321486811_0123704901" "0321486811" "0123704901" +cn "0131103628" "label":"C Programming L..." "full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)" +an "0201700735" "label":"C Programming L..." "full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)" +ae "0131103628_0201700735" "0131103628" "0201700735" +an "013089592X" "label":"C Programming L..." "full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)" +ae "0131103628_013089592X" "0131103628" "013089592X" +an "0672305100" "label":"C Programming L..." "full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)" +ae "0131103628_0672305100" "0131103628" "0672305100" +an "0201433079" "label":"C Programming L..." "full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)" +ae "0131103628_0201433079" "0131103628" "0201433079" +ae "013937681X_0131103628" "013937681X" "0131103628" +cn "0201485672" "label":"Refactoring: Im..." "full-label":"Refactoring: Improving the Design of Existing Code (The Addison-Wesley Object Technology Series)" +ae "0735619670_0201485672" "0735619670" "0201485672" +an "0321146530" "label":"Refactoring: Im..." "full-label":"Refactoring: Improving the Design of Existing Code (The Addison-Wesley Object Technology Series)" +ae "0201485672_0321146530" "0201485672" "0321146530" +an "0321127420" "label":"Refactoring: Im..." "full-label":"Refactoring: Improving the Design of Existing Code (The Addison-Wesley Object Technology Series)" +ae "0201485672_0321127420" "0201485672" "0321127420" +cn "0596007124" "label":"Head First Desi..." "full-label":"Head First Design Patterns (Head First)" +an "0596008678" "label":"Head First Desi..." "full-label":"Head First Design Patterns (Head First)" +ae "0596007124_0596008678" "0596007124" "0596008678" +an "0596009208" "label":"Head First Desi..." "full-label":"Head First Design Patterns (Head First)" +ae "0596007124_0596009208" "0596007124" "0596009208" +an "0596516681" "label":"Head First Desi..." "full-label":"Head First Design Patterns (Head First)" +ae "0596007124_0596516681" "0596007124" "0596516681" +an "0596102143" "label":"Head First Desi..." "full-label":"Head First Design Patterns (Head First)" +ae "0596007124_0596102143" "0596007124" "0596102143" +cn "0321334876" "label":"Effective C++: ..." "full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)" +an "020163371X" "label":"Effective C++: ..." "full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)" +ae "0321334876_020163371X" "0321334876" "020163371X" +an "0201749629" "label":"Effective C++: ..." "full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)" +ae "0321334876_0201749629" "0321334876" "0201749629" +ae "0201700735_0321334876" "0201700735" "0321334876" +an "0201379260" "label":"Effective C++: ..." "full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)" +ae "0321334876_0201379260" "0321334876" "0201379260" +cn "0321193687" "label":"UML Distilled: ..." "full-label":"UML Distilled: A Brief Guide to the Standard Object Modeling Language (3rd Edition) (The Addison-Wesley Object Technology Series)" +an "0131489062" "label":"UML Distilled: ..." "full-label":"UML Distilled: A Brief Guide to the Standard Object Modeling Language (3rd Edition) (The Addison-Wesley Object Technology Series)" +ae "0321193687_0131489062" "0321193687" "0131489062" +ae "0201485672_0321193687" "0201485672" "0321193687" +ae "0596007124_0321193687" "0596007124" "0321193687" +ae "0735619670_0321193687" "0735619670" "0321193687" +cn "0470121688" "label":"Puzzles for Pro..." "full-label":"Puzzles for Programmers and Pros" +ae "0201657880_0470121688" "0201657880" "0470121688" +ae "B000JBY0RY_0470121688" "B000JBY0RY" "0470121688" +ae "B000ESSSN4_0470121688" "B000ESSSN4" "0470121688" +an "0486281523" "label":"Puzzles for Pro..." "full-label":"Puzzles for Programmers and Pros" +ae "0470121688_0486281523" "0470121688" "0486281523" +cn "0596529309" "label":"High Performanc..." "full-label":"High Performance Web Sites: Essential Knowledge for Front-End Engineers" +ae "0596102356_0596529309" "0596102356" "0596529309" +ae "0596529260_0596529309" "0596529260" "0596529309" +an "0596528108" "label":"High Performanc..." "full-label":"High Performance Web Sites: Essential Knowledge for Front-End Engineers" +ae "0596529309_0596528108" "0596529309" "0596528108" +ae "0596510047_0596529309" "0596510047" "0596529309" +cn "0596529260" "label":"RESTful Web Ser..." "full-label":"RESTful Web Services" +ae "0596102356_0596529260" "0596102356" "0596529260" +an "0977616630" "label":"RESTful Web Ser..." "full-label":"RESTful Web Services" +ae "0596529260_0977616630" "0596529260" "0977616630" +cn "0596102356" "label":"Building Scalab..." "full-label":"Building Scalable Web Sites: Building, scaling, and optimizing the next generation of web applications" +an "067232699X" "label":"Building Scalab..." "full-label":"Building Scalable Web Sites: Building, scaling, and optimizing the next generation of web applications" +ae "0596102356_067232699X" "0596102356" "067232699X" +an "0596003064" "label":"Building Scalab..." "full-label":"Building Scalable Web Sites: Building, scaling, and optimizing the next generation of web applications" +ae "0596102356_0596003064" "0596102356" "0596003064" +cn "0596514557" "label":"Visualizing Dat..." "full-label":"Visualizing Data" +an "0262182629" "label":"Visualizing Dat..." "full-label":"Visualizing Data" +ae "0596514557_0262182629" "0596514557" "0262182629" +an "159059617X" "label":"Visualizing Dat..." "full-label":"Visualizing Data" +ae "0596514557_159059617X" "0596514557" "159059617X" +an "0596510519" "label":"Visualizing Dat..." "full-label":"Visualizing Data" +ae "0596514557_0596510519" "0596514557" "0596510519" +an "0321525655" "label":"Visualizing Dat..." "full-label":"Visualizing Data" +ae "0596514557_0321525655" "0596514557" "0321525655" +cn "1400082471" "label":"Dreaming in Cod..." "full-label":"Dreaming in Code: Two Dozen Programmers, Three Years, 4,732 Bugs, and One Quest for Transcendent Software" +an "1590597141" "label":"Dreaming in Cod..." "full-label":"Dreaming in Code: Two Dozen Programmers, Three Years, 4,732 Bugs, and One Quest for Transcendent Software" +ae "1400082471_1590597141" "1400082471" "1590597141" +ae "0201835959_1400082471" "0201835959" "1400082471" +ae "159059844X_1400082471" "159059844X" "1400082471" +an "1590598385" "label":"Dreaming in Cod..." "full-label":"Dreaming in Code: Two Dozen Programmers, Three Years, 4,732 Bugs, and One Quest for Transcendent Software" +ae "1400082471_1590598385" "1400082471" "1590598385" +cn "193435600X" "label":"Programming Erl..." "full-label":"Programming Erlang: Software for a Concurrent World" +ae "0596529325_193435600X" "0596529325" "193435600X" +ae "0596529260_193435600X" "0596529260" "193435600X" +an "0978739256" "label":"Programming Erl..." "full-label":"Programming Erlang: Software for a Concurrent World" +ae "193435600X_0978739256" "193435600X" "0978739256" +an "0521692695" "label":"Programming Erl..." "full-label":"Programming Erlang: Software for a Concurrent World" +ae "193435600X_0521692695" "193435600X" "0521692695" +cn "159059844X" "label":"Managing Humans..." "full-label":"Managing Humans: Biting and Humorous Tales of a Software Engineering Manager" +ae "1590598385_159059844X" "1590598385" "159059844X" +an "0596527055" "label":"Managing Humans..." "full-label":"Managing Humans: Biting and Humorous Tales of a Software Engineering Manager" +ae "159059844X_0596527055" "159059844X" "0596527055" +ae "1590597141_159059844X" "1590597141" "159059844X" +cn "B000JBY0RY" "label":"How Would You M..." "full-label":"How Would You Move Mount Fuji? Microsoft's Cult of the Puzzle - How the World's Smartest Company Selects the Most Creative Thinkers" +ae "0201657880_B000JBY0RY" "0201657880" "B000JBY0RY" +an "0071440011" "label":"How Would You M..." "full-label":"How Would You Move Mount Fuji? Microsoft's Cult of the Puzzle - How the World's Smartest Company Selects the Most Creative Thinkers" +ae "B000JBY0RY_0071440011" "B000JBY0RY" "0071440011" +an "0385242719" "label":"How Would You M..." "full-label":"How Would You Move Mount Fuji? Microsoft's Cult of the Puzzle - How the World's Smartest Company Selects the Most Creative Thinkers" +ae "B000JBY0RY_0385242719" "B000JBY0RY" "0385242719" +cn "B000ESSSN4" "label":"How Would You M..." "full-label":"How Would You Move Mount Fuji? : Microsoft's Cult of the Puzzle -- How the World's Smartest Companies Select the Most Creative Thinkers" +ae "0201657880_B000ESSSN4" "0201657880" "B000ESSSN4" +ae "0071440011_B000ESSSN4" "0071440011" "B000ESSSN4" +ae "0385242719_B000ESSSN4" "0385242719" "B000ESSSN4" +an "0970055269" "label":"How Would You M..." "full-label":"How Would You Move Mount Fuji? : Microsoft's Cult of the Puzzle -- How the World's Smartest Companies Select the Most Creative Thinkers" +ae "B000ESSSN4_0970055269" "B000ESSSN4" "0970055269" +cn "0071495789" "label":"Ace the IT Inte..." "full-label":"Ace the IT Interview (Ace the It Job Interview)" +an "0071492747" "label":"Ace the IT Inte..." "full-label":"Ace the IT Interview (Ace the It Job Interview)" +ae "0071495789_0071492747" "0071495789" "0071492747" +an "0071425470" "label":"Ace the IT Inte..." "full-label":"Ace the IT Interview (Ace the It Job Interview)" +ae "0071495789_0071425470" "0071495789" "0071425470" +an "0071387730" "label":"Ace the IT Inte..." "full-label":"Ace the IT Interview (Ace the It Job Interview)" +ae "0071495789_0071387730" "0071495789" "0071387730" +an "1402203853" "label":"Ace the IT Inte..." "full-label":"Ace the IT Interview (Ace the It Job Interview)" +ae "0071495789_1402203853" "0071495789" "1402203853" +cn "1556159005" "label":"Rapid Developme..." "full-label":"Rapid Development: Taming Wild Software Schedules" +ae "0735605351_1556159005" "0735605351" "1556159005" +an "1572316217" "label":"Rapid Developme..." "full-label":"Rapid Development: Taming Wild Software Schedules" +ae "1556159005_1572316217" "1556159005" "1572316217" +ae "0201835959_1556159005" "0201835959" "1556159005" +ae "0932633439_1556159005" "0932633439" "1556159005" +cn "0735605351" "label":"Software Estima..." "full-label":"Software Estimation: Demystifying the Black Art (Best Practices (Microsoft))" +an "0735618798" "label":"Software Estima..." "full-label":"Software Estimation: Demystifying the Black Art (Best Practices (Microsoft))" +ae "0735605351_0735618798" "0735605351" "0735618798" +ae "0932633439_0735605351" "0932633439" "0735605351" +an "0131479415" "label":"Software Estima..." "full-label":"Software Estimation: Demystifying the Black Art (Best Practices (Microsoft))" +ae "0735605351_0131479415" "0735605351" "0131479415" +cn "0201835959" "label":"The Mythical Ma..." "full-label":"The Mythical Man-Month: Essays on Software Engineering, Anniversary Edition (2nd Edition)" +ae "0932633439_0201835959" "0932633439" "0201835959" +ae "0201633612_0201835959" "0201633612" "0201835959" +cn "013937681X" "label":"Unix Programmin..." "full-label":"Unix Programming Environment (Prentice-Hall Software Series)" +an "0132017997" "label":"Unix Programmin..." "full-label":"Unix Programming Environment (Prentice-Hall Software Series)" +ae "013937681X_0132017997" "013937681X" "0132017997" +an "020103669X" "label":"Unix Programmin..." "full-label":"Unix Programming Environment (Prentice-Hall Software Series)" +ae "013937681X_020103669X" "013937681X" "020103669X" +ae "0201433079_013937681X" "0201433079" "013937681X" +cn "0932633439" "label":"Peopleware: Pro..." "full-label":"Peopleware: Productive Projects and Teams (Second Edition)" +ae "0735619670_0932633439" "0735619670" "0932633439" +an "1590593898" "label":"Peopleware: Pro..." "full-label":"Peopleware: Productive Projects and Teams (Second Edition)" +ae "0932633439_1590593898" "0932633439" "1590593898" +cn "0387952845" "label":"The Elements of..." "full-label":"The Elements of Statistical Learning" +ae "0471056693_0387952845" "0471056693" "0387952845" +ae "0120884070_0387952845" "0120884070" "0387952845" +an "0387402721" "label":"The Elements of..." "full-label":"The Elements of Statistical Learning" +ae "0387952845_0387402721" "0387952845" "0387402721" +an "158488388X" "label":"The Elements of..." "full-label":"The Elements of Statistical Learning" +ae "0387952845_158488388X" "0387952845" "158488388X" +cn "0471056693" "label":"Pattern Classif..." "full-label":"Pattern Classification (2nd Edition)" +an "0471429775" "label":"Pattern Classif..." "full-label":"Pattern Classification (2nd Edition)" +ae "0471056693_0471429775" "0471056693" "0471429775" +ae "0120884070_0471056693" "0120884070" "0471056693" +cn "0120884070" "label":"Data Mining: Pr..." "full-label":"Data Mining: Practical Machine Learning Tools and Techniques, Second Edition (Morgan Kaufmann Series in Data Management Systems)" +an "1558609016" "label":"Data Mining: Pr..." "full-label":"Data Mining: Practical Machine Learning Tools and Techniques, Second Edition (Morgan Kaufmann Series in Data Management Systems)" +ae "0120884070_1558609016" "0120884070" "1558609016" +ae "0596529325_0120884070" "0596529325" "0120884070" +an "0262012111" "label":"Data Mining: Pr..." "full-label":"Data Mining: Practical Machine Learning Tools and Techniques, Second Edition (Morgan Kaufmann Series in Data Management Systems)" +ae "0120884070_0262012111" "0120884070" "0262012111" +cn "026218253X" "label":"Gaussian Proces..." "full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)" +an "0262194759" "label":"Gaussian Proces..." "full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)" +ae "026218253X_0262194759" "026218253X" "0262194759" +an "0262033585" "label":"Gaussian Proces..." "full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)" +ae "026218253X_0262033585" "026218253X" "0262033585" +ae "0387952845_026218253X" "0387952845" "026218253X" +an "0521642981" "label":"Gaussian Proces..." "full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)" +ae "026218253X_0521642981" "026218253X" "0521642981" +cn "0486402584" "label":"Combinatorial O..." "full-label":"Combinatorial Optimization: Algorithms and Complexity" +ae "3540653678_0486402584" "3540653678" "0486402584" +an "0486247759" "label":"Combinatorial O..." "full-label":"Combinatorial Optimization: Algorithms and Complexity" +ae "0486402584_0486247759" "0486402584" "0486247759" +ae "0262032937_0486402584" "0262032937" "0486402584" +an "0486678709" "label":"Combinatorial O..." "full-label":"Combinatorial Optimization: Algorithms and Complexity" +ae "0486402584_0486678709" "0486402584" "0486678709" +cn "3540653678" "label":"Approximation A..." "full-label":"Approximation Algorithms" +an "0521474655" "label":"Approximation A..." "full-label":"Approximation Algorithms" +ae "3540653678_0521474655" "3540653678" "0521474655" +an "0521872820" "label":"Approximation A..." "full-label":"Approximation Algorithms" +ae "3540653678_0521872820" "3540653678" "0521872820" +an "0521835402" "label":"Approximation A..." "full-label":"Approximation Algorithms" +ae "3540653678_0521835402" "3540653678" "0521835402" +cn "0201530821" "label":"Computational C..." "full-label":"Computational Complexity" +ae "0534950973_0201530821" "0534950973" "0201530821" +ae "0486402584_0201530821" "0486402584" "0201530821" +ae "3540653678_0201530821" "3540653678" "0201530821" +ae "0262032937_0201530821" "0262032937" "0201530821" +cn "B00007FYCY" "label":"Lex & Yacc" "full-label":"Lex & Yacc" +an "0534939724" "label":"Lex & Yacc" "full-label":"Lex & Yacc" +ae "B00007FYCY_0534939724" "B00007FYCY" "0534939724" +ae "0262032937_B00007FYCY" "0262032937" "B00007FYCY" +an "B00007FYIJ" "label":"Lex & Yacc" "full-label":"Lex & Yacc" +ae "B00007FYCY_B00007FYIJ" "B00007FYCY" "B00007FYIJ" +ae "013089592X_B00007FYCY" "013089592X" "B00007FYCY" +cn "0126339511" "label":"Programming Lan..." "full-label":"Programming Language Pragmatics, Second Edition" +ae "0262032937_0126339511" "0262032937" "0126339511" +ae "0596510047_0126339511" "0596510047" "0126339511" +an "155860698X" "label":"Programming Lan..." "full-label":"Programming Language Pragmatics, Second Edition" +ae "0126339511_155860698X" "0126339511" "155860698X" +ae "0534950973_0126339511" "0534950973" "0126339511" +cn "0321369572" "label":"Fundamentals of..." "full-label":"Fundamentals of Database Systems (5th Edition)" +an "0072253517" "label":"Fundamentals of..." "full-label":"Fundamentals of Database Systems (5th Edition)" +ae "0321369572_0072253517" "0321369572" "0072253517" +ae "0471694665_0321369572" "0471694665" "0321369572" +ae "0262032937_0321369572" "0262032937" "0321369572" +ae "0137903952_0321369572" "0137903952" "0321369572" +cn "1593271042" "label":"Inside the Mach..." "full-label":"Inside the Machine: An Illustrated Introduction to Microprocessors and Computer Architecture" +an "1593270038" "label":"Inside the Mach..." "full-label":"Inside the Machine: An Illustrated Introduction to Microprocessors and Computer Architecture" +ae "1593271042_1593270038" "1593271042" "1593270038" +ae "0596510047_1593271042" "0596510047" "1593271042" +an "1593270658" "label":"Inside the Mach..." "full-label":"Inside the Machine: An Illustrated Introduction to Microprocessors and Computer Architecture" +ae "1593271042_1593270658" "1593271042" "1593270658" +ae "0123706068_1593271042" "0123706068" "1593271042" +cn "0201700735" "label":"The C++ Program..." "full-label":"The C++ Programming Language: Special Edition (3rd Edition)" +ae "0201379260_0201700735" "0201379260" "0201700735" +ae "0201633612_0201700735" "0201633612" "0201700735" +ae "020163371X_0201700735" "020163371X" "0201700735" +cn "013089592X" "label":"C: A Reference ..." "full-label":"C: A Reference Manual (5th Edition)" +an "0672326663" "label":"C: A Reference ..." "full-label":"C: A Reference Manual (5th Edition)" +ae "013089592X_0672326663" "013089592X" "0672326663" +an "0201179288" "label":"C: A Reference ..." "full-label":"C: A Reference Manual (5th Edition)" +ae "013089592X_0201179288" "013089592X" "0201179288" +an "0131774298" "label":"C: A Reference ..." "full-label":"C: A Reference Manual (5th Edition)" +ae "013089592X_0131774298" "013089592X" "0131774298" +ae "0201433079_013089592X" "0201433079" "013089592X" +cn "0672305100" "label":"Absolute Beginn..." "full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)" +ae "0672326663_0672305100" "0672326663" "0672305100" +an "0764570684" "label":"Absolute Beginn..." "full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)" +ae "0672305100_0764570684" "0672305100" "0764570684" +an "0672326965" "label":"Absolute Beginn..." "full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)" +ae "0672305100_0672326965" "0672305100" "0672326965" +an "0764570692" "label":"Absolute Beginn..." "full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)" +ae "0672305100_0764570692" "0672305100" "0764570692" +cn "0201433079" "label":"Advanced Progra..." "full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)" +an "0131411551" "label":"Advanced Progra..." "full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)" +ae "0201433079_0131411551" "0201433079" "0131411551" +an "0130810819" "label":"Advanced Progra..." "full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)" +ae "0201433079_0130810819" "0201433079" "0130810819" +an "0131411543" "label":"Advanced Progra..." "full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)" +ae "0201433079_0131411543" "0201433079" "0131411543" +an "0596005652" "label":"Advanced Progra..." "full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)" +ae "0201433079_0596005652" "0201433079" "0596005652" +cn "0321146530" "label":"Test Driven Dev..." "full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)" +an "0321278658" "label":"Test Driven Dev..." "full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)" +ae "0321146530_0321278658" "0321146530" "0321278658" +an "0321205685" "label":"Test Driven Dev..." "full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)" +ae "0321146530_0321205685" "0321146530" "0321205685" +an "0131177052" "label":"Test Driven Dev..." "full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)" +ae "0321146530_0131177052" "0321146530" "0131177052" +ae "0131479415_0321146530" "0131479415" "0321146530" +cn "0321127420" "label":"Patterns of Ent..." "full-label":"Patterns of Enterprise Application Architecture (The Addison-Wesley Signature Series)" +an "0321200683" "label":"Patterns of Ent..." "full-label":"Patterns of Enterprise Application Architecture (The Addison-Wesley Signature Series)" +ae "0321127420_0321200683" "0321127420" "0321200683" +an "0321125215" "label":"Patterns of Ent..." "full-label":"Patterns of Enterprise Application Architecture (The Addison-Wesley Signature Series)" +ae "0321127420_0321125215" "0321127420" "0321125215" +ae "0201633612_0321127420" "0201633612" "0321127420" +ae "0596007124_0321127420" "0596007124" "0321127420" +cn "0596008678" "label":"Head First Obje..." "full-label":"Head First Object-Oriented Analysis and Design (Head First)" +ae "0596009208_0596008678" "0596009208" "0596008678" +an "0596527357" "label":"Head First Obje..." "full-label":"Head First Object-Oriented Analysis and Design (Head First)" +ae "0596008678_0596527357" "0596008678" "0596527357" +ae "0596102143_0596008678" "0596102143" "0596008678" +an "059610197X" "label":"Head First Obje..." "full-label":"Head First Object-Oriented Analysis and Design (Head First)" +ae "0596008678_059610197X" "0596008678" "059610197X" +cn "0596009208" "label":"Head First Java..." "full-label":"Head First Java, 2nd Edition" +ae "0596516681_0596009208" "0596516681" "0596009208" +ae "059610197X_0596009208" "059610197X" "0596009208" +an "0596005717" "label":"Head First Java..." "full-label":"Head First Java, 2nd Edition" +ae "0596009208_0596005717" "0596009208" "0596005717" +cn "0596516681" "label":"Head First Serv..." "full-label":"Head First Servlets and JSP: Passing the Sun Certified Web Component Developer Exam (Brain-Friendly Guides)" +ae "0596005717_0596516681" "0596005717" "0596516681" +an "0072253606" "label":"Head First Serv..." "full-label":"Head First Servlets and JSP: Passing the Sun Certified Web Component Developer Exam (Brain-Friendly Guides)" +ae "0596516681_0072253606" "0596516681" "0072253606" +ae "059610197X_0596516681" "059610197X" "0596516681" +cn "0596102143" "label":"Head First Desi..." "full-label":"Head First Design Patterns Poster (Head First)" +ae "0596009208_0596102143" "0596009208" "0596102143" +ae "0596527357_0596102143" "0596527357" "0596102143" +ae "059610197X_0596102143" "059610197X" "0596102143" +cn "020163371X" "label":"More Effective ..." "full-label":"More Effective C++: 35 New Ways to Improve Your Programs and Designs (Addison-Wesley Professional Computing Series)" +ae "0201749629_020163371X" "0201749629" "020163371X" +ae "0201633612_020163371X" "0201633612" "020163371X" +ae "0201379260_020163371X" "0201379260" "020163371X" +cn "0201749629" "label":"Effective STL: ..." "full-label":"Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library (Addison-Wesley Professional Computing Series)" +ae "0201379260_0201749629" "0201379260" "0201749629" +ae "0201633612_0201749629" "0201633612" "0201749629" +ae "0201700735_0201749629" "0201700735" "0201749629" +cn "0201379260" "label":"The C++ Standar..." "full-label":"The C++ Standard Library: A Tutorial and Reference" +an "0201734842" "label":"The C++ Standar..." "full-label":"The C++ Standard Library: A Tutorial and Reference" +ae "0201379260_0201734842" "0201379260" "0201734842" +ae "0201633612_0201379260" "0201633612" "0201379260" +cn "0131489062" "label":"Applying UML an..." "full-label":"Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development (3rd Edition)" +ae "0596007124_0131489062" "0596007124" "0131489062" +ae "0201633612_0131489062" "0201633612" "0131489062" +an "0201702258" "label":"Applying UML an..." "full-label":"Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development (3rd Edition)" +ae "0131489062_0201702258" "0131489062" "0201702258" +ae "0201485672_0131489062" "0201485672" "0131489062" +cn "0486281523" "label":"My Best Mathema..." "full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)" +an "0486252116" "label":"My Best Mathema..." "full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)" +ae "0486281523_0486252116" "0486281523" "0486252116" +an "0486270785" "label":"My Best Mathema..." "full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)" +ae "0486281523_0486270785" "0486281523" "0486270785" +an "0486256375" "label":"My Best Mathema..." "full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)" +ae "0486281523_0486256375" "0486281523" "0486256375" +an "0486427552" "label":"My Best Mathema..." "full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)" +ae "0486281523_0486427552" "0486281523" "0486427552" +an "0970825315" "label":"My Best Mathema..." "full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)" +ae "0486281523_0970825315" "0486281523" "0970825315" +cn "0596528108" "label":"Designing Web N..." "full-label":"Designing Web Navigation: Optimizing the User Experience" +an "0596527349" "label":"Designing Web N..." "full-label":"Designing Web Navigation: Optimizing the User Experience" +ae "0596528108_0596527349" "0596528108" "0596527349" +ae "0596529325_0596528108" "0596529325" "0596528108" +an "0321344758" "label":"Designing Web N..." "full-label":"Designing Web Navigation: Optimizing the User Experience" +ae "0596528108_0321344758" "0596528108" "0321344758" +an "0596008031" "label":"Designing Web N..." "full-label":"Designing Web Navigation: Optimizing the User Experience" +ae "0596528108_0596008031" "0596528108" "0596008031" +cn "0977616630" "label":"Agile Web Devel..." "full-label":"Agile Web Development with Rails, 2nd Edition" +an "0974514055" "label":"Agile Web Devel..." "full-label":"Agile Web Development with Rails, 2nd Edition" +ae "0977616630_0974514055" "0977616630" "0974514055" +an "0977616606" "label":"Agile Web Devel..." "full-label":"Agile Web Development with Rails, 2nd Edition" +ae "0977616630_0977616606" "0977616630" "0977616606" +an "0596527446" "label":"Agile Web Devel..." "full-label":"Agile Web Development with Rails, 2nd Edition" +ae "0977616630_0596527446" "0977616630" "0596527446" +an "0596523696" "label":"Agile Web Devel..." "full-label":"Agile Web Development with Rails, 2nd Edition" +ae "0977616630_0596523696" "0977616630" "0596523696" +an "1932394699" "label":"Agile Web Devel..." "full-label":"Agile Web Development with Rails, 2nd Edition" +ae "0977616630_1932394699" "0977616630" "1932394699" +cn "067232699X" "label":"Scalable Intern..." "full-label":"Scalable Internet Architectures (Developer's Library)" +ae "0596529309_067232699X" "0596529309" "067232699X" +ae "0596529325_067232699X" "0596529325" "067232699X" +ae "0596529260_067232699X" "0596529260" "067232699X" +ae "0596003064_067232699X" "0596003064" "067232699X" +cn "0596003064" "label":"High Performanc..." "full-label":"High Performance MySQL" +an "059652708X" "label":"High Performanc..." "full-label":"High Performance MySQL" +ae "0596003064_059652708X" "0596003064" "059652708X" +an "0596100892" "label":"High Performanc..." "full-label":"High Performance MySQL" +ae "0596003064_0596100892" "0596003064" "0596100892" +ae "0596529309_0596003064" "0596529309" "0596003064" +an "159059505X" "label":"High Performanc..." "full-label":"High Performance MySQL" +ae "0596003064_159059505X" "0596003064" "159059505X" +cn "0262182629" "label":"Processing: A P..." "full-label":"Processing: A Programming Handbook for Visual Designers and Artists" +ae "159059617X_0262182629" "159059617X" "0262182629" +ae "0596510519_0262182629" "0596510519" "0262182629" +an "159200346X" "label":"Processing: A P..." "full-label":"Processing: A Programming Handbook for Visual Designers and Artists" +ae "0262182629_159200346X" "0262182629" "159200346X" +ae "0596529325_0262182629" "0596529325" "0262182629" +cn "159059617X" "label":"Processing: Cre..." "full-label":"Processing: Creative Coding and Computational Art (Foundation)" +ae "0596510519_159059617X" "0596510519" "159059617X" +ae "159200346X_159059617X" "159200346X" "159059617X" +an "0500285179" "label":"Processing: Cre..." "full-label":"Processing: Creative Coding and Computational Art (Foundation)" +ae "159059617X_0500285179" "159059617X" "0500285179" +cn "0596510519" "label":"Making Things T..." "full-label":"Making Things Talk: Practical Methods for Connecting Physical Objects" +ae "159200346X_0596510519" "159200346X" "0596510519" +an "059651428X" "label":"Making Things T..." "full-label":"Making Things Talk: Practical Methods for Connecting Physical Objects" +ae "0596510519_059651428X" "0596510519" "059651428X" +cn "0321525655" "label":"Presentation Ze..." "full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)" +an "1591841992" "label":"Presentation Ze..." "full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)" +ae "0321525655_1591841992" "0321525655" "1591841992" +an "0735623872" "label":"Presentation Ze..." "full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)" +ae "0321525655_0735623872" "0321525655" "0735623872" +an "0195320697" "label":"Presentation Ze..." "full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)" +ae "0321525655_0195320697" "0321525655" "0195320697" +an "0979777704" "label":"Presentation Ze..." "full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)" +ae "0321525655_0979777704" "0321525655" "0979777704" +an "1400064287" "label":"Presentation Ze..." "full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)" +ae "0321525655_1400064287" "0321525655" "1400064287" +cn "1590597141" "label":"Founders at Wor..." "full-label":"Founders at Work: Stories of Startups' Early Days" +an "1591840562" "label":"Founders at Wor..." "full-label":"Founders at Work: Stories of Startups' Early Days" +ae "1590597141_1591840562" "1590597141" "1591840562" +ae "0596527055_1590597141" "0596527055" "1590597141" +ae "0596510047_1590597141" "0596510047" "1590597141" +ae "0596529325_1590597141" "0596529325" "1590597141" +cn "1590598385" "label":"Smart and Gets ..." "full-label":"Smart and Gets Things Done: Joel Spolsky's Concise Guide to Finding the Best Technical Talent" +ae "1590593898_1590598385" "1590593898" "1590598385" +ae "0596510047_1590598385" "0596510047" "1590598385" +ae "1590597141_1590598385" "1590597141" "1590598385" +ae "0932633439_1590598385" "0932633439" "1590598385" +cn "0978739256" "label":"The Definitive ..." "full-label":"The Definitive ANTLR Reference: Building Domain-Specific Languages (Pragmatic Programmers)" +ae "0596510047_0978739256" "0596510047" "0978739256" +ae "0596529325_0978739256" "0596529325" "0978739256" +ae "0596529260_0978739256" "0596529260" "0978739256" +an "0321349601" "label":"The Definitive ..." "full-label":"The Definitive ANTLR Reference: Building Domain-Specific Languages (Pragmatic Programmers)" +ae "0978739256_0321349601" "0978739256" "0321349601" +cn "0521692695" "label":"Programming in ..." "full-label":"Programming in Haskell" +an "0521644089" "label":"Programming in ..." "full-label":"Programming in Haskell" +ae "0521692695_0521644089" "0521692695" "0521644089" +an "0954300696" "label":"Programming in ..." "full-label":"Programming in Haskell" +ae "0521692695_0954300696" "0521692695" "0954300696" +ae "0596510047_0521692695" "0596510047" "0521692695" +an "0521663504" "label":"Programming in ..." "full-label":"Programming in Haskell" +ae "0521692695_0521663504" "0521692695" "0521663504" +cn "0596527055" "label":"The Myths of In..." "full-label":"The Myths of Innovation" +ae "0596510047_0596527055" "0596510047" "0596527055" +ae "0596529325_0596527055" "0596529325" "0596527055" +ae "0321525655_0596527055" "0321525655" "0596527055" +cn "0071440011" "label":"How to Ace the ..." "full-label":"How to Ace the Brainteaser Interview" +ae "047012167X_0071440011" "047012167X" "0071440011" +ae "0071387730_0071440011" "0071387730" "0071440011" +an "0971015848" "label":"How to Ace the ..." "full-label":"How to Ace the Brainteaser Interview" +ae "0071440011_0971015848" "0071440011" "0971015848" +cn "0385242719" "label":"Labyrinths of R..." "full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge" +an "038541580X" "label":"Labyrinths of R..." "full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge" +ae "0385242719_038541580X" "0385242719" "038541580X" +an "0809045990" "label":"Labyrinths of R..." "full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge" +ae "0385242719_0809045990" "0385242719" "0809045990" +an "0521483476" "label":"Labyrinths of R..." "full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge" +ae "0385242719_0521483476" "0385242719" "0521483476" +cn "0970055269" "label":"Heard on the St..." "full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews" +an "0131499084" "label":"Heard on the St..." "full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews" +ae "0970055269_0131499084" "0970055269" "0131499084" +an "1581311729" "label":"Heard on the St..." "full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews" +ae "0970055269_1581311729" "0970055269" "1581311729" +an "0387401016" "label":"Heard on the St..." "full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews" +ae "0970055269_0387401016" "0970055269" "0387401016" +an "0470192739" "label":"Heard on the St..." "full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews" +ae "0970055269_0470192739" "0970055269" "0470192739" +an "1432706810" "label":"Heard on the St..." "full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews" +ae "0970055269_1432706810" "0970055269" "1432706810" +cn "0071492747" "label":"ACE the IT Resu..." "full-label":"ACE the IT Resume (Ace the It Resume)" +ae "047012167X_0071492747" "047012167X" "0071492747" +an "1593573111" "label":"ACE the IT Resu..." "full-label":"ACE the IT Resume (Ace the It Resume)" +ae "0071492747_1593573111" "0071492747" "1593573111" +an "1593571275" "label":"ACE the IT Resu..." "full-label":"ACE the IT Resume (Ace the It Resume)" +ae "0071492747_1593571275" "0071492747" "1593571275" +an "1587131560" "label":"ACE the IT Resu..." "full-label":"ACE the IT Resume (Ace the It Resume)" +ae "0071492747_1587131560" "0071492747" "1587131560" +cn "0071425470" "label":"Boost Your Inte..." "full-label":"Boost Your Interview IQ" +an "0071449825" "label":"Boost Your Inte..." "full-label":"Boost Your Interview IQ" +ae "0071425470_0071449825" "0071425470" "0071449825" +an "0970901224" "label":"Boost Your Inte..." "full-label":"Boost Your Interview IQ" +ae "0071425470_0970901224" "0071425470" "0970901224" +ae "1402203853_0071425470" "1402203853" "0071425470" +an "1564148696" "label":"Boost Your Inte..." "full-label":"Boost Your Interview IQ" +ae "0071425470_1564148696" "0071425470" "1564148696" +cn "0071387730" "label":"201 Best Questi..." "full-label":"201 Best Questions To Ask On Your Interview" +an "007016357X" "label":"201 Best Questi..." "full-label":"201 Best Questions To Ask On Your Interview" +ae "0071387730_007016357X" "0071387730" "007016357X" +ae "1402203853_0071387730" "1402203853" "0071387730" +ae "1593573111_0071387730" "1593573111" "0071387730" +an "007141827X" "label":"201 Best Questi..." "full-label":"201 Best Questions To Ask On Your Interview" +ae "0071387730_007141827X" "0071387730" "007141827X" +an "1418040002" "label":"201 Best Questi..." "full-label":"201 Best Questions To Ask On Your Interview" +ae "0071387730_1418040002" "0071387730" "1418040002" +cn "1402203853" "label":"301 Smart Answe..." "full-label":"301 Smart Answers to Tough Interview Questions" +ae "007016357X_1402203853" "007016357X" "1402203853" +an "1564147789" "label":"301 Smart Answe..." "full-label":"301 Smart Answers to Tough Interview Questions" +ae "1402203853_1564147789" "1402203853" "1564147789" +ae "1564148696_1402203853" "1564148696" "1402203853" +ae "007141827X_1402203853" "007141827X" "1402203853" +cn "1572316217" "label":"Software Projec..." "full-label":"Software Project Survival Guide (Pro -- Best Practices)" +ae "0735605351_1572316217" "0735605351" "1572316217" +ae "0735619670_1572316217" "0735619670" "1572316217" +ae "0201835959_1572316217" "0201835959" "1572316217" +ae "0735618798_1572316217" "0735618798" "1572316217" +cn "0735618798" "label":"Software Requir..." "full-label":"Software Requirements, Second Edition (Pro-Best Practices)" +an "0735622671" "label":"Software Requir..." "full-label":"Software Requirements, Second Edition (Pro-Best Practices)" +ae "0735618798_0735622671" "0735618798" "0735622671" +ae "0201702258_0735618798" "0201702258" "0735618798" +an "1576810607" "label":"Software Requir..." "full-label":"Software Requirements, Second Edition (Pro-Best Practices)" +ae "0735618798_1576810607" "0735618798" "1576810607" +ae "0735619670_0735618798" "0735619670" "0735618798" +cn "0131479415" "label":"Agile Estimatin..." "full-label":"Agile Estimating and Planning (Robert C. Martin Series)" +ae "0321205685_0131479415" "0321205685" "0131479415" +an "073561993X" "label":"Agile Estimatin..." "full-label":"Agile Estimating and Planning (Robert C. Martin Series)" +ae "0131479415_073561993X" "0131479415" "073561993X" +an "0130676349" "label":"Agile Estimatin..." "full-label":"Agile Estimating and Planning (Robert C. Martin Series)" +ae "0131479415_0130676349" "0131479415" "0130676349" +an "0131111558" "label":"Agile Estimatin..." "full-label":"Agile Estimating and Planning (Robert C. Martin Series)" +ae "0131479415_0131111558" "0131479415" "0131111558" +an "0977616649" "label":"Agile Estimatin..." "full-label":"Agile Estimating and Planning (Robert C. Martin Series)" +ae "0131479415_0977616649" "0131479415" "0977616649" +cn "0132017997" "label":"Design of the U..." "full-label":"Design of the UNIX Operating System (Prentice Hall Software Series)" +ae "0131103628_0132017997" "0131103628" "0132017997" +an "0131019082" "label":"Design of the U..." "full-label":"Design of the UNIX Operating System (Prentice Hall Software Series)" +ae "0132017997_0131019082" "0132017997" "0131019082" +an "0131429388" "label":"Design of the U..." "full-label":"Design of the UNIX Operating System (Prentice Hall Software Series)" +ae "0132017997_0131429388" "0132017997" "0131429388" +ae "0201433079_0132017997" "0201433079" "0132017997" +cn "020103669X" "label":"Software Tools" "full-label":"Software Tools" +ae "020161586X_020103669X" "020161586X" "020103669X" +ae "0131103628_020103669X" "0131103628" "020103669X" +an "020107981X" "label":"Software Tools" "full-label":"Software Tools" +ae "020103669X_020107981X" "020103669X" "020107981X" +ae "0132017997_020103669X" "0132017997" "020103669X" +cn "1590593898" "label":"Joel on Softwar..." "full-label":"Joel on Software: And on Diverse and Occasionally Related Matters That Will Prove of Interest to Software Developers, Designers, and Managers, and to Those Who, Whether by Good Fortune or Ill Luck, Work with Them in Some Capacity" +an "1590595009" "label":"Joel on Softwar..." "full-label":"Joel on Software: And on Diverse and Occasionally Related Matters That Will Prove of Interest to Software Developers, Designers, and Managers, and to Those Who, Whether by Good Fortune or Ill Luck, Work with Them in Some Capacity" +ae "1590593898_1590595009" "1590593898" "1590595009" +ae "0201835959_1590593898" "0201835959" "1590593898" +an "1893115941" "label":"Joel on Softwar..." "full-label":"Joel on Software: And on Diverse and Occasionally Related Matters That Will Prove of Interest to Software Developers, Designers, and Managers, and to Those Who, Whether by Good Fortune or Ill Luck, Work with Them in Some Capacity" +ae "1590593898_1893115941" "1590593898" "1893115941" +cn "0387402721" "label":"All of Statisti..." "full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)" +an "0387251456" "label":"All of Statisti..." "full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)" +ae "0387402721_0387251456" "0387402721" "0387251456" +ae "0387310738_0387402721" "0387310738" "0387402721" +an "0534243126" "label":"All of Statisti..." "full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)" +ae "0387402721_0534243126" "0387402721" "0534243126" +an "052168689X" "label":"All of Statisti..." "full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)" +ae "0387402721_052168689X" "0387402721" "052168689X" +cn "158488388X" "label":"Bayesian Data A..." "full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)" +ae "052168689X_158488388X" "052168689X" "158488388X" +an "0387212396" "label":"Bayesian Data A..." "full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)" +ae "158488388X_0387212396" "158488388X" "0387212396" +an "0387713840" "label":"Bayesian Data A..." "full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)" +ae "158488388X_0387713840" "158488388X" "0387713840" +an "0340814055" "label":"Bayesian Data A..." "full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)" +ae "158488388X_0340814055" "158488388X" "0340814055" +cn "0471429775" "label":"Computer Manual..." "full-label":"Computer Manual in MATLAB to Accompany Pattern Classification, Second Edition" +ae "0387310738_0471429775" "0387310738" "0471429775" +ae "0387952845_0471429775" "0387952845" "0471429775" +an "0130085197" "label":"Computer Manual..." "full-label":"Computer Manual in MATLAB to Accompany Pattern Classification, Second Edition" +ae "0471429775_0130085197" "0471429775" "0130085197" +an "0198538642" "label":"Computer Manual..." "full-label":"Computer Manual in MATLAB to Accompany Pattern Classification, Second Edition" +ae "0471429775_0198538642" "0471429775" "0198538642" +cn "1558609016" "label":"Data Mining: Co..." "full-label":"Data Mining: Concepts and Techniques, Second Edition (The Morgan Kaufmann Series in Data Management Systems)" +an "0321321367" "label":"Data Mining: Co..." "full-label":"Data Mining: Concepts and Techniques, Second Edition (The Morgan Kaufmann Series in Data Management Systems)" +ae "1558609016_0321321367" "1558609016" "0321321367" +an "0321356985" "label":"Data Mining: Co..." "full-label":"Data Mining: Concepts and Techniques, Second Edition (The Morgan Kaufmann Series in Data Management Systems)" +ae "1558609016_0321356985" "1558609016" "0321356985" +ae "0387952845_1558609016" "0387952845" "1558609016" +ae "0596529325_1558609016" "0596529325" "1558609016" +cn "0262012111" "label":"Introduction to..." "full-label":"Introduction to Machine Learning (Adaptive Computation and Machine Learning)" +ae "0387310738_0262012111" "0387310738" "0262012111" +ae "0071154671_0262012111" "0071154671" "0262012111" +ae "0387952845_0262012111" "0387952845" "0262012111" +ae "0471056693_0262012111" "0471056693" "0262012111" +cn "0262194759" "label":"Learning with K..." "full-label":"Learning with Kernels: Support Vector Machines, Regularization, Optimization, and Beyond (Adaptive Computation and Machine Learning)" +ae "0387310738_0262194759" "0387310738" "0262194759" +ae "0387952845_0262194759" "0387952845" "0262194759" +an "0521780195" "label":"Learning with K..." "full-label":"Learning with Kernels: Support Vector Machines, Regularization, Optimization, and Beyond (Adaptive Computation and Machine Learning)" +ae "0262194759_0521780195" "0262194759" "0521780195" +an "0521813972" "label":"Learning with K..." "full-label":"Learning with Kernels: Support Vector Machines, Regularization, Optimization, and Beyond (Adaptive Computation and Machine Learning)" +ae "0262194759_0521813972" "0262194759" "0521813972" +cn "0262033585" "label":"Semi-Supervised..." "full-label":"Semi-Supervised Learning (Adaptive Computation and Machine Learning)" +ae "0387310738_0262033585" "0387310738" "0262033585" +ae "0262194759_0262033585" "0262194759" "0262033585" +an "0262026171" "label":"Semi-Supervised..." "full-label":"Semi-Supervised Learning (Adaptive Computation and Machine Learning)" +ae "0262033585_0262026171" "0262033585" "0262026171" +an "0262026252" "label":"Semi-Supervised..." "full-label":"Semi-Supervised Learning (Adaptive Computation and Machine Learning)" +ae "0262033585_0262026252" "0262033585" "0262026252" +cn "0521642981" "label":"Information The..." "full-label":"Information Theory, Inference & Learning Algorithms" +ae "0387310738_0521642981" "0387310738" "0521642981" +an "0521592712" "label":"Information The..." "full-label":"Information Theory, Inference & Learning Algorithms" +ae "0521642981_0521592712" "0521642981" "0521592712" +an "0471241954" "label":"Information The..." "full-label":"Information Theory, Inference & Learning Algorithms" +ae "0521642981_0471241954" "0521642981" "0471241954" +ae "0387952845_0521642981" "0387952845" "0521642981" +cn "0486247759" "label":"Introductory Gr..." "full-label":"Introductory Graph Theory" +ae "0486678709_0486247759" "0486678709" "0486247759" +an "0387984887" "label":"Introductory Gr..." "full-label":"Introductory Graph Theory" +ae "0486247759_0387984887" "0486247759" "0387984887" +an "0486432327" "label":"Introductory Gr..." "full-label":"Introductory Graph Theory" +ae "0486247759_0486432327" "0486247759" "0486432327" +an "0070054894" "label":"Introductory Gr..." "full-label":"Introductory Graph Theory" +ae "0486247759_0070054894" "0486247759" "0070054894" +cn "0486678709" "label":"Introduction to..." "full-label":"Introduction to Graph Theory (Dover Books on Advanced Mathematics)" +an "0199218420" "label":"Introduction to..." "full-label":"Introduction to Graph Theory (Dover Books on Advanced Mathematics)" +ae "0486678709_0199218420" "0486678709" "0199218420" +ae "0070054894_0486678709" "0070054894" "0486678709" +ae "0486432327_0486678709" "0486432327" "0486678709" +cn "0521474655" "label":"Randomized Algo..." "full-label":"Randomized Algorithms" +ae "0521835402_0521474655" "0521835402" "0521474655" +ae "0486402584_0521474655" "0486402584" "0521474655" +ae "0716710455_0521474655" "0716710455" "0521474655" +ae "0521872820_0521474655" "0521872820" "0521474655" +cn "0521872820" "label":"Algorithmic Gam..." "full-label":"Algorithmic Game Theory" +an "0674341163" "label":"Algorithmic Gam..." "full-label":"Algorithmic Game Theory" +ae "0521872820_0674341163" "0521872820" "0674341163" +an "0262033429" "label":"Algorithmic Gam..." "full-label":"Algorithmic Game Theory" +ae "0521872820_0262033429" "0521872820" "0262033429" +ae "0486402584_0521872820" "0486402584" "0521872820" +an "0262650401" "label":"Algorithmic Gam..." "full-label":"Algorithmic Game Theory" +ae "0521872820_0262650401" "0521872820" "0262650401" +cn "0521835402" "label":"Probability and..." "full-label":"Probability and Computing: Randomized Algorithms and Probabilistic Analysis" +ae "0521872820_0521835402" "0521872820" "0521835402" +ae "0262032937_0521835402" "0262032937" "0521835402" +ae "0486402584_0521835402" "0486402584" "0521835402" +cn "0534939724" "label":"Compiler Constr..." "full-label":"Compiler Construction: Principles and Practice" +ae "0321486811_0534939724" "0321486811" "0534939724" +ae "0126339511_0534939724" "0126339511" "0534939724" +ae "0534950973_0534939724" "0534950973" "0534939724" +ae "013089592X_0534939724" "013089592X" "0534939724" +cn "B00007FYIJ" "label":"sed & awk (2nd ..." "full-label":"sed & awk (2nd Edition)" +an "0596005954" "label":"sed & awk (2nd ..." "full-label":"sed & awk (2nd Edition)" +ae "B00007FYIJ_0596005954" "B00007FYIJ" "0596005954" +an "0596528124" "label":"sed & awk (2nd ..." "full-label":"sed & awk (2nd Edition)" +ae "B00007FYIJ_0596528124" "B00007FYIJ" "0596528124" +an "0596009658" "label":"sed & awk (2nd ..." "full-label":"sed & awk (2nd Edition)" +ae "B00007FYIJ_0596009658" "B00007FYIJ" "0596009658" +an "B00007FYGT" "label":"sed & awk (2nd ..." "full-label":"sed & awk (2nd Edition)" +ae "B00007FYIJ_B00007FYGT" "B00007FYIJ" "B00007FYGT" +an "0596003528" "label":"sed & awk (2nd ..." "full-label":"sed & awk (2nd Edition)" +ae "B00007FYIJ_0596003528" "B00007FYIJ" "0596003528" +cn "155860698X" "label":"Engineering a C..." "full-label":"Engineering a Compiler" +ae "0321486811_155860698X" "0321486811" "155860698X" +an "1558603204" "label":"Engineering a C..." "full-label":"Engineering a Compiler" +ae "155860698X_1558603204" "155860698X" "1558603204" +an "1558602860" "label":"Engineering a C..." "full-label":"Engineering a Compiler" +ae "155860698X_1558602860" "155860698X" "1558602860" +ae "B00007FYCY_155860698X" "B00007FYCY" "155860698X" +cn "0072253517" "label":"Oracle Database..." "full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)" +an "0072231459" "label":"Oracle Database..." "full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)" +ae "0072253517_0072231459" "0072253517" "0072231459" +an "0072230665" "label":"Oracle Database..." "full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)" +ae "0072253517_0072230665" "0072253517" "0072230665" +an "0072263172" "label":"Oracle Database..." "full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)" +ae "0072253517_0072263172" "0072253517" "0072263172" +an "0072263059" "label":"Oracle Database..." "full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)" +ae "0072253517_0072263059" "0072253517" "0072263059" +an "0072230789" "label":"Oracle Database..." "full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)" +ae "0072253517_0072230789" "0072253517" "0072230789" +cn "1593270038" "label":"Write Great Cod..." "full-label":"Write Great Code: Volume 1: Understanding the Machine" +ae "1593270658_1593270038" "1593270658" "1593270038" +an "1886411972" "label":"Write Great Cod..." "full-label":"Write Great Code: Volume 1: Understanding the Machine" +ae "1593270038_1886411972" "1593270038" "1886411972" +an "0521520436" "label":"Write Great Cod..." "full-label":"Write Great Code: Volume 1: Understanding the Machine" +ae "1593270038_0521520436" "1593270038" "0521520436" +ae "0735619670_1593270038" "0735619670" "1593270038" +cn "1593270658" "label":"Write Great Cod..." "full-label":"Write Great Code, Volume 2: Thinking Low-Level, Writing High-Level" +ae "1886411972_1593270658" "1886411972" "1593270658" +ae "0521520436_1593270658" "0521520436" "1593270658" +an "1593270569" "label":"Write Great Cod..." "full-label":"Write Great Code, Volume 2: Thinking Low-Level, Writing High-Level" +ae "1593270658_1593270569" "1593270658" "1593270569" +cn "0672326663" "label":"Programming in ..." "full-label":"Programming in C (3rd Edition) (Developer's Library)" +ae "0131103628_0672326663" "0131103628" "0672326663" +ae "0672326965_0672326663" "0672326965" "0672326663" +an "0672325861" "label":"Programming in ..." "full-label":"Programming in C (3rd Edition) (Developer's Library)" +ae "0672326663_0672325861" "0672326663" "0672325861" +cn "0201179288" "label":"C Traps and Pit..." "full-label":"C Traps and Pitfalls" +ae "0131774298_0201179288" "0131774298" "0201179288" +an "0201604612" "label":"C Traps and Pit..." "full-label":"C Traps and Pitfalls" +ae "0201179288_0201604612" "0201179288" "0201604612" +an "0673999866" "label":"C Traps and Pit..." "full-label":"C Traps and Pitfalls" +ae "0201179288_0673999866" "0201179288" "0673999866" +ae "0131103628_0201179288" "0131103628" "0201179288" +cn "0131774298" "label":"Expert C Progra..." "full-label":"Expert C Programming" +ae "0131103628_0131774298" "0131103628" "0131774298" +ae "0201604612_0131774298" "0201604612" "0131774298" +ae "0673999866_0131774298" "0673999866" "0131774298" +cn "0764570684" "label":"C For Dummies, ..." "full-label":"C For Dummies, 2nd Edition" +ae "0764570692_0764570684" "0764570692" "0764570684" +an "0764568523" "label":"C For Dummies, ..." "full-label":"C For Dummies, 2nd Edition" +ae "0764570684_0764568523" "0764570684" "0764568523" +ae "0131103628_0764570684" "0131103628" "0764570684" +an "0470088702" "label":"C For Dummies, ..." "full-label":"C For Dummies, 2nd Edition" +ae "0764570684_0470088702" "0764570684" "0470088702" +cn "0672326965" "label":"C Primer Plus (..." "full-label":"C Primer Plus (5th Edition)" +an "0672326973" "label":"C Primer Plus (..." "full-label":"C Primer Plus (5th Edition)" +ae "0672326965_0672326973" "0672326965" "0672326973" +ae "0131103628_0672326965" "0131103628" "0672326965" +ae "013089592X_0672326965" "013089592X" "0672326965" +cn "0764570692" "label":"C All-in-One De..." "full-label":"C All-in-One Desk Reference for Dummies" +an "0764517953" "label":"C All-in-One De..." "full-label":"C All-in-One Desk Reference for Dummies" +ae "0764570692_0764517953" "0764570692" "0764517953" +ae "0131103628_0764570692" "0131103628" "0764570692" +an "0470124512" "label":"C All-in-One De..." "full-label":"C All-in-One Desk Reference for Dummies" +ae "0764570692_0470124512" "0764570692" "0470124512" +cn "0131411551" "label":"Unix Network Pr..." "full-label":"Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition) (Addison-Wesley Professional Computing Series)" +ae "0130810819_0131411551" "0130810819" "0131411551" +an "020163354X" "label":"Unix Network Pr..." "full-label":"Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition) (Addison-Wesley Professional Computing Series)" +ae "0131411551_020163354X" "0131411551" "020163354X" +an "0201633469" "label":"Unix Network Pr..." "full-label":"Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition) (Addison-Wesley Professional Computing Series)" +ae "0131411551_0201633469" "0131411551" "0201633469" +ae "0596005652_0131411551" "0596005652" "0131411551" +cn "0130810819" "label":"UNIX Network Pr..." "full-label":"UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition) (The Unix Networking Reference Series , Vol 2)" +ae "020163354X_0130810819" "020163354X" "0130810819" +an "013490012X" "label":"UNIX Network Pr..." "full-label":"UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition) (The Unix Networking Reference Series , Vol 2)" +ae "0130810819_013490012X" "0130810819" "013490012X" +an "0201633922" "label":"UNIX Network Pr..." "full-label":"UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition) (The Unix Networking Reference Series , Vol 2)" +ae "0130810819_0201633922" "0130810819" "0201633922" +cn "0131411543" "label":"Advanced UNIX P..." "full-label":"Advanced UNIX Programming (2nd Edition) (Addison-Wesley Professional Computing Series)" +ae "0131411551_0131411543" "0131411551" "0131411543" +ae "0130810819_0131411543" "0130810819" "0131411543" +ae "0131103628_0131411543" "0131103628" "0131411543" +an "0130424110" "label":"Advanced UNIX P..." "full-label":"Advanced UNIX Programming (2nd Edition) (Addison-Wesley Professional Computing Series)" +ae "0131411543_0130424110" "0131411543" "0130424110" +cn "0596005652" "label":"Understanding t..." "full-label":"Understanding the Linux Kernel, Third Edition" +an "B00006AVQ0" "label":"Understanding t..." "full-label":"Understanding the Linux Kernel, Third Edition" +ae "0596005652_B00006AVQ0" "0596005652" "B00006AVQ0" +an "0596002556" "label":"Understanding t..." "full-label":"Understanding the Linux Kernel, Third Edition" +ae "0596005652_0596002556" "0596005652" "0596002556" +an "0672327201" "label":"Understanding t..." "full-label":"Understanding the Linux Kernel, Third Edition" +ae "0596005652_0672327201" "0596005652" "0672327201" +an "0131679848" "label":"Understanding t..." "full-label":"Understanding the Linux Kernel, Third Edition" +ae "0596005652_0131679848" "0596005652" "0131679848" +an "059600222X" "label":"Understanding t..." "full-label":"Understanding the Linux Kernel, Third Edition" +ae "0596005652_059600222X" "0596005652" "059600222X" +cn "0321278658" "label":"Extreme Program..." "full-label":"Extreme Programming Explained: Embrace Change (2nd Edition) (The XP Series)" +ae "0130676349_0321278658" "0130676349" "0321278658" +an "0201710919" "label":"Extreme Program..." "full-label":"Extreme Programming Explained: Embrace Change (2nd Edition) (The XP Series)" +ae "0321278658_0201710919" "0321278658" "0201710919" +ae "073561993X_0321278658" "073561993X" "0321278658" +ae "0201485672_0321278658" "0201485672" "0321278658" +cn "0321205685" "label":"User Stories Ap..." "full-label":"User Stories Applied: For Agile Software Development (The Addison-Wesley Signature Series)" +ae "073561993X_0321205685" "073561993X" "0321205685" +ae "0130676349_0321205685" "0130676349" "0321205685" +ae "0977616649_0321205685" "0977616649" "0321205685" +ae "0131111558_0321205685" "0131111558" "0321205685" +cn "0131177052" "label":"Working Effecti..." "full-label":"Working Effectively with Legacy Code (Robert C. Martin Series)" +ae "0201485672_0131177052" "0201485672" "0131177052" +an "0131495054" "label":"Working Effecti..." "full-label":"Working Effectively with Legacy Code (Robert C. Martin Series)" +ae "0131177052_0131495054" "0131177052" "0131495054" +an "0321213351" "label":"Working Effecti..." "full-label":"Working Effectively with Legacy Code (Robert C. Martin Series)" +ae "0131177052_0321213351" "0131177052" "0321213351" +ae "020161622X_0131177052" "020161622X" "0131177052" +cn "0321200683" "label":"Enterprise Inte..." "full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)" +an "0131858580" "label":"Enterprise Inte..." "full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)" +ae "0321200683_0131858580" "0321200683" "0131858580" +an "0596006756" "label":"Enterprise Inte..." "full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)" +ae "0321200683_0596006756" "0321200683" "0596006756" +ae "0321125215_0321200683" "0321125215" "0321200683" +an "0132344823" "label":"Enterprise Inte..." "full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)" +ae "0321200683_0132344823" "0321200683" "0132344823" +cn "0321125215" "label":"Domain-Driven D..." "full-label":"Domain-Driven Design: Tackling Complexity in the Heart of Software" +an "0321268202" "label":"Domain-Driven D..." "full-label":"Domain-Driven Design: Tackling Complexity in the Heart of Software" +ae "0321125215_0321268202" "0321125215" "0321268202" +ae "0201485672_0321125215" "0201485672" "0321125215" +ae "0321146530_0321125215" "0321146530" "0321125215" +cn "0596527357" "label":"Head First Soft..." "full-label":"Head First Software Development (Brain-Friendly Guides)" +an "0596526849" "label":"Head First Soft..." "full-label":"Head First Software Development (Brain-Friendly Guides)" +ae "0596527357_0596526849" "0596527357" "0596526849" +an "0596514824" "label":"Head First Soft..." "full-label":"Head First Software Development (Brain-Friendly Guides)" +ae "0596527357_0596514824" "0596527357" "0596514824" +ae "0596007124_0596527357" "0596007124" "0596527357" +an "0596527748" "label":"Head First Soft..." "full-label":"Head First Software Development (Brain-Friendly Guides)" +ae "0596527357_0596527748" "0596527357" "0596527748" +cn "059610197X" "label":"Head First HTML..." "full-label":"Head First HTML with CSS & XHTML" +ae "0596527748_059610197X" "0596527748" "059610197X" +an "0596102259" "label":"Head First HTML..." "full-label":"Head First HTML with CSS & XHTML" +ae "059610197X_0596102259" "059610197X" "0596102259" +ae "0596526849_059610197X" "0596526849" "059610197X" +ae "0596007124_059610197X" "0596007124" "059610197X" +cn "0596005717" "label":"Head First EJB ..." "full-label":"Head First EJB (Brain-Friendly Study Guides; Enterprise JavaBeans)" +ae "0596007124_0596005717" "0596007124" "0596005717" +ae "0596008678_0596005717" "0596008678" "0596005717" +ae "0072253606_0596005717" "0072253606" "0596005717" +cn "0072253606" "label":"SCJP Sun Certif..." "full-label":"SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press Study Guides)" +an "1590596978" "label":"SCJP Sun Certif..." "full-label":"SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press Study Guides)" +ae "0072253606_1590596978" "0072253606" "1590596978" +ae "0596009208_0072253606" "0596009208" "0072253606" +ae "0596007124_0072253606" "0596007124" "0072253606" +an "1933988134" "label":"SCJP Sun Certif..." "full-label":"SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press Study Guides)" +ae "0072253606_1933988134" "0072253606" "1933988134" +cn "0201734842" "label":"C++ Templates: ..." "full-label":"C++ Templates: The Complete Guide" +an "0201704315" "label":"C++ Templates: ..." "full-label":"C++ Templates: The Complete Guide" +ae "0201734842_0201704315" "0201734842" "0201704315" +ae "0321334876_0201734842" "0321334876" "0201734842" +ae "0201749629_0201734842" "0201749629" "0201734842" +ae "0201700735_0201734842" "0201700735" "0201734842" +cn "0201702258" "label":"Writing Effecti..." "full-label":"Writing Effective Use Cases (The Agile Software Development Series)" +an "1592009123" "label":"Writing Effecti..." "full-label":"Writing Effective Use Cases (The Agile Software Development Series)" +ae "0201702258_1592009123" "0201702258" "1592009123" +ae "1576810607_0201702258" "1576810607" "0201702258" +an "0321419499" "label":"Writing Effecti..." "full-label":"Writing Effective Use Cases (The Agile Software Development Series)" +ae "0201702258_0321419499" "0201702258" "0321419499" +ae "0735622671_0201702258" "0735622671" "0201702258" +cn "0486252116" "label":"Entertaining Ma..." "full-label":"Entertaining Mathematical Puzzles" +ae "0486256375_0486252116" "0486256375" "0486252116" +ae "0486270785_0486252116" "0486270785" "0486252116" +ae "0486427552_0486252116" "0486427552" "0486252116" +ae "0970825315_0486252116" "0970825315" "0486252116" +cn "0486270785" "label":"The Moscow Puzz..." "full-label":"The Moscow Puzzles: 359 Mathematical Recreations (Math & Logic Puzzles)" +ae "0486256375_0486270785" "0486256375" "0486270785" +an "0393061140" "label":"The Moscow Puzz..." "full-label":"The Moscow Puzzles: 359 Mathematical Recreations (Math & Logic Puzzles)" +ae "0486270785_0393061140" "0486270785" "0393061140" +ae "0486427552_0486270785" "0486427552" "0486270785" +cn "0486256375" "label":"Perplexing Puzz..." "full-label":"Perplexing Puzzles and Tantalizing Teasers (Math & Logic Puzzles)" +ae "0970825315_0486256375" "0970825315" "0486256375" +ae "0486427552_0486256375" "0486427552" "0486256375" +cn "0486427552" "label":"Brain Busters! ..." "full-label":"Brain Busters! Mind-Stretching Puzzles in Math and Logic" +ae "0970825315_0486427552" "0970825315" "0486427552" +an "0970825307" "label":"Brain Busters! ..." "full-label":"Brain Busters! Mind-Stretching Puzzles in Math and Logic" +ae "0486427552_0970825307" "0486427552" "0970825307" +cn "0970825315" "label":"So You Think Yo..." "full-label":"So You Think You're Smart: 150 Fun and Challenging Brain Teasers" +ae "0970825307_0970825315" "0970825307" "0970825315" +an "1885003994" "label":"So You Think Yo..." "full-label":"So You Think You're Smart: 150 Fun and Challenging Brain Teasers" +ae "0970825315_1885003994" "0970825315" "1885003994" +an "0140318755" "label":"So You Think Yo..." "full-label":"So You Think You're Smart: 150 Fun and Challenging Brain Teasers" +ae "0970825315_0140318755" "0970825315" "0140318755" +cn "0596527349" "label":"Information Arc..." "full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites" +ae "0321344758_0596527349" "0321344758" "0596527349" +ae "0596008031_0596527349" "0596008031" "0596527349" +an "0321392353" "label":"Information Arc..." "full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites" +ae "0596527349_0321392353" "0596527349" "0321392353" +an "0596007655" "label":"Information Arc..." "full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites" +ae "0596527349_0596007655" "0596527349" "0596007655" +an "0735712026" "label":"Information Arc..." "full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites" +ae "0596527349_0735712026" "0596527349" "0735712026" +cn "0321344758" "label":"Don't Make Me T..." "full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition" +ae "0596008031_0321344758" "0596008031" "0321344758" +an "0321350316" "label":"Don't Make Me T..." "full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition" +ae "0321344758_0321350316" "0321344758" "0321350316" +an "0465067107" "label":"Don't Make Me T..." "full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition" +ae "0321344758_0465067107" "0321344758" "0465067107" +an "156205810X" "label":"Don't Make Me T..." "full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition" +ae "0321344758_156205810X" "0321344758" "156205810X" +cn "0596008031" "label":"Designing Inter..." "full-label":"Designing Interfaces: Patterns for Effective Interaction Design" +ae "0596007655_0596008031" "0596007655" "0596008031" +an "0262134748" "label":"Designing Inter..." "full-label":"Designing Interfaces: Patterns for Effective Interaction Design" +ae "0596008031_0262134748" "0596008031" "0262134748" +ae "0321392353_0596008031" "0321392353" "0596008031" +cn "0974514055" "label":"Programming Rub..." "full-label":"Programming Ruby: The Pragmatic Programmers' Guide, Second Edition" +ae "0596523696_0974514055" "0596523696" "0974514055" +ae "0977616606_0974514055" "0977616606" "0974514055" +an "0672328844" "label":"Programming Rub..." "full-label":"Programming Ruby: The Pragmatic Programmers' Guide, Second Edition" +ae "0974514055_0672328844" "0974514055" "0672328844" +ae "1932394699_0974514055" "1932394699" "0974514055" +cn "0977616606" "label":"Rails Recipes (..." "full-label":"Rails Recipes (Pragmatic Programmers)" +ae "1932394699_0977616606" "1932394699" "0977616606" +ae "0596523696_0977616606" "0596523696" "0977616606" +ae "0596527446_0977616606" "0596527446" "0977616606" +cn "0596527446" "label":"Ajax on Rails" "full-label":"Ajax on Rails" +an "0596527314" "label":"Ajax on Rails" "full-label":"Ajax on Rails" +ae "0596527446_0596527314" "0596527446" "0596527314" +ae "0596523696_0596527446" "0596523696" "0596527446" +ae "0974514055_0596527446" "0974514055" "0596527446" +cn "0596523696" "label":"Ruby Cookbook (..." "full-label":"Ruby Cookbook (Cookbooks (O'Reilly))" +ae "0596527314_0596523696" "0596527314" "0596523696" +ae "0672328844_0596523696" "0672328844" "0596523696" +cn "1932394699" "label":"Ruby for Rails:..." "full-label":"Ruby for Rails: Ruby Techniques for Rails Developers" +ae "0596523696_1932394699" "0596523696" "1932394699" +ae "0596527446_1932394699" "0596527446" "1932394699" +cn "059652708X" "label":"MySQL Cookbook" "full-label":"MySQL Cookbook" +an "0596101015" "label":"MySQL Cookbook" "full-label":"MySQL Cookbook" +ae "059652708X_0596101015" "059652708X" "0596101015" +ae "0596100892_059652708X" "0596100892" "059652708X" +an "0596008643" "label":"MySQL Cookbook" "full-label":"MySQL Cookbook" +ae "059652708X_0596008643" "059652708X" "0596008643" +an "0672327120" "label":"MySQL Cookbook" "full-label":"MySQL Cookbook" +ae "059652708X_0672327120" "059652708X" "0672327120" +cn "0596100892" "label":"MySQL Stored Pr..." "full-label":"MySQL Stored Procedure Programming" +ae "159059505X_0596100892" "159059505X" "0596100892" +an "0672328704" "label":"MySQL Stored Pr..." "full-label":"MySQL Stored Procedure Programming" +ae "0596100892_0672328704" "0596100892" "0672328704" +an "1590595351" "label":"MySQL Stored Pr..." "full-label":"MySQL Stored Procedure Programming" +ae "0596100892_1590595351" "0596100892" "1590595351" +cn "159059505X" "label":"Pro MySQL (Expe..." "full-label":"Pro MySQL (Expert's Voice in Open Source)" +ae "059652708X_159059505X" "059652708X" "159059505X" +ae "1590595351_159059505X" "1590595351" "159059505X" +ae "0596102356_159059505X" "0596102356" "159059505X" +cn "159200346X" "label":"Physical Comput..." "full-label":"Physical Computing: Sensing and Controlling the Physical World with Computers" +an "0071452818" "label":"Physical Comput..." "full-label":"Physical Computing: Sensing and Controlling the Physical World with Computers" +ae "159200346X_0071452818" "159200346X" "0071452818" +an "0945053312" "label":"Physical Comput..." "full-label":"Physical Computing: Sensing and Controlling the Physical World with Computers" +ae "159200346X_0945053312" "159200346X" "0945053312" +cn "0500285179" "label":"Creative Code: ..." "full-label":"Creative Code: Aesthetics + Computation" +an "0262134721" "label":"Creative Code: ..." "full-label":"Creative Code: Aesthetics + Computation" +ae "0500285179_0262134721" "0500285179" "0262134721" +ae "0262182629_0500285179" "0262182629" "0500285179" +an "0262632446" "label":"Creative Code: ..." "full-label":"Creative Code: Aesthetics + Computation" +ae "0500285179_0262632446" "0500285179" "0262632446" +an "0789305259" "label":"Creative Code: ..." "full-label":"Creative Code: Aesthetics + Computation" +ae "0500285179_0789305259" "0500285179" "0789305259" +cn "059651428X" "label":"The Best of MAK..." "full-label":"The Best of MAKE (Make)" +an "0596514921" "label":"The Best of MAK..." "full-label":"The Best of MAKE (Make)" +ae "059651428X_0596514921" "059651428X" "0596514921" +an "0596510543" "label":"The Best of MAK..." "full-label":"The Best of MAKE (Make)" +ae "059651428X_0596510543" "059651428X" "0596510543" +an "B0007RNI5K" "label":"The Best of MAK..." "full-label":"The Best of MAKE (Make)" +ae "059651428X_B0007RNI5K" "059651428X" "B0007RNI5K" +ae "159200346X_059651428X" "159200346X" "059651428X" +cn "1591841992" "label":"The Back of the..." "full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures" +an "0142005207" "label":"The Back of the..." "full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures" +ae "1591841992_0142005207" "1591841992" "0142005207" +ae "0979777704_1591841992" "0979777704" "1591841992" +an "1594482918" "label":"The Back of the..." "full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures" +ae "1591841992_1594482918" "1591841992" "1594482918" +an "006135323X" "label":"The Back of the..." "full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures" +ae "1591841992_006135323X" "1591841992" "006135323X" +cn "0735623872" "label":"Beyond Bullet P..." "full-label":"Beyond Bullet Points: Using Microsoft® Office PowerPoint® 2007 to Create Presentations That Inform, Motivate, and Inspire" +an "0131875108" "label":"Beyond Bullet P..." "full-label":"Beyond Bullet Points: Using Microsoft® Office PowerPoint® 2007 to Create Presentations That Inform, Motivate, and Inspire" +ae "0735623872_0131875108" "0735623872" "0131875108" +ae "1591841992_0735623872" "1591841992" "0735623872" +ae "0195320697_0735623872" "0195320697" "0735623872" +an "0615142230" "label":"Beyond Bullet P..." "full-label":"Beyond Bullet Points: Using Microsoft® Office PowerPoint® 2007 to Create Presentations That Inform, Motivate, and Inspire" +ae "0735623872_0615142230" "0735623872" "0615142230" +cn "0195320697" "label":"Clear and to th..." "full-label":"Clear and to the Point: 8 Psychological Principles for Compelling PowerPoint Presentations" +ae "1591841992_0195320697" "1591841992" "0195320697" +ae "0615142230_0195320697" "0615142230" "0195320697" +ae "1400064287_0195320697" "1400064287" "0195320697" +cn "0979777704" "label":"Brain Rules: 12..." "full-label":"Brain Rules: 12 Principles for Surviving and Thriving at Work, Home, and School (Book & DVD)" +an "0316113506" "label":"Brain Rules: 12..." "full-label":"Brain Rules: 12 Principles for Surviving and Thriving at Work, Home, and School (Book & DVD)" +ae "0979777704_0316113506" "0979777704" "0316113506" +an "1596912839" "label":"Brain Rules: 12..." "full-label":"Brain Rules: 12 Principles for Surviving and Thriving at Work, Home, and School (Book & DVD)" +ae "0979777704_1596912839" "0979777704" "1596912839" +ae "1594482918_0979777704" "1594482918" "0979777704" +cn "1400064287" "label":"Made to Stick: ..." "full-label":"Made to Stick: Why Some Ideas Survive and Others Die" +ae "1591841992_1400064287" "1591841992" "1400064287" +an "1591841666" "label":"Made to Stick: ..." "full-label":"Made to Stick: Why Some Ideas Survive and Others Die" +ae "1400064287_1591841666" "1400064287" "1591841666" +an "0307341518" "label":"Made to Stick: ..." "full-label":"Made to Stick: Why Some Ideas Survive and Others Die" +ae "1400064287_0307341518" "1400064287" "0307341518" +an "1591841437" "label":"Made to Stick: ..." "full-label":"Made to Stick: Why Some Ideas Survive and Others Die" +ae "1400064287_1591841437" "1400064287" "1591841437" +cn "1591840562" "label":"The Art of the ..." "full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything" +an "088730995X" "label":"The Art of the ..." "full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything" +ae "1591840562_088730995X" "1591840562" "088730995X" +an "0060517123" "label":"The Art of the ..." "full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything" +ae "1591840562_0060517123" "1591840562" "0060517123" +ae "0321525655_1591840562" "0321525655" "1591840562" +an "0971187304" "label":"The Art of the ..." "full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything" +ae "1591840562_0971187304" "1591840562" "0971187304" +cn "0321349601" "label":"Java Concurrenc..." "full-label":"Java Concurrency in Practice" +an "0596527756" "label":"Java Concurrenc..." "full-label":"Java Concurrency in Practice" +ae "0321349601_0596527756" "0321349601" "0596527756" +an "0321356683" "label":"Java Concurrenc..." "full-label":"Java Concurrency in Practice" +ae "0321349601_0321356683" "0321349601" "0321356683" +an "032133678X" "label":"Java Concurrenc..." "full-label":"Java Concurrency in Practice" +ae "0321349601_032133678X" "0321349601" "032133678X" +an "1932394885" "label":"Java Concurrenc..." "full-label":"Java Concurrency in Practice" +ae "0321349601_1932394885" "0321349601" "1932394885" +an "0201310058" "label":"Java Concurrenc..." "full-label":"Java Concurrency in Practice" +ae "0321349601_0201310058" "0321349601" "0201310058" +cn "0521644089" "label":"The Haskell Sch..." "full-label":"The Haskell School of Expression: Learning Functional Programming through Multimedia" +ae "0954300696_0521644089" "0954300696" "0521644089" +ae "193435600X_0521644089" "193435600X" "0521644089" +ae "0521663504_0521644089" "0521663504" "0521644089" +an "0201342758" "label":"The Haskell Sch..." "full-label":"The Haskell School of Expression: Learning Functional Programming through Multimedia" +ae "0521644089_0201342758" "0521644089" "0201342758" +cn "0954300696" "label":"The Haskell Roa..." "full-label":"The Haskell Road to Logic, Maths and Programming (Texts in Computing S.)" +an "0954300653" "label":"The Haskell Roa..." "full-label":"The Haskell Road to Logic, Maths and Programming (Texts in Computing S.)" +ae "0954300696_0954300653" "0954300696" "0954300653" +ae "193435600X_0954300696" "193435600X" "0954300696" +ae "0521663504_0954300696" "0521663504" "0954300696" +cn "0521663504" "label":"Purely Function..." "full-label":"Purely Functional Data Structures" +ae "193435600X_0521663504" "193435600X" "0521663504" +ae "0596510047_0521663504" "0596510047" "0521663504" +cn "0971015848" "label":"Case in Point:C..." "full-label":"Case in Point:Complete Case Interview Preparation - 5th edition" +an "1581313055" "label":"Case in Point:C..." "full-label":"Case in Point:Complete Case Interview Preparation - 5th edition" +ae "0971015848_1581313055" "0971015848" "1581313055" +an "0471757225" "label":"Case in Point:C..." "full-label":"Case in Point:Complete Case Interview Preparation - 5th edition" +ae "0971015848_0471757225" "0971015848" "0471757225" +an "0070534489" "label":"Case in Point:C..." "full-label":"Case in Point:Complete Case Interview Preparation - 5th edition" +ae "0971015848_0070534489" "0971015848" "0070534489" +an "0979003903" "label":"Case in Point:C..." "full-label":"Case in Point:Complete Case Interview Preparation - 5th edition" +ae "0971015848_0979003903" "0971015848" "0979003903" +an "0471444014" "label":"Case in Point:C..." "full-label":"Case in Point:Complete Case Interview Preparation - 5th edition" +ae "0971015848_0471444014" "0971015848" "0471444014" +cn "038541580X" "label":"Prisoner's Dile..." "full-label":"Prisoner's Dilemma" +an "0486296725" "label":"Prisoner's Dile..." "full-label":"Prisoner's Dilemma" +ae "038541580X_0486296725" "038541580X" "0486296725" +ae "0809045990_038541580X" "0809045990" "038541580X" +an "0486251012" "label":"Prisoner's Dile..." "full-label":"Prisoner's Dilemma" +ae "038541580X_0486251012" "038541580X" "0486251012" +an "0465005640" "label":"Prisoner's Dile..." "full-label":"Prisoner's Dilemma" +ae "038541580X_0465005640" "038541580X" "0465005640" +cn "0809045990" "label":"Fortune's Formu..." "full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street" +an "0812975219" "label":"Fortune's Formu..." "full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street" +ae "0809045990_0812975219" "0809045990" "0812975219" +an "047004389X" "label":"Fortune's Formu..." "full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street" +ae "0809045990_047004389X" "0809045990" "047004389X" +an "0231143729" "label":"Fortune's Formu..." "full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street" +ae "0809045990_0231143729" "0809045990" "0231143729" +an "1400063515" "label":"Fortune's Formu..." "full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street" +ae "0809045990_1400063515" "0809045990" "1400063515" +an "0471794473" "label":"Fortune's Formu..." "full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street" +ae "0809045990_0471794473" "0809045990" "0471794473" +cn "0521483476" "label":"Paradoxes" "full-label":"Paradoxes" +an "0691121273" "label":"Paradoxes" "full-label":"Paradoxes" +ae "0521483476_0691121273" "0521483476" "0691121273" +an "0415228093" "label":"Paradoxes" "full-label":"Paradoxes" +ae "0521483476_0415228093" "0521483476" "0415228093" +an "0195179862" "label":"Paradoxes" "full-label":"Paradoxes" +ae "0521483476_0195179862" "0521483476" "0195179862" +an "0486296644" "label":"Paradoxes" "full-label":"Paradoxes" +ae "0521483476_0486296644" "0521483476" "0486296644" +cn "0131499084" "label":"Options, Future..." "full-label":"Options, Futures and Other Derivatives (6th Edition)" +an "0131499068" "label":"Options, Future..." "full-label":"Options, Futures and Other Derivatives (6th Edition)" +ae "0131499084_0131499068" "0131499084" "0131499068" +ae "0387401016_0131499084" "0387401016" "0131499084" +an "0387249680" "label":"Options, Future..." "full-label":"Options, Futures and Other Derivatives (6th Edition)" +ae "0131499084_0387249680" "0131499084" "0387249680" +an "155738486X" "label":"Options, Future..." "full-label":"Options, Futures and Other Derivatives (6th Edition)" +ae "0131499084_155738486X" "0131499084" "155738486X" +cn "1581311729" "label":"Vault Guide to ..." "full-label":"Vault Guide to Advanced Finance & Quantitative Interviews" +an "1581313047" "label":"Vault Guide to ..." "full-label":"Vault Guide to Advanced Finance & Quantitative Interviews" +ae "1581311729_1581313047" "1581311729" "1581313047" +an "1581311702" "label":"Vault Guide to ..." "full-label":"Vault Guide to Advanced Finance & Quantitative Interviews" +ae "1581311729_1581311702" "1581311729" "1581311702" +an "1581315295" "label":"Vault Guide to ..." "full-label":"Vault Guide to Advanced Finance & Quantitative Interviews" +ae "1581311729_1581315295" "1581311729" "1581315295" +ae "0131499084_1581311729" "0131499084" "1581311729" +cn "0387401016" "label":"Stochastic Calc..." "full-label":"Stochastic Calculus for Finance II: Continuous-Time Models (Springer Finance)" +ae "0387249680_0387401016" "0387249680" "0387401016" +an "0387004513" "label":"Stochastic Calc..." "full-label":"Stochastic Calculus for Finance II: Continuous-Time Models (Springer Finance)" +ae "0387401016_0387004513" "0387401016" "0387004513" +an "3540221492" "label":"Stochastic Calc..." "full-label":"Stochastic Calculus for Finance II: Continuous-Time Models (Springer Finance)" +ae "0387401016_3540221492" "0387401016" "3540221492" +cn "0470192739" "label":"My Life as a Qu..." "full-label":"My Life as a Quant: Reflections on Physics and Finance" +an "0470050624" "label":"My Life as a Qu..." "full-label":"My Life as a Quant: Reflections on Physics and Finance" +ae "0470192739_0470050624" "0470192739" "0470050624" +an "0375758259" "label":"My Life as a Qu..." "full-label":"My Life as a Quant: Reflections on Physics and Finance" +ae "0470192739_0375758259" "0470192739" "0375758259" +an "0471457329" "label":"My Life as a Qu..." "full-label":"My Life as a Quant: Reflections on Physics and Finance" +ae "0470192739_0471457329" "0470192739" "0471457329" +ae "0812975219_0470192739" "0812975219" "0470192739" +cn "1432706810" "label":"Starting Your C..." "full-label":"Starting Your Career as a Wall Street Quant: A Practical, No-BS Guide to Getting a Job in Quantitative Finance and Launching a Lucrative Career" +ae "0470050624_1432706810" "0470050624" "1432706810" +ae "0470192739_1432706810" "0470192739" "1432706810" +an "0470058269" "label":"Starting Your C..." "full-label":"Starting Your Career as a Wall Street Quant: A Practical, No-BS Guide to Getting a Job in Quantitative Finance and Launching a Lucrative Career" +ae "1432706810_0470058269" "1432706810" "0470058269" +ae "0131499084_1432706810" "0131499084" "1432706810" +cn "1593573111" "label":"Resume Magic: T..." "full-label":"Resume Magic: Trade Secrets of a Professional Resume Writer (Resume Magic Trade Secrets of a Professional Resume Writer)" +an "1593573642" "label":"Resume Magic: T..." "full-label":"Resume Magic: Trade Secrets of a Professional Resume Writer (Resume Magic Trade Secrets of a Professional Resume Writer)" +ae "1593573111_1593573642" "1593573111" "1593573642" +an "081447280X" "label":"Resume Magic: T..." "full-label":"Resume Magic: Trade Secrets of a Professional Resume Writer (Resume Magic Trade Secrets of a Professional Resume Writer)" +ae "1593573111_081447280X" "1593573111" "081447280X" +ae "1402203853_1593573111" "1402203853" "1593573111" +ae "007016357X_1593573111" "007016357X" "1593573111" +cn "1593571275" "label":"Expert Resumes ..." "full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)" +an "1593570007" "label":"Expert Resumes ..." "full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)" +ae "1593571275_1593570007" "1593571275" "1593570007" +an "1593570929" "label":"Expert Resumes ..." "full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)" +ae "1593571275_1593570929" "1593571275" "1593570929" +ae "1593573111_1593571275" "1593573111" "1593571275" +an "1593573669" "label":"Expert Resumes ..." "full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)" +ae "1593571275_1593573669" "1593571275" "1593573669" +cn "1587131560" "label":"The IT Career B..." "full-label":"The IT Career Builder's Toolkit" +ae "0071495789_1587131560" "0071495789" "1587131560" +an "0072126833" "label":"The IT Career B..." "full-label":"The IT Career Builder's Toolkit" +ae "1587131560_0072126833" "1587131560" "0072126833" +an "0132253569" "label":"The IT Career B..." "full-label":"The IT Career Builder's Toolkit" +ae "1587131560_0132253569" "1587131560" "0132253569" +an "1933639261" "label":"The IT Career B..." "full-label":"The IT Career Builder's Toolkit" +ae "1587131560_1933639261" "1587131560" "1933639261" +cn "0071449825" "label":"Perfect Phrases..." "full-label":"Perfect Phrases for the Perfect Interview: Hundreds of Ready-to-Use Phrases That Succinctly Demonstrate Your Skills, Your Experience and Your Value in Any Interview Situation (Perfect Phrases)" +ae "1402203853_0071449825" "1402203853" "0071449825" +an "0071454055" "label":"Perfect Phrases..." "full-label":"Perfect Phrases for the Perfect Interview: Hundreds of Ready-to-Use Phrases That Succinctly Demonstrate Your Skills, Your Experience and Your Value in Any Interview Situation (Perfect Phrases)" +ae "0071449825_0071454055" "0071449825" "0071454055" +ae "0071387730_0071449825" "0071387730" "0071449825" +an "0071454063" "label":"Perfect Phrases..." "full-label":"Perfect Phrases for the Perfect Interview: Hundreds of Ready-to-Use Phrases That Succinctly Demonstrate Your Skills, Your Experience and Your Value in Any Interview Situation (Perfect Phrases)" +ae "0071449825_0071454063" "0071449825" "0071454063" +cn "0970901224" "label":"Interview Fitne..." "full-label":"Interview Fitness Training: A Workout with Carole Martin, the Interview Coach" +ae "0071449825_0970901224" "0071449825" "0970901224" +ae "1402203853_0970901224" "1402203853" "0970901224" +ae "1564148696_0970901224" "1564148696" "0970901224" +ae "0071387730_0970901224" "0071387730" "0970901224" +cn "1564148696" "label":"Competency-Base..." "full-label":"Competency-Based Interviews: Master the Tough New Interview Style And Give Them the Answers That Will Win You the Job" +an "156414772X" "label":"Competency-Base..." "full-label":"Competency-Based Interviews: Master the Tough New Interview Style And Give Them the Answers That Will Win You the Job" +ae "1564148696_156414772X" "1564148696" "156414772X" +ae "1564147789_1564148696" "1564147789" "1564148696" +an "0814473016" "label":"Competency-Base..." "full-label":"Competency-Based Interviews: Master the Tough New Interview Style And Give Them the Answers That Will Win You the Job" +ae "1564148696_0814473016" "1564148696" "0814473016" +ae "0071387730_1564148696" "0071387730" "1564148696" +cn "007016357X" "label":"Best Answers to..." "full-label":"Best Answers to the 201 Most Frequently Asked Interview Questions" +an "0425166864" "label":"Best Answers to..." "full-label":"Best Answers to the 201 Most Frequently Asked Interview Questions" +ae "007016357X_0425166864" "007016357X" "0425166864" +ae "007141827X_007016357X" "007141827X" "007016357X" +cn "007141827X" "label":"How to Intervie..." "full-label":"How to Interview Like a Top MBA: Job-Winning Strategies From Headhunters, Fortune 100 Recruiters, and Career Counselors" +ae "0971015848_007141827X" "0971015848" "007141827X" +ae "1564148696_007141827X" "1564148696" "007141827X" +cn "1418040002" "label":"101 Great Answe..." "full-label":"101 Great Answers to the Toughest Interview Questions" +an "1418040010" "label":"101 Great Answe..." "full-label":"101 Great Answers to the Toughest Interview Questions" +ae "1418040002_1418040010" "1418040002" "1418040010" +ae "1402203853_1418040002" "1402203853" "1418040002" +ae "007016357X_1418040002" "007016357X" "1418040002" +ae "1593573111_1418040002" "1593573111" "1418040002" +cn "1564147789" "label":"Winning Job Int..." "full-label":"Winning Job Interviews: Reduce Interview Anxiety / Outprepare the Other Candidates / Land the Job You Love" +ae "0071387730_1564147789" "0071387730" "1564147789" +ae "1593573111_1564147789" "1593573111" "1564147789" +ae "007016357X_1564147789" "007016357X" "1564147789" +cn "0735622671" "label":"More About Soft..." "full-label":"More About Software Requirements: Thorny Issues and Practical Advice" +ae "1576810607_0735622671" "1576810607" "0735622671" +ae "0735605351_0735622671" "0735605351" "0735622671" +an "0735623988" "label":"More About Soft..." "full-label":"More About Software Requirements: Thorny Issues and Practical Advice" +ae "0735622671_0735623988" "0735622671" "0735623988" +cn "1576810607" "label":"The Software Re..." "full-label":"The Software Requirements Memory Jogger: A Pocket Guide to Help Software And Business Teams Develop And Manage Requirements (Memory Jogger) (Memory Jogger)" +an "0201786060" "label":"The Software Re..." "full-label":"The Software Requirements Memory Jogger: A Pocket Guide to Help Software And Business Teams Develop And Manage Requirements (Memory Jogger) (Memory Jogger)" +ae "1576810607_0201786060" "1576810607" "0201786060" +ae "1592009123_1576810607" "1592009123" "1576810607" +cn "073561993X" "label":"Agile Project M..." "full-label":"Agile Project Management with Scrum (Microsoft Professional)" +ae "0130676349_073561993X" "0130676349" "073561993X" +an "0735623376" "label":"Agile Project M..." "full-label":"Agile Project Management with Scrum (Microsoft Professional)" +ae "073561993X_0735623376" "073561993X" "0735623376" +ae "0131111558_073561993X" "0131111558" "073561993X" +cn "0130676349" "label":"Agile Software ..." "full-label":"Agile Software Development with SCRUM (Series in Agile Software Development)" +ae "0131111558_0130676349" "0131111558" "0130676349" +ae "0735623376_0130676349" "0735623376" "0130676349" +cn "0131111558" "label":"Agile and Itera..." "full-label":"Agile and Iterative Development: A Manager's Guide (The Agile Software Development Series)" +an "0321219775" "label":"Agile and Itera..." "full-label":"Agile and Iterative Development: A Manager's Guide (The Agile Software Development Series)" +ae "0131111558_0321219775" "0131111558" "0321219775" +cn "0977616649" "label":"Agile Retrospec..." "full-label":"Agile Retrospectives: Making Good Teams Great" +ae "073561993X_0977616649" "073561993X" "0977616649" +an "0976694026" "label":"Agile Retrospec..." "full-label":"Agile Retrospectives: Making Good Teams Great" +ae "0977616649_0976694026" "0977616649" "0976694026" +an "097451408X" "label":"Agile Retrospec..." "full-label":"Agile Retrospectives: Making Good Teams Great" +ae "0977616649_097451408X" "0977616649" "097451408X" +cn "0131019082" "label":"UNIX Internals:..." "full-label":"UNIX Internals: The New Frontiers" +ae "0201433079_0131019082" "0201433079" "0131019082" +an "0201633388" "label":"UNIX Internals:..." "full-label":"UNIX Internals: The New Frontiers" +ae "0131019082_0201633388" "0131019082" "0201633388" +an "0471164836" "label":"UNIX Internals:..." "full-label":"UNIX Internals: The New Frontiers" +ae "0131019082_0471164836" "0131019082" "0471164836" +an "1573980137" "label":"UNIX Internals:..." "full-label":"UNIX Internals: The New Frontiers" +ae "0131019082_1573980137" "0131019082" "1573980137" +cn "0131429388" "label":"Operating Syste..." "full-label":"Operating Systems Design and Implementation (3rd Edition) (Prentice Hall Software Series)" +an "0136006639" "label":"Operating Syste..." "full-label":"Operating Systems Design and Implementation (3rd Edition) (Prentice Hall Software Series)" +ae "0131429388_0136006639" "0131429388" "0136006639" +an "0130661023" "label":"Operating Syste..." "full-label":"Operating Systems Design and Implementation (3rd Edition) (Prentice Hall Software Series)" +ae "0131429388_0130661023" "0131429388" "0130661023" +ae "0321486811_0131429388" "0321486811" "0131429388" +ae "0596005652_0131429388" "0596005652" "0131429388" +cn "020107981X" "label":"The AWK Program..." "full-label":"The AWK Programming Language" +an "0596000707" "label":"The AWK Program..." "full-label":"The AWK Programming Language" +ae "020107981X_0596000707" "020107981X" "0596000707" +ae "B00007FYIJ_020107981X" "B00007FYIJ" "020107981X" +ae "013937681X_020107981X" "013937681X" "020107981X" +ae "0596528124_020107981X" "0596528124" "020107981X" +cn "1590595009" "label":"The Best Softwa..." "full-label":"The Best Software Writing I: Selected and Introduced by Joel Spolsky" +ae "1590598385_1590595009" "1590598385" "1590595009" +ae "1893115941_1590595009" "1893115941" "1590595009" +ae "0932633439_1590595009" "0932633439" "1590595009" +ae "0201835959_1590595009" "0201835959" "1590595009" +cn "1893115941" "label":"User Interface ..." "full-label":"User Interface Design for Programmers" +ae "1590598385_1893115941" "1590598385" "1893115941" +ae "0321344758_1893115941" "0321344758" "1893115941" +ae "0932633439_1893115941" "0932633439" "1893115941" +cn "0387251456" "label":"All of Nonparam..." "full-label":"All of Nonparametric Statistics (Springer Texts in Statistics)" +ae "0387310738_0387251456" "0387310738" "0387251456" +ae "0387952845_0387251456" "0387952845" "0387251456" +an "0387988645" "label":"All of Nonparam..." "full-label":"All of Nonparametric Statistics (Springer Texts in Statistics)" +ae "0387251456_0387988645" "0387251456" "0387988645" +ae "052168689X_0387251456" "052168689X" "0387251456" +cn "0534243126" "label":"Statistical Inf..." "full-label":"Statistical Inference" +an "0195073401" "label":"Statistical Inf..." "full-label":"Statistical Inference" +ae "0534243126_0195073401" "0534243126" "0195073401" +an "0393957330" "label":"Statistical Inf..." "full-label":"Statistical Inference" +ae "0534243126_0393957330" "0534243126" "0393957330" +an "0691010188" "label":"Statistical Inf..." "full-label":"Statistical Inference" +ae "0534243126_0691010188" "0534243126" "0691010188" +an "026212274X" "label":"Statistical Inf..." "full-label":"Statistical Inference" +ae "0534243126_026212274X" "0534243126" "026212274X" +an "0393957357" "label":"Statistical Inf..." "full-label":"Statistical Inference" +ae "0534243126_0393957357" "0534243126" "0393957357" +cn "052168689X" "label":"Data Analysis U..." "full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models" +an "159385191X" "label":"Data Analysis U..." "full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models" +ae "052168689X_159385191X" "052168689X" "159385191X" +ae "0387713840_052168689X" "0387713840" "052168689X" +an "0521671930" "label":"Data Analysis U..." "full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models" +ae "052168689X_0521671930" "052168689X" "0521671930" +an "0470510242" "label":"Data Analysis U..." "full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models" +ae "052168689X_0470510242" "052168689X" "0470510242" +cn "0387212396" "label":"Monte Carlo Sta..." "full-label":"Monte Carlo Statistical Methods (Springer Texts in Statistics)" +an "0387763694" "label":"Monte Carlo Sta..." "full-label":"Monte Carlo Statistical Methods (Springer Texts in Statistics)" +ae "0387212396_0387763694" "0387212396" "0387763694" +ae "052168689X_0387212396" "052168689X" "0387212396" +ae "0387952845_0387212396" "0387952845" "0387212396" +an "0387715983" "label":"Monte Carlo Sta..." "full-label":"Monte Carlo Statistical Methods (Springer Texts in Statistics)" +ae "0387212396_0387715983" "0387212396" "0387715983" +cn "0387713840" "label":"Bayesian Comput..." "full-label":"Bayesian Computation with R (Use R)" +ae "0470510242_0387713840" "0470510242" "0387713840" +an "0387389792" "label":"Bayesian Comput..." "full-label":"Bayesian Computation with R (Use R)" +ae "0387713840_0387389792" "0387713840" "0387389792" +an "0387759689" "label":"Bayesian Comput..." "full-label":"Bayesian Computation with R (Use R)" +ae "0387713840_0387759689" "0387713840" "0387759689" +an "0387747303" "label":"Bayesian Comput..." "full-label":"Bayesian Computation with R (Use R)" +ae "0387713840_0387747303" "0387713840" "0387747303" +cn "0340814055" "label":"Bayesian Statis..." "full-label":"Bayesian Statistics: An Introduction (Arnold Publication)" +an "0198568320" "label":"Bayesian Statis..." "full-label":"Bayesian Statistics: An Introduction (Arnold Publication)" +ae "0340814055_0198568320" "0340814055" "0198568320" +ae "052168689X_0340814055" "052168689X" "0340814055" +ae "0387212396_0340814055" "0387212396" "0340814055" +an "0470141158" "label":"Bayesian Statis..." "full-label":"Bayesian Statistics: An Introduction (Arnold Publication)" +ae "0340814055_0470141158" "0340814055" "0470141158" +cn "0130085197" "label":"Digital Image P..." "full-label":"Digital Image Processing Using MATLAB(R)" +an "013168728X" "label":"Digital Image P..." "full-label":"Digital Image Processing Using MATLAB(R)" +ae "0130085197_013168728X" "0130085197" "013168728X" +ae "0471056693_0130085197" "0471056693" "0130085197" +an "0534400116" "label":"Digital Image P..." "full-label":"Digital Image Processing Using MATLAB(R)" +ae "0130085197_0534400116" "0130085197" "0534400116" +an "0470108770" "label":"Digital Image P..." "full-label":"Digital Image Processing Using MATLAB(R)" +ae "0130085197_0470108770" "0130085197" "0470108770" +cn "0198538642" "label":"Neural Networks..." "full-label":"Neural Networks for Pattern Recognition" +ae "0387310738_0198538642" "0387310738" "0198538642" +ae "0471056693_0198538642" "0471056693" "0198538642" +an "0132733501" "label":"Neural Networks..." "full-label":"Neural Networks for Pattern Recognition" +ae "0198538642_0132733501" "0198538642" "0132733501" +an "0521717701" "label":"Neural Networks..." "full-label":"Neural Networks for Pattern Recognition" +ae "0198538642_0521717701" "0198538642" "0521717701" +cn "0321321367" "label":"Introduction to..." "full-label":"Introduction to Data Mining" +ae "0120884070_0321321367" "0120884070" "0321321367" +ae "0387310738_0321321367" "0387310738" "0321321367" +ae "0387952845_0321321367" "0387952845" "0321321367" +ae "0596529325_0321321367" "0596529325" "0321321367" +cn "0321356985" "label":"Practical Busin..." "full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)" +an "1590598342" "label":"Practical Busin..." "full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)" +ae "0321356985_1590598342" "0321356985" "1590598342" +an "0471267155" "label":"Practical Busin..." "full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)" +ae "0321356985_0471267155" "0321356985" "0471267155" +an "0072260904" "label":"Practical Busin..." "full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)" +ae "0321356985_0072260904" "0321356985" "0072260904" +an "0672327821" "label":"Practical Busin..." "full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)" +ae "0321356985_0672327821" "0321356985" "0672327821" +cn "0521780195" "label":"An Introduction..." "full-label":"An Introduction to Support Vector Machines and Other Kernel-based Learning Methods" +ae "0521813972_0521780195" "0521813972" "0521780195" +ae "0387310738_0521780195" "0387310738" "0521780195" +ae "0387952845_0521780195" "0387952845" "0521780195" +ae "0120884070_0521780195" "0120884070" "0521780195" +cn "0521813972" "label":"Kernel Methods ..." "full-label":"Kernel Methods for Pattern Analysis" +ae "0387310738_0521813972" "0387310738" "0521813972" +ae "0387952845_0521813972" "0387952845" "0521813972" +ae "0471056693_0521813972" "0471056693" "0521813972" +cn "0262026171" "label":"Predicting Stru..." "full-label":"Predicting Structured Data (Neural Information Processing)" +an "0262072882" "label":"Predicting Stru..." "full-label":"Predicting Structured Data (Neural Information Processing)" +ae "0262026171_0262072882" "0262026171" "0262072882" +ae "0262026252_0262026171" "0262026252" "0262026171" +ae "0387310738_0262026171" "0387310738" "0262026171" +ae "0596529325_0262026171" "0596529325" "0262026171" +cn "0262026252" "label":"Large-Scale Ker..." "full-label":"Large-Scale Kernel Machines (Neural Information Processing)" +ae "0262072882_0262026252" "0262072882" "0262026252" +ae "0596529325_0262026252" "0596529325" "0262026252" +an "0262072815" "label":"Large-Scale Ker..." "full-label":"Large-Scale Kernel Machines (Neural Information Processing)" +ae "0262026252_0262072815" "0262026252" "0262072815" +cn "0521592712" "label":"Probability The..." "full-label":"Probability Theory: The Logic of Science" +an "080186982X" "label":"Probability The..." "full-label":"Probability Theory: The Logic of Science" +ae "0521592712_080186982X" "0521592712" "080186982X" +ae "0198568320_0521592712" "0198568320" "0521592712" +ae "0387310738_0521592712" "0387310738" "0521592712" +an "1602063281" "label":"Probability The..." "full-label":"Probability Theory: The Logic of Science" +ae "0521592712_1602063281" "0521592712" "1602063281" diff --git a/modules/StreamingServer/src/test/resources/org/gephi/streaming/server/test/graph.json b/modules/StreamingServer/src/test/resources/org/gephi/streaming/server/test/graph.json new file mode 100644 index 0000000000..bcf825043d --- /dev/null +++ b/modules/StreamingServer/src/test/resources/org/gephi/streaming/server/test/graph.json @@ -0,0 +1,1190 @@ +{"an":{"0201485419":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"Art of Computer...","full-label":"Art of Computer Programming, The, Volumes 1-3 Boxed Set (2nd Edition) (The Art of Computer Programming Series)","z":0,"y":-161.4361,"x":-68.93629,"size":9.315789}}} +{"an":{"0321335708":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Art of Computer...","full-label":"Art of Computer Programming, Volume 4, Fascicle 4,The: Generating All Trees--History of Combinatorial Generation (Art of Computer Programming)","z":0,"y":-99.64874,"x":-53.693092,"size":8.263158}}} +{"ae":{"0201485419_0321335708":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201485419","r":-1,"directed":false,"target":"0321335708"}}} +{"an":{"0201558025":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Concrete Mathem...","full-label":"Concrete Mathematics: A Foundation for Computer Science (2nd Edition)","z":0,"y":-31.431034,"x":-59.53262,"size":7.2105265}}} +{"ae":{"0201485419_0201558025":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201485419","r":-1,"directed":false,"target":"0201558025"}}} +{"ae":{"0321335708_0201558025":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321335708","r":-1,"directed":false,"target":"0201558025"}}} +{"an":{"0262032937":{"g":0.09803922,"b":0,"r":0.9019608,"Label":"Introduction to...","full-label":"Introduction to Algorithms","z":0,"y":-155.96906,"x":22.111515,"size":21.947369}}} +{"ae":{"0262032937_0201558025":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0201558025"}}} +{"ae":{"0201485419_0262032937":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201485419","r":-1,"directed":false,"target":"0262032937"}}} +{"an":{"0201853930":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"The Art of Comp...","full-label":"The Art of Computer Programming, Volume 4, Fascicle 2: Generating All Tuples and Permutations (Art of Computer Programming)","z":0,"y":-167.6665,"x":-193.9437,"size":7.2105265}}} +{"ae":{"0201853930_0321335708":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201853930","r":-1,"directed":false,"target":"0321335708"}}} +{"ae":{"0201485419_0201853930":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201485419","r":-1,"directed":false,"target":"0201853930"}}} +{"an":{"0201853949":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"The Art of Comp...","full-label":"The Art of Computer Programming, Volume 4, Fascicle 3: Generating All Combinations and Partitions (Art of Computer Programming)","z":0,"y":-194.16353,"x":-128.27342,"size":7.2105265}}} +{"ae":{"0201853949_0321335708":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201853949","r":-1,"directed":false,"target":"0321335708"}}} +{"ae":{"0201853949_0201853930":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201853949","r":-1,"directed":false,"target":"0201853930"}}} +{"ae":{"0201485419_0201853949":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201485419","r":-1,"directed":false,"target":"0201853949"}}} +{"an":{"0201853922":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"The Art of Comp...","full-label":"The Art of Computer Programming, Volume 1, Fascicle 1: MMIX -- A RISC Computer for the New Millennium (Art of Computer Programming)","z":0,"y":-109.39606,"x":-111.68071,"size":7.2105265}}} +{"ae":{"0201853922_0201853930":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201853922","r":-1,"directed":false,"target":"0201853930"}}} +{"ae":{"0201853922_0201853949":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201853922","r":-1,"directed":false,"target":"0201853949"}}} +{"ae":{"0201485419_0201853922":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201485419","r":-1,"directed":false,"target":"0201853922"}}} +{"ae":{"0321335708_0201853922":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321335708","r":-1,"directed":false,"target":"0201853922"}}} +{"an":{"0321534964":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"The Art of Comp...","full-label":"The Art of Computer Programming, Volume 4, Fascicle 0: Introduction to Combinatorial Algorithms and Boolean Functions (Art of Computer Programming)","z":0,"y":-127.19927,"x":-164.83841,"size":7.2105265}}} +{"ae":{"0321534964_0201853930":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321534964","r":-1,"directed":false,"target":"0201853930"}}} +{"ae":{"0321534964_0201853949":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321534964","r":-1,"directed":false,"target":"0201853949"}}} +{"ae":{"0321534964_0201853922":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321534964","r":-1,"directed":false,"target":"0201853922"}}} +{"ae":{"0201485419_0321534964":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201485419","r":-1,"directed":false,"target":"0321534964"}}} +{"ae":{"0321335708_0321534964":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321335708","r":-1,"directed":false,"target":"0321534964"}}} +{"an":{"0387948600":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"The Algorithm D...","full-label":"The Algorithm Design Manual","z":0,"y":-7.665352,"x":4.334555,"size":8.263158}}} +{"ae":{"0201558025_0387948600":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201558025","r":-1,"directed":false,"target":"0387948600"}}} +{"ae":{"0262032937_0387948600":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0387948600"}}} +{"an":{"0201657880":{"g":0.88235295,"b":0,"r":0.1254902,"Label":"Programming Pea...","full-label":"Programming Pearls (2nd Edition) (ACM Press)","z":0,"y":21.926872,"x":-166.06108,"size":13.526316}}} +{"ae":{"0201657880_0387948600":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201657880","r":-1,"directed":false,"target":"0387948600"}}} +{"ae":{"0201558025_0201657880":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201558025","r":-1,"directed":false,"target":"0201657880"}}} +{"an":{"0137903952":{"g":0.9098039,"b":0.09411765,"r":0.011764706,"Label":"Artificial Inte...","full-label":"Artificial Intelligence: A Modern Approach (2nd Edition) (Prentice Hall Series in Artificial Intelligence)","z":0,"y":-187.14307,"x":198.13034,"size":11.421053}}} +{"ae":{"0262032937_0137903952":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0137903952"}}} +{"an":{"0534950973":{"g":0.8117647,"b":0.20784314,"r":0.011764706,"Label":"Introduction to...","full-label":"Introduction to the Theory of Computation, Second Edition","z":0,"y":-234.03485,"x":89.17118,"size":10.368422}}} +{"ae":{"0534950973_0137903952":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0534950973","r":-1,"directed":false,"target":"0137903952"}}} +{"ae":{"0262032937_0534950973":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0534950973"}}} +{"an":{"0471694665":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"Operating Syste...","full-label":"Operating System Concepts (7th Edition)","z":0,"y":-133.17636,"x":153.0079,"size":9.315789}}} +{"ae":{"0471694665_0137903952":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0471694665","r":-1,"directed":false,"target":"0137903952"}}} +{"ae":{"0471694665_0534950973":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0471694665","r":-1,"directed":false,"target":"0534950973"}}} +{"ae":{"0262032937_0471694665":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0471694665"}}} +{"an":{"0123706068":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"Computer Organi...","full-label":"Computer Organization and Design: The Hardware/Software Interface. Third Edition, Revised","z":0,"y":-198.44923,"x":25.155857,"size":9.315789}}} +{"ae":{"0123706068_0471694665":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0123706068","r":-1,"directed":false,"target":"0471694665"}}} +{"ae":{"0262032937_0123706068":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0123706068"}}} +{"ae":{"0137903952_0123706068":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0137903952","r":-1,"directed":false,"target":"0123706068"}}} +{"an":{"0201633612":{"g":0.39215687,"b":0,"r":0.6117647,"Label":"Design Patterns...","full-label":"Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series)","z":0,"y":136.87799,"x":-159.21788,"size":18.789474}}} +{"ae":{"0201633612_0201657880":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201633612","r":-1,"directed":false,"target":"0201657880"}}} +{"ae":{"0262032937_0201633612":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0201633612"}}} +{"an":{"0387001638":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Programming Cha...","full-label":"Programming Challenges","z":0,"y":-61.397514,"x":-188.501,"size":7.2105265}}} +{"ae":{"0262032937_0387001638":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0387001638"}}} +{"ae":{"0387948600_0387001638":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387948600","r":-1,"directed":false,"target":"0387001638"}}} +{"ae":{"0201657880_0387001638":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201657880","r":-1,"directed":false,"target":"0387001638"}}} +{"an":{"0596529325":{"g":0.09803922,"b":0,"r":0.9019608,"Label":"Programming Col...","full-label":"Programming Collective Intelligence: Building Smart Web 2.0 Applications","z":0,"y":69.91398,"x":253.71008,"size":21.947369}}} +{"ae":{"0387948600_0596529325":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387948600","r":-1,"directed":false,"target":"0596529325"}}} +{"an":{"0596510047":{"g":0.39215687,"b":0,"r":0.6117647,"Label":"Beautiful Code:...","full-label":"Beautiful Code: Leading Programmers Explain How They Think (Theory in Practice (O'Reilly))","z":0,"y":83.93204,"x":146.20343,"size":18.789474}}} +{"ae":{"0596510047_0596529325":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510047","r":-1,"directed":false,"target":"0596529325"}}} +{"ae":{"0387948600_0596510047":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387948600","r":-1,"directed":false,"target":"0596510047"}}} +{"an":{"047012167X":{"g":0.8117647,"b":0.20784314,"r":0.011764706,"Label":"Programming Int...","full-label":"Programming Interviews Exposed: Secrets to Landing Your Next Job (Programmer to Programmer)","z":0,"y":-10.552108,"x":-340.01245,"size":10.368422}}} +{"ae":{"047012167X_0387001638":{"g":0,"weight":1,"Weight":1,"b":0,"source":"047012167X","r":-1,"directed":false,"target":"0387001638"}}} +{"ae":{"0201657880_047012167X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201657880","r":-1,"directed":false,"target":"047012167X"}}} +{"an":{"0735619670":{"g":0.6862745,"b":0,"r":0.32156864,"Label":"Code Complete: ...","full-label":"Code Complete: A Practical Handbook of Software Construction","z":0,"y":199.41493,"x":-134.89636,"size":15.631578}}} +{"ae":{"0735619670_0201633612":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735619670","r":-1,"directed":false,"target":"0201633612"}}} +{"ae":{"0201657880_0735619670":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201657880","r":-1,"directed":false,"target":"0735619670"}}} +{"an":{"020161586X":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"The Practice of...","full-label":"The Practice of Programming (Addison-Wesley Professional Computing Series)","z":0,"y":-48.786167,"x":-129.67381,"size":8.263158}}} +{"ae":{"0201657880_020161586X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201657880","r":-1,"directed":false,"target":"020161586X"}}} +{"ae":{"0201633612_020161586X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201633612","r":-1,"directed":false,"target":"020161586X"}}} +{"ae":{"0735619670_020161586X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735619670","r":-1,"directed":false,"target":"020161586X"}}} +{"an":{"020161622X":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"The Pragmatic P...","full-label":"The Pragmatic Programmer: From Journeyman to Master","z":0,"y":195.48308,"x":-90.41004,"size":9.315789}}} +{"ae":{"020161622X_0735619670":{"g":0,"weight":1,"Weight":1,"b":0,"source":"020161622X","r":-1,"directed":false,"target":"0735619670"}}} +{"ae":{"0201657880_020161622X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201657880","r":-1,"directed":false,"target":"020161622X"}}} +{"ae":{"0201633612_020161622X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201633612","r":-1,"directed":false,"target":"020161622X"}}} +{"an":{"0387310738":{"g":0,"b":0,"r":1,"Label":"Pattern Recogni...","full-label":"Pattern Recognition and Machine Learning (Information Science and Statistics)","z":0,"y":-165.8724,"x":366.53897,"size":23}}} +{"ae":{"0137903952_0387310738":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0137903952","r":-1,"directed":false,"target":"0387310738"}}} +{"ae":{"0596529325_0387310738":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"0387310738"}}} +{"an":{"0071154671":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Machine Learnin...","full-label":"Machine Learning (Mcgraw-Hill International Edit)","z":0,"y":-253.31204,"x":288.01794,"size":8.263158}}} +{"ae":{"0137903952_0071154671":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0137903952","r":-1,"directed":false,"target":"0071154671"}}} +{"ae":{"0387310738_0071154671":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0071154671"}}} +{"an":{"0716710455":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Computers and I...","full-label":"Computers and Intractability: A Guide to the Theory of NP-Completeness (Series of Books in the Mathematical Sciences)","z":0,"y":-313.39703,"x":116.44965,"size":8.263158}}} +{"ae":{"0262032937_0716710455":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0716710455"}}} +{"ae":{"0534950973_0716710455":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0534950973","r":-1,"directed":false,"target":"0716710455"}}} +{"an":{"0321486811":{"g":0.98039216,"b":0,"r":0.02745098,"Label":"Compilers: Prin...","full-label":"Compilers: Principles, Techniques, and Tools (2nd Edition)","z":0,"y":-171.77571,"x":84.30129,"size":12.473684}}} +{"ae":{"0262032937_0321486811":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0321486811"}}} +{"ae":{"0137903952_0321486811":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0137903952","r":-1,"directed":false,"target":"0321486811"}}} +{"ae":{"0534950973_0321486811":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0534950973","r":-1,"directed":false,"target":"0321486811"}}} +{"ae":{"0596510047_0321486811":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510047","r":-1,"directed":false,"target":"0321486811"}}} +{"an":{"0321497708":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Computer Networ...","full-label":"Computer Networking: A Top-Down Approach (4th Edition)","z":0,"y":-198.29207,"x":145.42244,"size":7.2105265}}} +{"ae":{"0262032937_0321497708":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0321497708"}}} +{"ae":{"0137903952_0321497708":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0137903952","r":-1,"directed":false,"target":"0321497708"}}} +{"ae":{"0471694665_0321497708":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0471694665","r":-1,"directed":false,"target":"0321497708"}}} +{"ae":{"0123706068_0321497708":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0123706068","r":-1,"directed":false,"target":"0321497708"}}} +{"an":{"0123704901":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Computer Archit...","full-label":"Computer Architecture, Fourth Edition: A Quantitative Approach","z":0,"y":-86.75508,"x":36.36069,"size":7.2105265}}} +{"ae":{"0262032937_0123704901":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0123704901"}}} +{"ae":{"0471694665_0123704901":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0471694665","r":-1,"directed":false,"target":"0123704901"}}} +{"ae":{"0123706068_0123704901":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0123706068","r":-1,"directed":false,"target":"0123704901"}}} +{"ae":{"0321486811_0123704901":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321486811","r":-1,"directed":false,"target":"0123704901"}}} +{"an":{"0131103628":{"g":0.39215687,"b":0,"r":0.6117647,"Label":"C Programming L...","full-label":"C Programming Language (2nd Edition) (Prentice Hall Software)","z":0,"y":-272.45364,"x":-170.91354,"size":18.789474}}} +{"ae":{"0131103628_020161586X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131103628","r":-1,"directed":false,"target":"020161586X"}}} +{"ae":{"0123706068_0131103628":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0123706068","r":-1,"directed":false,"target":"0131103628"}}} +{"an":{"0201485672":{"g":0.98039216,"b":0,"r":0.02745098,"Label":"Refactoring: Im...","full-label":"Refactoring: Improving the Design of Existing Code (The Addison-Wesley Object Technology Series)","z":0,"y":322.49863,"x":-191.5915,"size":12.473684}}} +{"ae":{"0201485672_020161622X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201485672","r":-1,"directed":false,"target":"020161622X"}}} +{"ae":{"0201633612_0201485672":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201633612","r":-1,"directed":false,"target":"0201485672"}}} +{"ae":{"0735619670_0201485672":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735619670","r":-1,"directed":false,"target":"0201485672"}}} +{"an":{"0596007124":{"g":0.78431374,"b":0,"r":0.22352941,"Label":"Head First Desi...","full-label":"Head First Design Patterns (Head First)","z":0,"y":346.7765,"x":-293.65393,"size":14.578947}}} +{"ae":{"0201633612_0596007124":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201633612","r":-1,"directed":false,"target":"0596007124"}}} +{"an":{"0321334876":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Effective C++: ...","full-label":"Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) (Addison-Wesley Professional Computing Series)","z":0,"y":134.87206,"x":-274.0785,"size":8.263158}}} +{"ae":{"0201633612_0321334876":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201633612","r":-1,"directed":false,"target":"0321334876"}}} +{"an":{"0321193687":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"UML Distilled: ...","full-label":"UML Distilled: A Brief Guide to the Standard Object Modeling Language (3rd Edition) (The Addison-Wesley Object Technology Series)","z":0,"y":225.60315,"x":-244.27174,"size":7.2105265}}} +{"ae":{"0201633612_0321193687":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201633612","r":-1,"directed":false,"target":"0321193687"}}} +{"ae":{"0735619670_0321193687":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735619670","r":-1,"directed":false,"target":"0321193687"}}} +{"ae":{"0201485672_0321193687":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201485672","r":-1,"directed":false,"target":"0321193687"}}} +{"ae":{"0596007124_0321193687":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596007124","r":-1,"directed":false,"target":"0321193687"}}} +{"an":{"0470121688":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Puzzles for Pro...","full-label":"Puzzles for Programmers and Pros","z":0,"y":17.484829,"x":-285.94733,"size":8.263158}}} +{"ae":{"0470121688_047012167X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0470121688","r":-1,"directed":false,"target":"047012167X"}}} +{"ae":{"0201657880_0470121688":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201657880","r":-1,"directed":false,"target":"0470121688"}}} +{"ae":{"0387001638_0470121688":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387001638","r":-1,"directed":false,"target":"0470121688"}}} +{"an":{"0596529309":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"High Performanc...","full-label":"High Performance Web Sites: Essential Knowledge for Front-End Engineers","z":0,"y":190.36697,"x":267.57144,"size":9.315789}}} +{"ae":{"0596529325_0596529309":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"0596529309"}}} +{"ae":{"0596510047_0596529309":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510047","r":-1,"directed":false,"target":"0596529309"}}} +{"an":{"0596529260":{"g":0.8117647,"b":0.20784314,"r":0.011764706,"Label":"RESTful Web Ser...","full-label":"RESTful Web Services","z":0,"y":164.22902,"x":313.20026,"size":10.368422}}} +{"ae":{"0596529260_0596510047":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529260","r":-1,"directed":false,"target":"0596510047"}}} +{"ae":{"0596529260_0596529309":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529260","r":-1,"directed":false,"target":"0596529309"}}} +{"ae":{"0596529325_0596529260":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"0596529260"}}} +{"an":{"0596102356":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Building Scalab...","full-label":"Building Scalable Web Sites: Building, scaling, and optimizing the next generation of web applications","z":0,"y":202.51845,"x":359.38724,"size":8.263158}}} +{"ae":{"0596102356_0596529309":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596102356","r":-1,"directed":false,"target":"0596529309"}}} +{"ae":{"0596102356_0596529260":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596102356","r":-1,"directed":false,"target":"0596529260"}}} +{"ae":{"0596529325_0596102356":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"0596102356"}}} +{"an":{"0596514557":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Visualizing Dat...","full-label":"Visualizing Data","z":0,"y":275.8018,"x":318.99237,"size":7.2105265}}} +{"ae":{"0596529325_0596514557":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"0596514557"}}} +{"an":{"1400082471":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Dreaming in Cod...","full-label":"Dreaming in Code: Two Dozen Programmers, Three Years, 4,732 Bugs, and One Quest for Transcendent Software","z":0,"y":202.40594,"x":40.694023,"size":7.2105265}}} +{"ae":{"0596510047_1400082471":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510047","r":-1,"directed":false,"target":"1400082471"}}} +{"an":{"193435600X":{"g":0.8117647,"b":0.20784314,"r":0.011764706,"Label":"Programming Erl...","full-label":"Programming Erlang: Software for a Concurrent World","z":0,"y":96.31309,"x":204.31776,"size":10.368422}}} +{"ae":{"0596529325_193435600X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"193435600X"}}} +{"ae":{"0596510047_193435600X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510047","r":-1,"directed":false,"target":"193435600X"}}} +{"ae":{"0596529260_193435600X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529260","r":-1,"directed":false,"target":"193435600X"}}} +{"an":{"159059844X":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Managing Humans...","full-label":"Managing Humans: Biting and Humorous Tales of a Software Engineering Manager","z":0,"y":169.84491,"x":82.74668,"size":7.2105265}}} +{"ae":{"159059844X_1400082471":{"g":0,"weight":1,"Weight":1,"b":0,"source":"159059844X","r":-1,"directed":false,"target":"1400082471"}}} +{"ae":{"0596510047_159059844X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510047","r":-1,"directed":false,"target":"159059844X"}}} +{"an":{"B000JBY0RY":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"How Would You M...","full-label":"How Would You Move Mount Fuji? Microsoft's Cult of the Puzzle - How the World's Smartest Company Selects the Most Creative Thinkers","z":0,"y":-54.23156,"x":-287.6692,"size":7.2105265}}} +{"ae":{"B000JBY0RY_0470121688":{"g":0,"weight":1,"Weight":1,"b":0,"source":"B000JBY0RY","r":-1,"directed":false,"target":"0470121688"}}} +{"ae":{"0201657880_B000JBY0RY":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201657880","r":-1,"directed":false,"target":"B000JBY0RY"}}} +{"ae":{"047012167X_B000JBY0RY":{"g":0,"weight":1,"Weight":1,"b":0,"source":"047012167X","r":-1,"directed":false,"target":"B000JBY0RY"}}} +{"an":{"B000ESSSN4":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"How Would You M...","full-label":"How Would You Move Mount Fuji? : Microsoft's Cult of the Puzzle -- How the World's Smartest Companies Select the Most Creative Thinkers","z":0,"y":-99.02864,"x":-334.81476,"size":8.263158}}} +{"ae":{"B000ESSSN4_0470121688":{"g":0,"weight":1,"Weight":1,"b":0,"source":"B000ESSSN4","r":-1,"directed":false,"target":"0470121688"}}} +{"ae":{"0201657880_B000ESSSN4":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201657880","r":-1,"directed":false,"target":"B000ESSSN4"}}} +{"ae":{"047012167X_B000ESSSN4":{"g":0,"weight":1,"Weight":1,"b":0,"source":"047012167X","r":-1,"directed":false,"target":"B000ESSSN4"}}} +{"an":{"0071495789":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Ace the IT Inte...","full-label":"Ace the IT Interview (Ace the It Job Interview)","z":0,"y":88.461624,"x":-449.74078,"size":8.263158}}} +{"ae":{"047012167X_0071495789":{"g":0,"weight":1,"Weight":1,"b":0,"source":"047012167X","r":-1,"directed":false,"target":"0071495789"}}} +{"an":{"1556159005":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Rapid Developme...","full-label":"Rapid Development: Taming Wild Software Schedules","z":0,"y":281.07138,"x":-59.837284,"size":7.2105265}}} +{"ae":{"0735619670_1556159005":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735619670","r":-1,"directed":false,"target":"1556159005"}}} +{"an":{"0735605351":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"Software Estima...","full-label":"Software Estimation: Demystifying the Black Art (Best Practices (Microsoft))","z":0,"y":382.09418,"x":-34.364437,"size":9.315789}}} +{"ae":{"0735605351_1556159005":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735605351","r":-1,"directed":false,"target":"1556159005"}}} +{"ae":{"0735619670_0735605351":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735619670","r":-1,"directed":false,"target":"0735605351"}}} +{"an":{"0201835959":{"g":0.9098039,"b":0.09411765,"r":0.011764706,"Label":"The Mythical Ma...","full-label":"The Mythical Man-Month: Essays on Software Engineering, Anniversary Edition (2nd Edition)","z":0,"y":218.55168,"x":-31.115849,"size":11.421053}}} +{"ae":{"0201835959_020161622X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201835959","r":-1,"directed":false,"target":"020161622X"}}} +{"ae":{"0201835959_1400082471":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201835959","r":-1,"directed":false,"target":"1400082471"}}} +{"ae":{"0201835959_1556159005":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201835959","r":-1,"directed":false,"target":"1556159005"}}} +{"ae":{"0201633612_0201835959":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201633612","r":-1,"directed":false,"target":"0201835959"}}} +{"ae":{"0735619670_0201835959":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735619670","r":-1,"directed":false,"target":"0201835959"}}} +{"an":{"013937681X":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Unix Programmin...","full-label":"Unix Programming Environment (Prentice-Hall Software Series)","z":0,"y":-288.6967,"x":-111.807106,"size":8.263158}}} +{"ae":{"013937681X_0131103628":{"g":0,"weight":1,"Weight":1,"b":0,"source":"013937681X","r":-1,"directed":false,"target":"0131103628"}}} +{"ae":{"020161586X_013937681X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"020161586X","r":-1,"directed":false,"target":"013937681X"}}} +{"an":{"0932633439":{"g":0.9098039,"b":0.09411765,"r":0.011764706,"Label":"Peopleware: Pro...","full-label":"Peopleware: Productive Projects and Teams (Second Edition)","z":0,"y":282.74866,"x":0.5499332,"size":11.421053}}} +{"ae":{"0932633439_1556159005":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0932633439","r":-1,"directed":false,"target":"1556159005"}}} +{"ae":{"0932633439_0735605351":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0932633439","r":-1,"directed":false,"target":"0735605351"}}} +{"ae":{"0932633439_0201835959":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0932633439","r":-1,"directed":false,"target":"0201835959"}}} +{"ae":{"0735619670_0932633439":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735619670","r":-1,"directed":false,"target":"0932633439"}}} +{"ae":{"020161622X_0932633439":{"g":0,"weight":1,"Weight":1,"b":0,"source":"020161622X","r":-1,"directed":false,"target":"0932633439"}}} +{"an":{"0387952845":{"g":0.29411766,"b":0,"r":0.7058824,"Label":"The Elements of...","full-label":"The Elements of Statistical Learning","z":0,"y":-182.26845,"x":424.54086,"size":19.842106}}} +{"ae":{"0387952845_0071154671":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387952845","r":-1,"directed":false,"target":"0071154671"}}} +{"ae":{"0387310738_0387952845":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0387952845"}}} +{"an":{"0471056693":{"g":0.9098039,"b":0.09411765,"r":0.011764706,"Label":"Pattern Classif...","full-label":"Pattern Classification (2nd Edition)","z":0,"y":-293.6187,"x":364.29922,"size":11.421053}}} +{"ae":{"0471056693_0071154671":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0471056693","r":-1,"directed":false,"target":"0071154671"}}} +{"ae":{"0471056693_0387952845":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0471056693","r":-1,"directed":false,"target":"0387952845"}}} +{"ae":{"0387310738_0471056693":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0471056693"}}} +{"an":{"0120884070":{"g":0.9098039,"b":0.09411765,"r":0.011764706,"Label":"Data Mining: Pr...","full-label":"Data Mining: Practical Machine Learning Tools and Techniques, Second Edition (Morgan Kaufmann Series in Data Management Systems)","z":0,"y":-145.25755,"x":303.92606,"size":11.421053}}} +{"ae":{"0120884070_0071154671":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0120884070","r":-1,"directed":false,"target":"0071154671"}}} +{"ae":{"0120884070_0387952845":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0120884070","r":-1,"directed":false,"target":"0387952845"}}} +{"ae":{"0120884070_0471056693":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0120884070","r":-1,"directed":false,"target":"0471056693"}}} +{"ae":{"0596529325_0120884070":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"0120884070"}}} +{"ae":{"0387310738_0120884070":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0120884070"}}} +{"an":{"026218253X":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Gaussian Proces...","full-label":"Gaussian Processes for Machine Learning (Adaptive Computation and Machine Learning)","z":0,"y":-58.50053,"x":410.89618,"size":7.2105265}}} +{"ae":{"0387310738_026218253X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"026218253X"}}} +{"ae":{"0387952845_026218253X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387952845","r":-1,"directed":false,"target":"026218253X"}}} +{"an":{"0486402584":{"g":0.9098039,"b":0.09411765,"r":0.011764706,"Label":"Combinatorial O...","full-label":"Combinatorial Optimization: Algorithms and Complexity","z":0,"y":-417.48282,"x":107.62506,"size":11.421053}}} +{"ae":{"0262032937_0486402584":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0486402584"}}} +{"ae":{"0716710455_0486402584":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0716710455","r":-1,"directed":false,"target":"0486402584"}}} +{"an":{"3540653678":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Approximation A...","full-label":"Approximation Algorithms","z":0,"y":-367.37573,"x":152.63393,"size":8.263158}}} +{"ae":{"3540653678_0486402584":{"g":0,"weight":1,"Weight":1,"b":0,"source":"3540653678","r":-1,"directed":false,"target":"0486402584"}}} +{"ae":{"0716710455_3540653678":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0716710455","r":-1,"directed":false,"target":"3540653678"}}} +{"an":{"0201530821":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Computational C...","full-label":"Computational Complexity","z":0,"y":-300.43005,"x":53.209324,"size":7.2105265}}} +{"ae":{"0262032937_0201530821":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0201530821"}}} +{"ae":{"0534950973_0201530821":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0534950973","r":-1,"directed":false,"target":"0201530821"}}} +{"ae":{"0716710455_0201530821":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0716710455","r":-1,"directed":false,"target":"0201530821"}}} +{"ae":{"0486402584_0201530821":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486402584","r":-1,"directed":false,"target":"0201530821"}}} +{"ae":{"3540653678_0201530821":{"g":0,"weight":1,"Weight":1,"b":0,"source":"3540653678","r":-1,"directed":false,"target":"0201530821"}}} +{"an":{"B00007FYCY":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Lex & Yacc","full-label":"Lex & Yacc","z":0,"y":-320.48438,"x":-4.830319,"size":8.263158}}} +{"ae":{"0262032937_B00007FYCY":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"B00007FYCY"}}} +{"ae":{"0321486811_B00007FYCY":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321486811","r":-1,"directed":false,"target":"B00007FYCY"}}} +{"an":{"0126339511":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Programming Lan...","full-label":"Programming Language Pragmatics, Second Edition","z":0,"y":-99.99868,"x":101.63591,"size":8.263158}}} +{"ae":{"0262032937_0126339511":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0126339511"}}} +{"ae":{"0534950973_0126339511":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0534950973","r":-1,"directed":false,"target":"0126339511"}}} +{"ae":{"0596510047_0126339511":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510047","r":-1,"directed":false,"target":"0126339511"}}} +{"ae":{"0321486811_0126339511":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321486811","r":-1,"directed":false,"target":"0126339511"}}} +{"an":{"0321369572":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Fundamentals of...","full-label":"Fundamentals of Database Systems (5th Edition)","z":0,"y":-284.6423,"x":213.42932,"size":7.2105265}}} +{"ae":{"0262032937_0321369572":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0321369572"}}} +{"ae":{"0137903952_0321369572":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0137903952","r":-1,"directed":false,"target":"0321369572"}}} +{"ae":{"0471694665_0321369572":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0471694665","r":-1,"directed":false,"target":"0321369572"}}} +{"ae":{"0321497708_0321369572":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321497708","r":-1,"directed":false,"target":"0321369572"}}} +{"an":{"1593271042":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Inside the Mach...","full-label":"Inside the Machine: An Illustrated Introduction to Microprocessors and Computer Architecture","z":0,"y":-21.890327,"x":64.841125,"size":7.2105265}}} +{"ae":{"0123706068_1593271042":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0123706068","r":-1,"directed":false,"target":"1593271042"}}} +{"ae":{"0596510047_1593271042":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510047","r":-1,"directed":false,"target":"1593271042"}}} +{"ae":{"0123704901_1593271042":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0123704901","r":-1,"directed":false,"target":"1593271042"}}} +{"an":{"0201700735":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"The C++ Program...","full-label":"The C++ Programming Language: Special Edition (3rd Edition)","z":0,"y":-4.791209,"x":-223.76816,"size":9.315789}}} +{"ae":{"0201700735_0321334876":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201700735","r":-1,"directed":false,"target":"0321334876"}}} +{"ae":{"0201633612_0201700735":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201633612","r":-1,"directed":false,"target":"0201700735"}}} +{"ae":{"0131103628_0201700735":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131103628","r":-1,"directed":false,"target":"0201700735"}}} +{"an":{"013089592X":{"g":0.8117647,"b":0.20784314,"r":0.011764706,"Label":"C: A Reference ...","full-label":"C: A Reference Manual (5th Edition)","z":0,"y":-343.39307,"x":-118.30886,"size":10.368422}}} +{"ae":{"013089592X_B00007FYCY":{"g":0,"weight":1,"Weight":1,"b":0,"source":"013089592X","r":-1,"directed":false,"target":"B00007FYCY"}}} +{"ae":{"0131103628_013089592X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131103628","r":-1,"directed":false,"target":"013089592X"}}} +{"an":{"0672305100":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Absolute Beginn...","full-label":"Absolute Beginner's Guide to C (2nd Edition) (Other Sams)","z":0,"y":-222.27028,"x":-240.80327,"size":7.2105265}}} +{"ae":{"0131103628_0672305100":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131103628","r":-1,"directed":false,"target":"0672305100"}}} +{"an":{"0201433079":{"g":0.9098039,"b":0.09411765,"r":0.011764706,"Label":"Advanced Progra...","full-label":"Advanced Programming in the UNIX(R) Environment (2nd Edition) (Addison-Wesley Professional Computing Series)","z":0,"y":-467.9772,"x":-120.6775,"size":11.421053}}} +{"ae":{"0201433079_013937681X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201433079","r":-1,"directed":false,"target":"013937681X"}}} +{"ae":{"0201433079_013089592X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201433079","r":-1,"directed":false,"target":"013089592X"}}} +{"ae":{"0131103628_0201433079":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131103628","r":-1,"directed":false,"target":"0201433079"}}} +{"an":{"0321146530":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Test Driven Dev...","full-label":"Test Driven Development: By Example (The Addison-Wesley Signature Series)","z":0,"y":483.2812,"x":-147.84818,"size":8.263158}}} +{"ae":{"0201485672_0321146530":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201485672","r":-1,"directed":false,"target":"0321146530"}}} +{"an":{"0321127420":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Patterns of Ent...","full-label":"Patterns of Enterprise Application Architecture (The Addison-Wesley Signature Series)","z":0,"y":296.49377,"x":-257.82138,"size":7.2105265}}} +{"ae":{"0201633612_0321127420":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201633612","r":-1,"directed":false,"target":"0321127420"}}} +{"ae":{"0201485672_0321127420":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201485672","r":-1,"directed":false,"target":"0321127420"}}} +{"ae":{"0596007124_0321127420":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596007124","r":-1,"directed":false,"target":"0321127420"}}} +{"an":{"0596008678":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Head First Obje...","full-label":"Head First Object-Oriented Analysis and Design (Head First)","z":0,"y":371.8657,"x":-396.34882,"size":8.263158}}} +{"ae":{"0596007124_0596008678":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596007124","r":-1,"directed":false,"target":"0596008678"}}} +{"an":{"0596009208":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"Head First Java...","full-label":"Head First Java, 2nd Edition","z":0,"y":412.6984,"x":-351.83282,"size":9.315789}}} +{"ae":{"0596009208_0596008678":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596009208","r":-1,"directed":false,"target":"0596008678"}}} +{"ae":{"0596007124_0596009208":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596007124","r":-1,"directed":false,"target":"0596009208"}}} +{"an":{"0596516681":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Head First Serv...","full-label":"Head First Servlets and JSP: Passing the Sun Certified Web Component Developer Exam (Brain-Friendly Guides)","z":0,"y":472.70743,"x":-331.12637,"size":7.2105265}}} +{"ae":{"0596516681_0596009208":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596516681","r":-1,"directed":false,"target":"0596009208"}}} +{"ae":{"0596007124_0596516681":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596007124","r":-1,"directed":false,"target":"0596516681"}}} +{"an":{"0596102143":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Head First Desi...","full-label":"Head First Design Patterns Poster (Head First)","z":0,"y":331.00754,"x":-352.6951,"size":7.2105265}}} +{"ae":{"0596102143_0596008678":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596102143","r":-1,"directed":false,"target":"0596008678"}}} +{"ae":{"0596007124_0596102143":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596007124","r":-1,"directed":false,"target":"0596102143"}}} +{"ae":{"0596009208_0596102143":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596009208","r":-1,"directed":false,"target":"0596102143"}}} +{"an":{"020163371X":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"More Effective ...","full-label":"More Effective C++: 35 New Ways to Improve Your Programs and Designs (Addison-Wesley Professional Computing Series)","z":0,"y":79.82843,"x":-169.72,"size":7.2105265}}} +{"ae":{"020163371X_0201700735":{"g":0,"weight":1,"Weight":1,"b":0,"source":"020163371X","r":-1,"directed":false,"target":"0201700735"}}} +{"ae":{"0201633612_020163371X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201633612","r":-1,"directed":false,"target":"020163371X"}}} +{"ae":{"0321334876_020163371X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321334876","r":-1,"directed":false,"target":"020163371X"}}} +{"an":{"0201749629":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Effective STL: ...","full-label":"Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library (Addison-Wesley Professional Computing Series)","z":0,"y":147.41339,"x":-222.33926,"size":8.263158}}} +{"ae":{"0201749629_020163371X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201749629","r":-1,"directed":false,"target":"020163371X"}}} +{"ae":{"0201633612_0201749629":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201633612","r":-1,"directed":false,"target":"0201749629"}}} +{"ae":{"0321334876_0201749629":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321334876","r":-1,"directed":false,"target":"0201749629"}}} +{"ae":{"0201700735_0201749629":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201700735","r":-1,"directed":false,"target":"0201749629"}}} +{"an":{"0201379260":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"The C++ Standar...","full-label":"The C++ Standard Library: A Tutorial and Reference","z":0,"y":75.26161,"x":-237.36363,"size":8.263158}}} +{"ae":{"0201379260_0201700735":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201379260","r":-1,"directed":false,"target":"0201700735"}}} +{"ae":{"0201379260_020163371X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201379260","r":-1,"directed":false,"target":"020163371X"}}} +{"ae":{"0201379260_0201749629":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201379260","r":-1,"directed":false,"target":"0201749629"}}} +{"ae":{"0201633612_0201379260":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201633612","r":-1,"directed":false,"target":"0201379260"}}} +{"ae":{"0321334876_0201379260":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321334876","r":-1,"directed":false,"target":"0201379260"}}} +{"an":{"0131489062":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Applying UML an...","full-label":"Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development (3rd Edition)","z":0,"y":262.75085,"x":-187.8554,"size":7.2105265}}} +{"ae":{"0201633612_0131489062":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201633612","r":-1,"directed":false,"target":"0131489062"}}} +{"ae":{"0201485672_0131489062":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201485672","r":-1,"directed":false,"target":"0131489062"}}} +{"ae":{"0596007124_0131489062":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596007124","r":-1,"directed":false,"target":"0131489062"}}} +{"ae":{"0321193687_0131489062":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321193687","r":-1,"directed":false,"target":"0131489062"}}} +{"an":{"0486281523":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"My Best Mathema...","full-label":"My Best Mathematical and Logic Puzzles (Math & Logic Puzzles)","z":0,"y":190.01703,"x":-334.12128,"size":8.263158}}} +{"ae":{"0470121688_0486281523":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0470121688","r":-1,"directed":false,"target":"0486281523"}}} +{"an":{"0596528108":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Designing Web N...","full-label":"Designing Web Navigation: Optimizing the User Experience","z":0,"y":322.48364,"x":187.15059,"size":7.2105265}}} +{"ae":{"0596529325_0596528108":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"0596528108"}}} +{"ae":{"0596529309_0596528108":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529309","r":-1,"directed":false,"target":"0596528108"}}} +{"an":{"0977616630":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Agile Web Devel...","full-label":"Agile Web Development with Rails, 2nd Edition","z":0,"y":273.37378,"x":517.7288,"size":8.263158}}} +{"ae":{"0596529260_0977616630":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529260","r":-1,"directed":false,"target":"0977616630"}}} +{"an":{"067232699X":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Scalable Intern...","full-label":"Scalable Internet Architectures (Developer's Library)","z":0,"y":124.58404,"x":361.9445,"size":7.2105265}}} +{"ae":{"0596529325_067232699X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"067232699X"}}} +{"ae":{"0596529309_067232699X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529309","r":-1,"directed":false,"target":"067232699X"}}} +{"ae":{"0596529260_067232699X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529260","r":-1,"directed":false,"target":"067232699X"}}} +{"ae":{"0596102356_067232699X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596102356","r":-1,"directed":false,"target":"067232699X"}}} +{"an":{"0596003064":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"High Performanc...","full-label":"High Performance MySQL","z":0,"y":178.50078,"x":426.98447,"size":8.263158}}} +{"ae":{"0596003064_067232699X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596003064","r":-1,"directed":false,"target":"067232699X"}}} +{"ae":{"0596529309_0596003064":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529309","r":-1,"directed":false,"target":"0596003064"}}} +{"ae":{"0596102356_0596003064":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596102356","r":-1,"directed":false,"target":"0596003064"}}} +{"an":{"0262182629":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Processing: A P...","full-label":"Processing: A Programming Handbook for Visual Designers and Artists","z":0,"y":267.12283,"x":385.24347,"size":8.263158}}} +{"ae":{"0596529325_0262182629":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"0262182629"}}} +{"ae":{"0596514557_0262182629":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596514557","r":-1,"directed":false,"target":"0262182629"}}} +{"an":{"159059617X":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Processing: Cre...","full-label":"Processing: Creative Coding and Computational Art (Foundation)","z":0,"y":337.4568,"x":371.16635,"size":7.2105265}}} +{"ae":{"159059617X_0262182629":{"g":0,"weight":1,"Weight":1,"b":0,"source":"159059617X","r":-1,"directed":false,"target":"0262182629"}}} +{"ae":{"0596514557_159059617X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596514557","r":-1,"directed":false,"target":"159059617X"}}} +{"an":{"0596510519":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Making Things T...","full-label":"Making Things Talk: Practical Methods for Connecting Physical Objects","z":0,"y":323.12106,"x":437.71143,"size":7.2105265}}} +{"ae":{"0596510519_0262182629":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510519","r":-1,"directed":false,"target":"0262182629"}}} +{"ae":{"0596510519_159059617X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510519","r":-1,"directed":false,"target":"159059617X"}}} +{"ae":{"0596514557_0596510519":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596514557","r":-1,"directed":false,"target":"0596510519"}}} +{"an":{"0321525655":{"g":0.8117647,"b":0.20784314,"r":0.011764706,"Label":"Presentation Ze...","full-label":"Presentation Zen: Simple Ideas on Presentation Design and Delivery (Voices That Matter)","z":0,"y":429.93442,"x":294.08777,"size":10.368422}}} +{"ae":{"0596514557_0321525655":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596514557","r":-1,"directed":false,"target":"0321525655"}}} +{"an":{"1590597141":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"Founders at Wor...","full-label":"Founders at Work: Stories of Startups' Early Days","z":0,"y":232.39876,"x":158.10092,"size":9.315789}}} +{"ae":{"1590597141_159059844X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1590597141","r":-1,"directed":false,"target":"159059844X"}}} +{"ae":{"0596529325_1590597141":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"1590597141"}}} +{"ae":{"0596510047_1590597141":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510047","r":-1,"directed":false,"target":"1590597141"}}} +{"ae":{"1400082471_1590597141":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1400082471","r":-1,"directed":false,"target":"1590597141"}}} +{"an":{"1590598385":{"g":0.8117647,"b":0.20784314,"r":0.011764706,"Label":"Smart and Gets ...","full-label":"Smart and Gets Things Done: Joel Spolsky's Concise Guide to Finding the Best Technical Talent","z":0,"y":255.37,"x":92.88532,"size":10.368422}}} +{"ae":{"1590598385_159059844X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1590598385","r":-1,"directed":false,"target":"159059844X"}}} +{"ae":{"0596510047_1590598385":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510047","r":-1,"directed":false,"target":"1590598385"}}} +{"ae":{"1400082471_1590598385":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1400082471","r":-1,"directed":false,"target":"1590598385"}}} +{"ae":{"0932633439_1590598385":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0932633439","r":-1,"directed":false,"target":"1590598385"}}} +{"ae":{"1590597141_1590598385":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1590597141","r":-1,"directed":false,"target":"1590598385"}}} +{"an":{"0978739256":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"The Definitive ...","full-label":"The Definitive ANTLR Reference: Building Domain-Specific Languages (Pragmatic Programmers)","z":0,"y":95.239624,"x":299.86188,"size":7.2105265}}} +{"ae":{"0596529325_0978739256":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"0978739256"}}} +{"ae":{"0596510047_0978739256":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510047","r":-1,"directed":false,"target":"0978739256"}}} +{"ae":{"0596529260_0978739256":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529260","r":-1,"directed":false,"target":"0978739256"}}} +{"ae":{"193435600X_0978739256":{"g":0,"weight":1,"Weight":1,"b":0,"source":"193435600X","r":-1,"directed":false,"target":"0978739256"}}} +{"an":{"0521692695":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Programming in ...","full-label":"Programming in Haskell","z":0,"y":31.118309,"x":139.6912,"size":7.2105265}}} +{"ae":{"0596510047_0521692695":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510047","r":-1,"directed":false,"target":"0521692695"}}} +{"ae":{"193435600X_0521692695":{"g":0,"weight":1,"Weight":1,"b":0,"source":"193435600X","r":-1,"directed":false,"target":"0521692695"}}} +{"an":{"0596527055":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"The Myths of In...","full-label":"The Myths of Innovation","z":0,"y":235.85454,"x":218.11624,"size":7.2105265}}} +{"ae":{"0596527055_1590597141":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596527055","r":-1,"directed":false,"target":"1590597141"}}} +{"ae":{"0596529325_0596527055":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"0596527055"}}} +{"ae":{"0596510047_0596527055":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510047","r":-1,"directed":false,"target":"0596527055"}}} +{"ae":{"159059844X_0596527055":{"g":0,"weight":1,"Weight":1,"b":0,"source":"159059844X","r":-1,"directed":false,"target":"0596527055"}}} +{"ae":{"0321525655_0596527055":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321525655","r":-1,"directed":false,"target":"0596527055"}}} +{"an":{"0071440011":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"How to Ace the ...","full-label":"How to Ace the Brainteaser Interview","z":0,"y":61.26506,"x":-392.18372,"size":7.2105265}}} +{"ae":{"0071440011_B000ESSSN4":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071440011","r":-1,"directed":false,"target":"B000ESSSN4"}}} +{"ae":{"047012167X_0071440011":{"g":0,"weight":1,"Weight":1,"b":0,"source":"047012167X","r":-1,"directed":false,"target":"0071440011"}}} +{"ae":{"B000JBY0RY_0071440011":{"g":0,"weight":1,"Weight":1,"b":0,"source":"B000JBY0RY","r":-1,"directed":false,"target":"0071440011"}}} +{"an":{"0385242719":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Labyrinths of R...","full-label":"Labyrinths of Reason: Paradox, Puzzles, and the Frailty of Knowledge","z":0,"y":-115.56172,"x":-403.97394,"size":7.2105265}}} +{"ae":{"0385242719_B000ESSSN4":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0385242719","r":-1,"directed":false,"target":"B000ESSSN4"}}} +{"ae":{"B000JBY0RY_0385242719":{"g":0,"weight":1,"Weight":1,"b":0,"source":"B000JBY0RY","r":-1,"directed":false,"target":"0385242719"}}} +{"an":{"0970055269":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Heard on the St...","full-label":"Heard on the Street: Quantitative Questions from Wall Street Job Interviews","z":0,"y":-357.77887,"x":-424.7851,"size":8.263158}}} +{"ae":{"B000ESSSN4_0970055269":{"g":0,"weight":1,"Weight":1,"b":0,"source":"B000ESSSN4","r":-1,"directed":false,"target":"0970055269"}}} +{"an":{"0071492747":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"ACE the IT Resu...","full-label":"ACE the IT Resume (Ace the It Resume)","z":0,"y":-34.421734,"x":-467.671,"size":7.2105265}}} +{"ae":{"047012167X_0071492747":{"g":0,"weight":1,"Weight":1,"b":0,"source":"047012167X","r":-1,"directed":false,"target":"0071492747"}}} +{"ae":{"0071495789_0071492747":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071495789","r":-1,"directed":false,"target":"0071492747"}}} +{"an":{"0071425470":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Boost Your Inte...","full-label":"Boost Your Interview IQ","z":0,"y":161.65135,"x":-499.7812,"size":7.2105265}}} +{"ae":{"0071495789_0071425470":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071495789","r":-1,"directed":false,"target":"0071425470"}}} +{"an":{"0071387730":{"g":0.88235295,"b":0,"r":0.1254902,"Label":"201 Best Questi...","full-label":"201 Best Questions To Ask On Your Interview","z":0,"y":100.52775,"x":-527.5357,"size":13.526316}}} +{"ae":{"0071387730_0071440011":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071387730","r":-1,"directed":false,"target":"0071440011"}}} +{"ae":{"0071495789_0071387730":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071495789","r":-1,"directed":false,"target":"0071387730"}}} +{"an":{"1402203853":{"g":0.88235295,"b":0,"r":0.1254902,"Label":"301 Smart Answe...","full-label":"301 Smart Answers to Tough Interview Questions","z":0,"y":109.55526,"x":-579.4041,"size":13.526316}}} +{"ae":{"1402203853_0071425470":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1402203853","r":-1,"directed":false,"target":"0071425470"}}} +{"ae":{"1402203853_0071387730":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1402203853","r":-1,"directed":false,"target":"0071387730"}}} +{"ae":{"0071495789_1402203853":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071495789","r":-1,"directed":false,"target":"1402203853"}}} +{"an":{"1572316217":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Software Projec...","full-label":"Software Project Survival Guide (Pro -- Best Practices)","z":0,"y":281.30356,"x":-116.386375,"size":7.2105265}}} +{"ae":{"0735619670_1572316217":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735619670","r":-1,"directed":false,"target":"1572316217"}}} +{"ae":{"1556159005_1572316217":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1556159005","r":-1,"directed":false,"target":"1572316217"}}} +{"ae":{"0735605351_1572316217":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735605351","r":-1,"directed":false,"target":"1572316217"}}} +{"ae":{"0201835959_1572316217":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201835959","r":-1,"directed":false,"target":"1572316217"}}} +{"an":{"0735618798":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Software Requir...","full-label":"Software Requirements, Second Edition (Pro-Best Practices)","z":0,"y":356.78516,"x":-78.44975,"size":8.263158}}} +{"ae":{"0735618798_1572316217":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735618798","r":-1,"directed":false,"target":"1572316217"}}} +{"ae":{"0735619670_0735618798":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735619670","r":-1,"directed":false,"target":"0735618798"}}} +{"ae":{"0735605351_0735618798":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735605351","r":-1,"directed":false,"target":"0735618798"}}} +{"an":{"0131479415":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"Agile Estimatin...","full-label":"Agile Estimating and Planning (Robert C. Martin Series)","z":0,"y":588.9256,"x":-80.954636,"size":9.315789}}} +{"ae":{"0131479415_0321146530":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131479415","r":-1,"directed":false,"target":"0321146530"}}} +{"ae":{"0735605351_0131479415":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735605351","r":-1,"directed":false,"target":"0131479415"}}} +{"an":{"0132017997":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Design of the U...","full-label":"Design of the UNIX Operating System (Prentice Hall Software Series)","z":0,"y":-401.78207,"x":-104.634514,"size":8.263158}}} +{"ae":{"0131103628_0132017997":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131103628","r":-1,"directed":false,"target":"0132017997"}}} +{"ae":{"013937681X_0132017997":{"g":0,"weight":1,"Weight":1,"b":0,"source":"013937681X","r":-1,"directed":false,"target":"0132017997"}}} +{"ae":{"0201433079_0132017997":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201433079","r":-1,"directed":false,"target":"0132017997"}}} +{"an":{"020103669X":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Software Tools","full-label":"Software Tools","z":0,"y":-250.8101,"x":-77.41463,"size":7.2105265}}} +{"ae":{"020161586X_020103669X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"020161586X","r":-1,"directed":false,"target":"020103669X"}}} +{"ae":{"0131103628_020103669X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131103628","r":-1,"directed":false,"target":"020103669X"}}} +{"ae":{"013937681X_020103669X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"013937681X","r":-1,"directed":false,"target":"020103669X"}}} +{"ae":{"0132017997_020103669X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0132017997","r":-1,"directed":false,"target":"020103669X"}}} +{"an":{"1590593898":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Joel on Softwar...","full-label":"Joel on Software: And on Diverse and Occasionally Related Matters That Will Prove of Interest to Software Developers, Designers, and Managers, and to Those Who, Whether by Good Fortune or Ill Luck, Work with Them in Some Capacity","z":0,"y":309.4759,"x":67.12211,"size":7.2105265}}} +{"ae":{"1590593898_1590598385":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1590593898","r":-1,"directed":false,"target":"1590598385"}}} +{"ae":{"0201835959_1590593898":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201835959","r":-1,"directed":false,"target":"1590593898"}}} +{"ae":{"0932633439_1590593898":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0932633439","r":-1,"directed":false,"target":"1590593898"}}} +{"an":{"0387402721":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"All of Statisti...","full-label":"All of Statistics: A Concise Course in Statistical Inference (Springer Texts in Statistics)","z":0,"y":-269.80087,"x":499.66898,"size":7.2105265}}} +{"ae":{"0387310738_0387402721":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0387402721"}}} +{"ae":{"0387952845_0387402721":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387952845","r":-1,"directed":false,"target":"0387402721"}}} +{"an":{"158488388X":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Bayesian Data A...","full-label":"Bayesian Data Analysis, Second Edition (Texts in Statistical Science)","z":0,"y":-168.38905,"x":566.4374,"size":7.2105265}}} +{"ae":{"0387952845_158488388X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387952845","r":-1,"directed":false,"target":"158488388X"}}} +{"an":{"0471429775":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Computer Manual...","full-label":"Computer Manual in MATLAB to Accompany Pattern Classification, Second Edition","z":0,"y":-307.7294,"x":439.2209,"size":7.2105265}}} +{"ae":{"0387310738_0471429775":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0471429775"}}} +{"ae":{"0387952845_0471429775":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387952845","r":-1,"directed":false,"target":"0471429775"}}} +{"ae":{"0471056693_0471429775":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0471056693","r":-1,"directed":false,"target":"0471429775"}}} +{"an":{"1558609016":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Data Mining: Co...","full-label":"Data Mining: Concepts and Techniques, Second Edition (The Morgan Kaufmann Series in Data Management Systems)","z":0,"y":-185.11247,"x":267.87823,"size":7.2105265}}} +{"ae":{"0596529325_1558609016":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"1558609016"}}} +{"ae":{"0387952845_1558609016":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387952845","r":-1,"directed":false,"target":"1558609016"}}} +{"ae":{"0120884070_1558609016":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0120884070","r":-1,"directed":false,"target":"1558609016"}}} +{"an":{"0262012111":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Introduction to...","full-label":"Introduction to Machine Learning (Adaptive Computation and Machine Learning)","z":0,"y":-234.92833,"x":343.26334,"size":7.2105265}}} +{"ae":{"0387310738_0262012111":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0262012111"}}} +{"ae":{"0071154671_0262012111":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071154671","r":-1,"directed":false,"target":"0262012111"}}} +{"ae":{"0387952845_0262012111":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387952845","r":-1,"directed":false,"target":"0262012111"}}} +{"ae":{"0471056693_0262012111":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0471056693","r":-1,"directed":false,"target":"0262012111"}}} +{"ae":{"0120884070_0262012111":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0120884070","r":-1,"directed":false,"target":"0262012111"}}} +{"an":{"0262194759":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Learning with K...","full-label":"Learning with Kernels: Support Vector Machines, Regularization, Optimization, and Beyond (Adaptive Computation and Machine Learning)","z":0,"y":-116.76352,"x":432.83817,"size":8.263158}}} +{"ae":{"0387310738_0262194759":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0262194759"}}} +{"ae":{"0387952845_0262194759":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387952845","r":-1,"directed":false,"target":"0262194759"}}} +{"ae":{"026218253X_0262194759":{"g":0,"weight":1,"Weight":1,"b":0,"source":"026218253X","r":-1,"directed":false,"target":"0262194759"}}} +{"an":{"0262033585":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Semi-Supervised...","full-label":"Semi-Supervised Learning (Adaptive Computation and Machine Learning)","z":0,"y":-47.336494,"x":322.734,"size":7.2105265}}} +{"ae":{"0387310738_0262033585":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0262033585"}}} +{"ae":{"026218253X_0262033585":{"g":0,"weight":1,"Weight":1,"b":0,"source":"026218253X","r":-1,"directed":false,"target":"0262033585"}}} +{"ae":{"0262194759_0262033585":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262194759","r":-1,"directed":false,"target":"0262033585"}}} +{"an":{"0521642981":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Information The...","full-label":"Information Theory, Inference & Learning Algorithms","z":0,"y":-122.151184,"x":494.04156,"size":7.2105265}}} +{"ae":{"0387310738_0521642981":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0521642981"}}} +{"ae":{"0387952845_0521642981":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387952845","r":-1,"directed":false,"target":"0521642981"}}} +{"ae":{"026218253X_0521642981":{"g":0,"weight":1,"Weight":1,"b":0,"source":"026218253X","r":-1,"directed":false,"target":"0521642981"}}} +{"an":{"0486247759":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Introductory Gr...","full-label":"Introductory Graph Theory","z":0,"y":-594.25116,"x":194.73067,"size":7.2105265}}} +{"ae":{"0486402584_0486247759":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486402584","r":-1,"directed":false,"target":"0486247759"}}} +{"an":{"0486678709":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Introduction to...","full-label":"Introduction to Graph Theory (Dover Books on Advanced Mathematics)","z":0,"y":-608.2786,"x":137.08861,"size":7.2105265}}} +{"ae":{"0486678709_0486247759":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486678709","r":-1,"directed":false,"target":"0486247759"}}} +{"ae":{"0486402584_0486678709":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486402584","r":-1,"directed":false,"target":"0486678709"}}} +{"an":{"0521474655":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Randomized Algo...","full-label":"Randomized Algorithms","z":0,"y":-409.61588,"x":193.81761,"size":7.2105265}}} +{"ae":{"0716710455_0521474655":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0716710455","r":-1,"directed":false,"target":"0521474655"}}} +{"ae":{"0486402584_0521474655":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486402584","r":-1,"directed":false,"target":"0521474655"}}} +{"ae":{"3540653678_0521474655":{"g":0,"weight":1,"Weight":1,"b":0,"source":"3540653678","r":-1,"directed":false,"target":"0521474655"}}} +{"an":{"0521872820":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"Algorithmic Gam...","full-label":"Algorithmic Game Theory","z":0,"y":-464.28198,"x":163.2528,"size":9.315789}}} +{"ae":{"0521872820_0521474655":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521872820","r":-1,"directed":false,"target":"0521474655"}}} +{"ae":{"0486402584_0521872820":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486402584","r":-1,"directed":false,"target":"0521872820"}}} +{"ae":{"3540653678_0521872820":{"g":0,"weight":1,"Weight":1,"b":0,"source":"3540653678","r":-1,"directed":false,"target":"0521872820"}}} +{"an":{"0521835402":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Probability and...","full-label":"Probability and Computing: Randomized Algorithms and Probabilistic Analysis","z":0,"y":-365.90417,"x":69.113365,"size":7.2105265}}} +{"ae":{"0521835402_0521474655":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521835402","r":-1,"directed":false,"target":"0521474655"}}} +{"ae":{"0262032937_0521835402":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262032937","r":-1,"directed":false,"target":"0521835402"}}} +{"ae":{"0486402584_0521835402":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486402584","r":-1,"directed":false,"target":"0521835402"}}} +{"ae":{"3540653678_0521835402":{"g":0,"weight":1,"Weight":1,"b":0,"source":"3540653678","r":-1,"directed":false,"target":"0521835402"}}} +{"ae":{"0521872820_0521835402":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521872820","r":-1,"directed":false,"target":"0521835402"}}} +{"an":{"0534939724":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Compiler Constr...","full-label":"Compiler Construction: Principles and Practice","z":0,"y":-246.79741,"x":-10.693725,"size":7.2105265}}} +{"ae":{"0534950973_0534939724":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0534950973","r":-1,"directed":false,"target":"0534939724"}}} +{"ae":{"0321486811_0534939724":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321486811","r":-1,"directed":false,"target":"0534939724"}}} +{"ae":{"B00007FYCY_0534939724":{"g":0,"weight":1,"Weight":1,"b":0,"source":"B00007FYCY","r":-1,"directed":false,"target":"0534939724"}}} +{"ae":{"0126339511_0534939724":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0126339511","r":-1,"directed":false,"target":"0534939724"}}} +{"ae":{"013089592X_0534939724":{"g":0,"weight":1,"Weight":1,"b":0,"source":"013089592X","r":-1,"directed":false,"target":"0534939724"}}} +{"an":{"B00007FYIJ":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)","z":0,"y":-519.38226,"x":13.5093355,"size":9.315789}}} +{"ae":{"B00007FYCY_B00007FYIJ":{"g":0,"weight":1,"Weight":1,"b":0,"source":"B00007FYCY","r":-1,"directed":false,"target":"B00007FYIJ"}}} +{"an":{"155860698X":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Engineering a C...","full-label":"Engineering a Compiler","z":0,"y":-266.31744,"x":161.24925,"size":7.2105265}}} +{"ae":{"0321486811_155860698X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321486811","r":-1,"directed":false,"target":"155860698X"}}} +{"ae":{"B00007FYCY_155860698X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"B00007FYCY","r":-1,"directed":false,"target":"155860698X"}}} +{"ae":{"0126339511_155860698X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0126339511","r":-1,"directed":false,"target":"155860698X"}}} +{"an":{"0072253517":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)","z":0,"y":-525.1006,"x":291.53198,"size":8.263158}}} +{"ae":{"0321369572_0072253517":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321369572","r":-1,"directed":false,"target":"0072253517"}}} +{"an":{"1593270038":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Write Great Cod...","full-label":"Write Great Code: Volume 1: Understanding the Machine","z":0,"y":51.864815,"x":-76.23576,"size":7.2105265}}} +{"ae":{"0735619670_1593270038":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735619670","r":-1,"directed":false,"target":"1593270038"}}} +{"ae":{"1593271042_1593270038":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1593271042","r":-1,"directed":false,"target":"1593270038"}}} +{"an":{"1593270658":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Write Great Cod...","full-label":"Write Great Code, Volume 2: Thinking Low-Level, Writing High-Level","z":0,"y":65.12926,"x":-13.753951,"size":7.2105265}}} +{"ae":{"1593270658_1593270038":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1593270658","r":-1,"directed":false,"target":"1593270038"}}} +{"ae":{"1593271042_1593270658":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1593271042","r":-1,"directed":false,"target":"1593270658"}}} +{"an":{"0672326663":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Programming in ...","full-label":"Programming in C (3rd Edition) (Developer's Library)","z":0,"y":-282.94278,"x":-243.66737,"size":7.2105265}}} +{"ae":{"0672326663_0672305100":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0672326663","r":-1,"directed":false,"target":"0672305100"}}} +{"ae":{"0131103628_0672326663":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131103628","r":-1,"directed":false,"target":"0672326663"}}} +{"ae":{"013089592X_0672326663":{"g":0,"weight":1,"Weight":1,"b":0,"source":"013089592X","r":-1,"directed":false,"target":"0672326663"}}} +{"an":{"0201179288":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"C Traps and Pit...","full-label":"C Traps and Pitfalls","z":0,"y":-427.84738,"x":-185.19868,"size":7.2105265}}} +{"ae":{"0131103628_0201179288":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131103628","r":-1,"directed":false,"target":"0201179288"}}} +{"ae":{"013089592X_0201179288":{"g":0,"weight":1,"Weight":1,"b":0,"source":"013089592X","r":-1,"directed":false,"target":"0201179288"}}} +{"an":{"0131774298":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Expert C Progra...","full-label":"Expert C Programming","z":0,"y":-371.89594,"x":-184.00174,"size":7.2105265}}} +{"ae":{"0131774298_0201179288":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131774298","r":-1,"directed":false,"target":"0201179288"}}} +{"ae":{"0131103628_0131774298":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131103628","r":-1,"directed":false,"target":"0131774298"}}} +{"ae":{"013089592X_0131774298":{"g":0,"weight":1,"Weight":1,"b":0,"source":"013089592X","r":-1,"directed":false,"target":"0131774298"}}} +{"an":{"0764570684":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"C For Dummies, ...","full-label":"C For Dummies, 2nd Edition","z":0,"y":-237.67241,"x":-301.56137,"size":7.2105265}}} +{"ae":{"0131103628_0764570684":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131103628","r":-1,"directed":false,"target":"0764570684"}}} +{"ae":{"0672305100_0764570684":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0672305100","r":-1,"directed":false,"target":"0764570684"}}} +{"an":{"0672326965":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"C Primer Plus (...","full-label":"C Primer Plus (5th Edition)","z":0,"y":-337.2732,"x":-232.79353,"size":7.2105265}}} +{"ae":{"0672326965_0672326663":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0672326965","r":-1,"directed":false,"target":"0672326663"}}} +{"ae":{"0131103628_0672326965":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131103628","r":-1,"directed":false,"target":"0672326965"}}} +{"ae":{"013089592X_0672326965":{"g":0,"weight":1,"Weight":1,"b":0,"source":"013089592X","r":-1,"directed":false,"target":"0672326965"}}} +{"ae":{"0672305100_0672326965":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0672305100","r":-1,"directed":false,"target":"0672326965"}}} +{"an":{"0764570692":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"C All-in-One De...","full-label":"C All-in-One Desk Reference for Dummies","z":0,"y":-338.00494,"x":-299.58264,"size":7.2105265}}} +{"ae":{"0764570692_0764570684":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0764570692","r":-1,"directed":false,"target":"0764570684"}}} +{"ae":{"0131103628_0764570692":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131103628","r":-1,"directed":false,"target":"0764570692"}}} +{"ae":{"0672305100_0764570692":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0672305100","r":-1,"directed":false,"target":"0764570692"}}} +{"an":{"0131411551":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Unix Network Pr...","full-label":"Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition) (Addison-Wesley Professional Computing Series)","z":0,"y":-594.5562,"x":-111.3791,"size":8.263158}}} +{"ae":{"0201433079_0131411551":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201433079","r":-1,"directed":false,"target":"0131411551"}}} +{"an":{"0130810819":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"UNIX Network Pr...","full-label":"UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition) (The Unix Networking Reference Series , Vol 2)","z":0,"y":-610.2206,"x":-170.40115,"size":8.263158}}} +{"ae":{"0130810819_0131411551":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0130810819","r":-1,"directed":false,"target":"0131411551"}}} +{"ae":{"0201433079_0130810819":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201433079","r":-1,"directed":false,"target":"0130810819"}}} +{"an":{"0131411543":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Advanced UNIX P...","full-label":"Advanced UNIX Programming (2nd Edition) (Addison-Wesley Professional Computing Series)","z":0,"y":-503.8781,"x":-172.89433,"size":7.2105265}}} +{"ae":{"0131103628_0131411543":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131103628","r":-1,"directed":false,"target":"0131411543"}}} +{"ae":{"0201433079_0131411543":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201433079","r":-1,"directed":false,"target":"0131411543"}}} +{"ae":{"0131411551_0131411543":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131411551","r":-1,"directed":false,"target":"0131411543"}}} +{"ae":{"0130810819_0131411543":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0130810819","r":-1,"directed":false,"target":"0131411543"}}} +{"an":{"0596005652":{"g":0.8117647,"b":0.20784314,"r":0.011764706,"Label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition","z":0,"y":-626.58386,"x":-26.109322,"size":10.368422}}} +{"ae":{"0596005652_0131411551":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596005652","r":-1,"directed":false,"target":"0131411551"}}} +{"ae":{"0201433079_0596005652":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201433079","r":-1,"directed":false,"target":"0596005652"}}} +{"an":{"0321278658":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Extreme Program...","full-label":"Extreme Programming Explained: Embrace Change (2nd Edition) (The XP Series)","z":0,"y":545.553,"x":-180.64491,"size":7.2105265}}} +{"ae":{"0201485672_0321278658":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201485672","r":-1,"directed":false,"target":"0321278658"}}} +{"ae":{"0321146530_0321278658":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321146530","r":-1,"directed":false,"target":"0321278658"}}} +{"an":{"0321205685":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"User Stories Ap...","full-label":"User Stories Applied: For Agile Software Development (The Addison-Wesley Signature Series)","z":0,"y":611.15314,"x":-137.62611,"size":8.263158}}} +{"ae":{"0321205685_0131479415":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321205685","r":-1,"directed":false,"target":"0131479415"}}} +{"ae":{"0321146530_0321205685":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321146530","r":-1,"directed":false,"target":"0321205685"}}} +{"an":{"0131177052":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Working Effecti...","full-label":"Working Effectively with Legacy Code (Robert C. Martin Series)","z":0,"y":359.42505,"x":-148.82513,"size":7.2105265}}} +{"ae":{"020161622X_0131177052":{"g":0,"weight":1,"Weight":1,"b":0,"source":"020161622X","r":-1,"directed":false,"target":"0131177052"}}} +{"ae":{"0201485672_0131177052":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201485672","r":-1,"directed":false,"target":"0131177052"}}} +{"ae":{"0321146530_0131177052":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321146530","r":-1,"directed":false,"target":"0131177052"}}} +{"an":{"0321200683":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Enterprise Inte...","full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)","z":0,"y":502.58987,"x":-270.54874,"size":7.2105265}}} +{"ae":{"0321127420_0321200683":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321127420","r":-1,"directed":false,"target":"0321200683"}}} +{"an":{"0321125215":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Domain-Driven D...","full-label":"Domain-Driven Design: Tackling Complexity in the Heart of Software","z":0,"y":404.11606,"x":-224.31035,"size":7.2105265}}} +{"ae":{"0321125215_0321200683":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321125215","r":-1,"directed":false,"target":"0321200683"}}} +{"ae":{"0201485672_0321125215":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201485672","r":-1,"directed":false,"target":"0321125215"}}} +{"ae":{"0321146530_0321125215":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321146530","r":-1,"directed":false,"target":"0321125215"}}} +{"ae":{"0321127420_0321125215":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321127420","r":-1,"directed":false,"target":"0321125215"}}} +{"an":{"0596527357":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Head First Soft...","full-label":"Head First Software Development (Brain-Friendly Guides)","z":0,"y":432.20834,"x":-442.5732,"size":8.263158}}} +{"ae":{"0596527357_0596102143":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596527357","r":-1,"directed":false,"target":"0596102143"}}} +{"ae":{"0596007124_0596527357":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596007124","r":-1,"directed":false,"target":"0596527357"}}} +{"ae":{"0596008678_0596527357":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596008678","r":-1,"directed":false,"target":"0596527357"}}} +{"an":{"059610197X":{"g":0.8117647,"b":0.20784314,"r":0.011764706,"Label":"Head First HTML...","full-label":"Head First HTML with CSS & XHTML","z":0,"y":472.85538,"x":-401.74326,"size":10.368422}}} +{"ae":{"059610197X_0596009208":{"g":0,"weight":1,"Weight":1,"b":0,"source":"059610197X","r":-1,"directed":false,"target":"0596009208"}}} +{"ae":{"059610197X_0596516681":{"g":0,"weight":1,"Weight":1,"b":0,"source":"059610197X","r":-1,"directed":false,"target":"0596516681"}}} +{"ae":{"059610197X_0596102143":{"g":0,"weight":1,"Weight":1,"b":0,"source":"059610197X","r":-1,"directed":false,"target":"0596102143"}}} +{"ae":{"0596007124_059610197X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596007124","r":-1,"directed":false,"target":"059610197X"}}} +{"ae":{"0596008678_059610197X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596008678","r":-1,"directed":false,"target":"059610197X"}}} +{"an":{"0596005717":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Head First EJB ...","full-label":"Head First EJB (Brain-Friendly Study Guides; Enterprise JavaBeans)","z":0,"y":419.5777,"x":-289.15192,"size":7.2105265}}} +{"ae":{"0596005717_0596516681":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596005717","r":-1,"directed":false,"target":"0596516681"}}} +{"ae":{"0596007124_0596005717":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596007124","r":-1,"directed":false,"target":"0596005717"}}} +{"ae":{"0596008678_0596005717":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596008678","r":-1,"directed":false,"target":"0596005717"}}} +{"ae":{"0596009208_0596005717":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596009208","r":-1,"directed":false,"target":"0596005717"}}} +{"an":{"0072253606":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"SCJP Sun Certif...","full-label":"SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press Study Guides)","z":0,"y":533.31885,"x":-346.45453,"size":8.263158}}} +{"ae":{"0072253606_0596005717":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0072253606","r":-1,"directed":false,"target":"0596005717"}}} +{"ae":{"0596007124_0072253606":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596007124","r":-1,"directed":false,"target":"0072253606"}}} +{"ae":{"0596009208_0072253606":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596009208","r":-1,"directed":false,"target":"0072253606"}}} +{"ae":{"0596516681_0072253606":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596516681","r":-1,"directed":false,"target":"0072253606"}}} +{"an":{"0201734842":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"C++ Templates: ...","full-label":"C++ Templates: The Complete Guide","z":0,"y":81.90479,"x":-313.15567,"size":7.2105265}}} +{"ae":{"0321334876_0201734842":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321334876","r":-1,"directed":false,"target":"0201734842"}}} +{"ae":{"0201700735_0201734842":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201700735","r":-1,"directed":false,"target":"0201734842"}}} +{"ae":{"0201749629_0201734842":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201749629","r":-1,"directed":false,"target":"0201734842"}}} +{"ae":{"0201379260_0201734842":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201379260","r":-1,"directed":false,"target":"0201734842"}}} +{"an":{"0201702258":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Writing Effecti...","full-label":"Writing Effective Use Cases (The Agile Software Development Series)","z":0,"y":442.20728,"x":-70.38872,"size":8.263158}}} +{"ae":{"0201702258_0735618798":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201702258","r":-1,"directed":false,"target":"0735618798"}}} +{"ae":{"0131489062_0201702258":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131489062","r":-1,"directed":false,"target":"0201702258"}}} +{"an":{"0486252116":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Entertaining Ma...","full-label":"Entertaining Mathematical Puzzles","z":0,"y":184.70721,"x":-414.12057,"size":7.2105265}}} +{"ae":{"0486281523_0486252116":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486281523","r":-1,"directed":false,"target":"0486252116"}}} +{"an":{"0486270785":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"The Moscow Puzz...","full-label":"The Moscow Puzzles: 359 Mathematical Recreations (Math & Logic Puzzles)","z":0,"y":273.52182,"x":-397.41733,"size":7.2105265}}} +{"ae":{"0486270785_0486252116":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486270785","r":-1,"directed":false,"target":"0486252116"}}} +{"ae":{"0486281523_0486270785":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486281523","r":-1,"directed":false,"target":"0486270785"}}} +{"an":{"0486256375":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Perplexing Puzz...","full-label":"Perplexing Puzzles and Tantalizing Teasers (Math & Logic Puzzles)","z":0,"y":241.0687,"x":-352.1135,"size":7.2105265}}} +{"ae":{"0486256375_0486252116":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486256375","r":-1,"directed":false,"target":"0486252116"}}} +{"ae":{"0486256375_0486270785":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486256375","r":-1,"directed":false,"target":"0486270785"}}} +{"ae":{"0486281523_0486256375":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486281523","r":-1,"directed":false,"target":"0486256375"}}} +{"an":{"0486427552":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Brain Busters! ...","full-label":"Brain Busters! Mind-Stretching Puzzles in Math and Logic","z":0,"y":233.64612,"x":-451.4868,"size":8.263158}}} +{"ae":{"0486427552_0486252116":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486427552","r":-1,"directed":false,"target":"0486252116"}}} +{"ae":{"0486427552_0486270785":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486427552","r":-1,"directed":false,"target":"0486270785"}}} +{"ae":{"0486427552_0486256375":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486427552","r":-1,"directed":false,"target":"0486256375"}}} +{"ae":{"0486281523_0486427552":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486281523","r":-1,"directed":false,"target":"0486427552"}}} +{"an":{"0970825315":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"So You Think Yo...","full-label":"So You Think You're Smart: 150 Fun and Challenging Brain Teasers","z":0,"y":306.94708,"x":-455.56686,"size":9.315789}}} +{"ae":{"0970825315_0486252116":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0970825315","r":-1,"directed":false,"target":"0486252116"}}} +{"ae":{"0970825315_0486256375":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0970825315","r":-1,"directed":false,"target":"0486256375"}}} +{"ae":{"0970825315_0486427552":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0970825315","r":-1,"directed":false,"target":"0486427552"}}} +{"ae":{"0486281523_0970825315":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486281523","r":-1,"directed":false,"target":"0970825315"}}} +{"an":{"0596527349":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Information Arc...","full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites","z":0,"y":534.9177,"x":123.1355,"size":8.263158}}} +{"ae":{"0596528108_0596527349":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596528108","r":-1,"directed":false,"target":"0596527349"}}} +{"an":{"0321344758":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"Don't Make Me T...","full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition","z":0,"y":470.59662,"x":91.01722,"size":9.315789}}} +{"ae":{"0321344758_0596527349":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321344758","r":-1,"directed":false,"target":"0596527349"}}} +{"ae":{"0596528108_0321344758":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596528108","r":-1,"directed":false,"target":"0321344758"}}} +{"an":{"0596008031":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Designing Inter...","full-label":"Designing Interfaces: Patterns for Effective Interaction Design","z":0,"y":447.60986,"x":148.63983,"size":8.263158}}} +{"ae":{"0596008031_0596527349":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596008031","r":-1,"directed":false,"target":"0596527349"}}} +{"ae":{"0596008031_0321344758":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596008031","r":-1,"directed":false,"target":"0321344758"}}} +{"ae":{"0596528108_0596008031":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596528108","r":-1,"directed":false,"target":"0596008031"}}} +{"an":{"0974514055":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Programming Rub...","full-label":"Programming Ruby: The Pragmatic Programmers' Guide, Second Edition","z":0,"y":319.0653,"x":600.2296,"size":8.263158}}} +{"ae":{"0977616630_0974514055":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0977616630","r":-1,"directed":false,"target":"0974514055"}}} +{"an":{"0977616606":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Rails Recipes (...","full-label":"Rails Recipes (Pragmatic Programmers)","z":0,"y":208.32278,"x":615.30994,"size":7.2105265}}} +{"ae":{"0977616606_0974514055":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0977616606","r":-1,"directed":false,"target":"0974514055"}}} +{"ae":{"0977616630_0977616606":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0977616630","r":-1,"directed":false,"target":"0977616606"}}} +{"an":{"0596527446":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Ajax on Rails","full-label":"Ajax on Rails","z":0,"y":232.01123,"x":670.7031,"size":8.263158}}} +{"ae":{"0596527446_0977616606":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596527446","r":-1,"directed":false,"target":"0977616606"}}} +{"ae":{"0977616630_0596527446":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0977616630","r":-1,"directed":false,"target":"0596527446"}}} +{"ae":{"0974514055_0596527446":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0974514055","r":-1,"directed":false,"target":"0596527446"}}} +{"an":{"0596523696":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"Ruby Cookbook (...","full-label":"Ruby Cookbook (Cookbooks (O'Reilly))","z":0,"y":289.0088,"x":653.4876,"size":9.315789}}} +{"ae":{"0596523696_0974514055":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596523696","r":-1,"directed":false,"target":"0974514055"}}} +{"ae":{"0596523696_0977616606":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596523696","r":-1,"directed":false,"target":"0977616606"}}} +{"ae":{"0596523696_0596527446":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596523696","r":-1,"directed":false,"target":"0596527446"}}} +{"ae":{"0977616630_0596523696":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0977616630","r":-1,"directed":false,"target":"0596523696"}}} +{"an":{"1932394699":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Ruby for Rails:...","full-label":"Ruby for Rails: Ruby Techniques for Rails Developers","z":0,"y":260.68573,"x":583.13184,"size":7.2105265}}} +{"ae":{"1932394699_0974514055":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1932394699","r":-1,"directed":false,"target":"0974514055"}}} +{"ae":{"1932394699_0977616606":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1932394699","r":-1,"directed":false,"target":"0977616606"}}} +{"ae":{"0977616630_1932394699":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0977616630","r":-1,"directed":false,"target":"1932394699"}}} +{"ae":{"0596527446_1932394699":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596527446","r":-1,"directed":false,"target":"1932394699"}}} +{"ae":{"0596523696_1932394699":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596523696","r":-1,"directed":false,"target":"1932394699"}}} +{"an":{"059652708X":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"MySQL Cookbook","full-label":"MySQL Cookbook","z":0,"y":145.0134,"x":592.7548,"size":8.263158}}} +{"ae":{"0596003064_059652708X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596003064","r":-1,"directed":false,"target":"059652708X"}}} +{"an":{"0596100892":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"MySQL Stored Pr...","full-label":"MySQL Stored Procedure Programming","z":0,"y":150.3499,"x":521.73755,"size":7.2105265}}} +{"ae":{"0596100892_059652708X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596100892","r":-1,"directed":false,"target":"059652708X"}}} +{"ae":{"0596003064_0596100892":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596003064","r":-1,"directed":false,"target":"0596100892"}}} +{"an":{"159059505X":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Pro MySQL (Expe...","full-label":"Pro MySQL (Expert's Voice in Open Source)","z":0,"y":230.2158,"x":461.0593,"size":7.2105265}}} +{"ae":{"159059505X_0596100892":{"g":0,"weight":1,"Weight":1,"b":0,"source":"159059505X","r":-1,"directed":false,"target":"0596100892"}}} +{"ae":{"0596102356_159059505X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596102356","r":-1,"directed":false,"target":"159059505X"}}} +{"ae":{"0596003064_159059505X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596003064","r":-1,"directed":false,"target":"159059505X"}}} +{"ae":{"059652708X_159059505X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"059652708X","r":-1,"directed":false,"target":"159059505X"}}} +{"an":{"159200346X":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Physical Comput...","full-label":"Physical Computing: Sensing and Controlling the Physical World with Computers","z":0,"y":345.74905,"x":502.71643,"size":8.263158}}} +{"ae":{"159200346X_159059617X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"159200346X","r":-1,"directed":false,"target":"159059617X"}}} +{"ae":{"159200346X_0596510519":{"g":0,"weight":1,"Weight":1,"b":0,"source":"159200346X","r":-1,"directed":false,"target":"0596510519"}}} +{"ae":{"0262182629_159200346X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262182629","r":-1,"directed":false,"target":"159200346X"}}} +{"an":{"0500285179":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Creative Code: ...","full-label":"Creative Code: Aesthetics + Computation","z":0,"y":396.09314,"x":390.83514,"size":7.2105265}}} +{"ae":{"0262182629_0500285179":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262182629","r":-1,"directed":false,"target":"0500285179"}}} +{"ae":{"159059617X_0500285179":{"g":0,"weight":1,"Weight":1,"b":0,"source":"159059617X","r":-1,"directed":false,"target":"0500285179"}}} +{"an":{"059651428X":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"The Best of MAK...","full-label":"The Best of MAKE (Make)","z":0,"y":431.95352,"x":531.8619,"size":7.2105265}}} +{"ae":{"0596510519_059651428X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510519","r":-1,"directed":false,"target":"059651428X"}}} +{"ae":{"159200346X_059651428X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"159200346X","r":-1,"directed":false,"target":"059651428X"}}} +{"an":{"1591841992":{"g":0.8117647,"b":0.20784314,"r":0.011764706,"Label":"The Back of the...","full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures","z":0,"y":545.4346,"x":354.86667,"size":10.368422}}} +{"ae":{"0321525655_1591841992":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321525655","r":-1,"directed":false,"target":"1591841992"}}} +{"an":{"0735623872":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Beyond Bullet P...","full-label":"Beyond Bullet Points: Using Microsoft® Office PowerPoint® 2007 to Create Presentations That Inform, Motivate, and Inspire","z":0,"y":473.57687,"x":356.7522,"size":7.2105265}}} +{"ae":{"0321525655_0735623872":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321525655","r":-1,"directed":false,"target":"0735623872"}}} +{"ae":{"1591841992_0735623872":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1591841992","r":-1,"directed":false,"target":"0735623872"}}} +{"an":{"0195320697":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Clear and to th...","full-label":"Clear and to the Point: 8 Psychological Principles for Compelling PowerPoint Presentations","z":0,"y":513.2836,"x":299.55704,"size":7.2105265}}} +{"ae":{"0195320697_0735623872":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0195320697","r":-1,"directed":false,"target":"0735623872"}}} +{"ae":{"0321525655_0195320697":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321525655","r":-1,"directed":false,"target":"0195320697"}}} +{"ae":{"1591841992_0195320697":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1591841992","r":-1,"directed":false,"target":"0195320697"}}} +{"an":{"0979777704":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Brain Rules: 12...","full-label":"Brain Rules: 12 Principles for Surviving and Thriving at Work, Home, and School (Book & DVD)","z":0,"y":585.3013,"x":297.10464,"size":7.2105265}}} +{"ae":{"0979777704_1591841992":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0979777704","r":-1,"directed":false,"target":"1591841992"}}} +{"ae":{"0321525655_0979777704":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321525655","r":-1,"directed":false,"target":"0979777704"}}} +{"an":{"1400064287":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Made to Stick: ...","full-label":"Made to Stick: Why Some Ideas Survive and Others Die","z":0,"y":606.3189,"x":237.95473,"size":8.263158}}} +{"ae":{"1400064287_0195320697":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1400064287","r":-1,"directed":false,"target":"0195320697"}}} +{"ae":{"0321525655_1400064287":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321525655","r":-1,"directed":false,"target":"1400064287"}}} +{"ae":{"1591841992_1400064287":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1591841992","r":-1,"directed":false,"target":"1400064287"}}} +{"an":{"1591840562":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"The Art of the ...","full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything","z":0,"y":371.26358,"x":225.6804,"size":7.2105265}}} +{"ae":{"0321525655_1591840562":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321525655","r":-1,"directed":false,"target":"1591840562"}}} +{"ae":{"1590597141_1591840562":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1590597141","r":-1,"directed":false,"target":"1591840562"}}} +{"an":{"0321349601":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Java Concurrenc...","full-label":"Java Concurrency in Practice","z":0,"y":66.63179,"x":409.4309,"size":8.263158}}} +{"ae":{"0978739256_0321349601":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0978739256","r":-1,"directed":false,"target":"0321349601"}}} +{"an":{"0521644089":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"The Haskell Sch...","full-label":"The Haskell School of Expression: Learning Functional Programming through Multimedia","z":0,"y":158.69196,"x":198.94157,"size":7.2105265}}} +{"ae":{"193435600X_0521644089":{"g":0,"weight":1,"Weight":1,"b":0,"source":"193435600X","r":-1,"directed":false,"target":"0521644089"}}} +{"ae":{"0521692695_0521644089":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521692695","r":-1,"directed":false,"target":"0521644089"}}} +{"an":{"0954300696":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"The Haskell Roa...","full-label":"The Haskell Road to Logic, Maths and Programming (Texts in Computing S.)","z":0,"y":169.26549,"x":138.67688,"size":7.2105265}}} +{"ae":{"0954300696_0521644089":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0954300696","r":-1,"directed":false,"target":"0521644089"}}} +{"ae":{"193435600X_0954300696":{"g":0,"weight":1,"Weight":1,"b":0,"source":"193435600X","r":-1,"directed":false,"target":"0954300696"}}} +{"ae":{"0521692695_0954300696":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521692695","r":-1,"directed":false,"target":"0954300696"}}} +{"an":{"0521663504":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Purely Function...","full-label":"Purely Functional Data Structures","z":0,"y":101.3106,"x":88.146126,"size":7.2105265}}} +{"ae":{"0521663504_0521644089":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521663504","r":-1,"directed":false,"target":"0521644089"}}} +{"ae":{"0521663504_0954300696":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521663504","r":-1,"directed":false,"target":"0954300696"}}} +{"ae":{"0596510047_0521663504":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596510047","r":-1,"directed":false,"target":"0521663504"}}} +{"ae":{"193435600X_0521663504":{"g":0,"weight":1,"Weight":1,"b":0,"source":"193435600X","r":-1,"directed":false,"target":"0521663504"}}} +{"ae":{"0521692695_0521663504":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521692695","r":-1,"directed":false,"target":"0521663504"}}} +{"an":{"0971015848":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition","z":0,"y":265.61652,"x":-536.5629,"size":9.315789}}} +{"ae":{"0071440011_0971015848":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071440011","r":-1,"directed":false,"target":"0971015848"}}} +{"an":{"038541580X":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Prisoner's Dile...","full-label":"Prisoner's Dilemma","z":0,"y":-185.47514,"x":-395.15714,"size":7.2105265}}} +{"ae":{"0385242719_038541580X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0385242719","r":-1,"directed":false,"target":"038541580X"}}} +{"an":{"0809045990":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street","z":0,"y":-217.10852,"x":-509.91748,"size":9.315789}}} +{"ae":{"0809045990_038541580X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0809045990","r":-1,"directed":false,"target":"038541580X"}}} +{"ae":{"0385242719_0809045990":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0385242719","r":-1,"directed":false,"target":"0809045990"}}} +{"an":{"0521483476":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Paradoxes","full-label":"Paradoxes","z":0,"y":-153.50133,"x":-543.9663,"size":7.2105265}}} +{"ae":{"0385242719_0521483476":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0385242719","r":-1,"directed":false,"target":"0521483476"}}} +{"an":{"0131499084":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"Options, Future...","full-label":"Options, Futures and Other Derivatives (6th Edition)","z":0,"y":-471.89236,"x":-443.6404,"size":9.315789}}} +{"ae":{"0970055269_0131499084":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0970055269","r":-1,"directed":false,"target":"0131499084"}}} +{"an":{"1581311729":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Vault Guide to ...","full-label":"Vault Guide to Advanced Finance & Quantitative Interviews","z":0,"y":-525.63837,"x":-384.81146,"size":7.2105265}}} +{"ae":{"0970055269_1581311729":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0970055269","r":-1,"directed":false,"target":"1581311729"}}} +{"ae":{"0131499084_1581311729":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131499084","r":-1,"directed":false,"target":"1581311729"}}} +{"an":{"0387401016":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Stochastic Calc...","full-label":"Stochastic Calculus for Finance II: Continuous-Time Models (Springer Finance)","z":0,"y":-467.44113,"x":-507.69907,"size":7.2105265}}} +{"ae":{"0387401016_0131499084":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387401016","r":-1,"directed":false,"target":"0131499084"}}} +{"ae":{"0970055269_0387401016":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0970055269","r":-1,"directed":false,"target":"0387401016"}}} +{"an":{"0470192739":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"My Life as a Qu...","full-label":"My Life as a Quant: Reflections on Physics and Finance","z":0,"y":-343.7004,"x":-550.20776,"size":8.263158}}} +{"ae":{"0970055269_0470192739":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0970055269","r":-1,"directed":false,"target":"0470192739"}}} +{"an":{"1432706810":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Starting Your C...","full-label":"Starting Your Career as a Wall Street Quant: A Practical, No-BS Guide to Getting a Job in Quantitative Finance and Launching a Lucrative Career","z":0,"y":-394.39075,"x":-486.52994,"size":7.2105265}}} +{"ae":{"0970055269_1432706810":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0970055269","r":-1,"directed":false,"target":"1432706810"}}} +{"ae":{"0131499084_1432706810":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131499084","r":-1,"directed":false,"target":"1432706810"}}} +{"ae":{"0470192739_1432706810":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0470192739","r":-1,"directed":false,"target":"1432706810"}}} +{"an":{"1593573111":{"g":0.9098039,"b":0.09411765,"r":0.011764706,"Label":"Resume Magic: T...","full-label":"Resume Magic: Trade Secrets of a Professional Resume Writer (Resume Magic Trade Secrets of a Professional Resume Writer)","z":0,"y":-2.5075076,"x":-586.4022,"size":11.421053}}} +{"ae":{"1593573111_0071387730":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1593573111","r":-1,"directed":false,"target":"0071387730"}}} +{"ae":{"0071492747_1593573111":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071492747","r":-1,"directed":false,"target":"1593573111"}}} +{"ae":{"1402203853_1593573111":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1402203853","r":-1,"directed":false,"target":"1593573111"}}} +{"an":{"1593571275":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Expert Resumes ...","full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)","z":0,"y":-74.24741,"x":-629.65894,"size":7.2105265}}} +{"ae":{"0071492747_1593571275":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071492747","r":-1,"directed":false,"target":"1593571275"}}} +{"ae":{"1593573111_1593571275":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1593573111","r":-1,"directed":false,"target":"1593571275"}}} +{"an":{"1587131560":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"The IT Career B...","full-label":"The IT Career Builder's Toolkit","z":0,"y":-30.955002,"x":-406.66208,"size":7.2105265}}} +{"ae":{"0071495789_1587131560":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071495789","r":-1,"directed":false,"target":"1587131560"}}} +{"ae":{"0071492747_1587131560":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071492747","r":-1,"directed":false,"target":"1587131560"}}} +{"an":{"0071449825":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Perfect Phrases...","full-label":"Perfect Phrases for the Perfect Interview: Hundreds of Ready-to-Use Phrases That Succinctly Demonstrate Your Skills, Your Experience and Your Value in Any Interview Situation (Perfect Phrases)","z":0,"y":215.0165,"x":-642.97314,"size":8.263158}}} +{"ae":{"0071425470_0071449825":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071425470","r":-1,"directed":false,"target":"0071449825"}}} +{"ae":{"0071387730_0071449825":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071387730","r":-1,"directed":false,"target":"0071449825"}}} +{"ae":{"1402203853_0071449825":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1402203853","r":-1,"directed":false,"target":"0071449825"}}} +{"an":{"0970901224":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Interview Fitne...","full-label":"Interview Fitness Training: A Workout with Carole Martin, the Interview Coach","z":0,"y":177.14197,"x":-598.7025,"size":7.2105265}}} +{"ae":{"0071425470_0970901224":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071425470","r":-1,"directed":false,"target":"0970901224"}}} +{"ae":{"0071387730_0970901224":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071387730","r":-1,"directed":false,"target":"0970901224"}}} +{"ae":{"1402203853_0970901224":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1402203853","r":-1,"directed":false,"target":"0970901224"}}} +{"ae":{"0071449825_0970901224":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071449825","r":-1,"directed":false,"target":"0970901224"}}} +{"an":{"1564148696":{"g":0.8117647,"b":0.20784314,"r":0.011764706,"Label":"Competency-Base...","full-label":"Competency-Based Interviews: Master the Tough New Interview Style And Give Them the Answers That Will Win You the Job","z":0,"y":138.83363,"x":-649.36597,"size":10.368422}}} +{"ae":{"1564148696_1402203853":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1564148696","r":-1,"directed":false,"target":"1402203853"}}} +{"ae":{"1564148696_0970901224":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1564148696","r":-1,"directed":false,"target":"0970901224"}}} +{"ae":{"0071425470_1564148696":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071425470","r":-1,"directed":false,"target":"1564148696"}}} +{"ae":{"0071387730_1564148696":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071387730","r":-1,"directed":false,"target":"1564148696"}}} +{"an":{"007016357X":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"Best Answers to...","full-label":"Best Answers to the 201 Most Frequently Asked Interview Questions","z":0,"y":73.727684,"x":-629.8051,"size":9.315789}}} +{"ae":{"007016357X_1402203853":{"g":0,"weight":1,"Weight":1,"b":0,"source":"007016357X","r":-1,"directed":false,"target":"1402203853"}}} +{"ae":{"007016357X_1593573111":{"g":0,"weight":1,"Weight":1,"b":0,"source":"007016357X","r":-1,"directed":false,"target":"1593573111"}}} +{"ae":{"0071387730_007016357X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071387730","r":-1,"directed":false,"target":"007016357X"}}} +{"an":{"007141827X":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"How to Intervie...","full-label":"How to Interview Like a Top MBA: Job-Winning Strategies From Headhunters, Fortune 100 Recruiters, and Career Counselors","z":0,"y":193.55424,"x":-546.3795,"size":7.2105265}}} +{"ae":{"007141827X_1402203853":{"g":0,"weight":1,"Weight":1,"b":0,"source":"007141827X","r":-1,"directed":false,"target":"1402203853"}}} +{"ae":{"007141827X_007016357X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"007141827X","r":-1,"directed":false,"target":"007016357X"}}} +{"ae":{"0071387730_007141827X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071387730","r":-1,"directed":false,"target":"007141827X"}}} +{"ae":{"0971015848_007141827X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0971015848","r":-1,"directed":false,"target":"007141827X"}}} +{"ae":{"1564148696_007141827X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1564148696","r":-1,"directed":false,"target":"007141827X"}}} +{"an":{"1418040002":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"101 Great Answe...","full-label":"101 Great Answers to the Toughest Interview Questions","z":0,"y":13.265904,"x":-644.0538,"size":7.2105265}}} +{"ae":{"0071387730_1418040002":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071387730","r":-1,"directed":false,"target":"1418040002"}}} +{"ae":{"1402203853_1418040002":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1402203853","r":-1,"directed":false,"target":"1418040002"}}} +{"ae":{"1593573111_1418040002":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1593573111","r":-1,"directed":false,"target":"1418040002"}}} +{"ae":{"007016357X_1418040002":{"g":0,"weight":1,"Weight":1,"b":0,"source":"007016357X","r":-1,"directed":false,"target":"1418040002"}}} +{"an":{"1564147789":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Winning Job Int...","full-label":"Winning Job Interviews: Reduce Interview Anxiety / Outprepare the Other Candidates / Land the Job You Love","z":0,"y":40.633015,"x":-542.28644,"size":7.2105265}}} +{"ae":{"1564147789_1564148696":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1564147789","r":-1,"directed":false,"target":"1564148696"}}} +{"ae":{"0071387730_1564147789":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071387730","r":-1,"directed":false,"target":"1564147789"}}} +{"ae":{"1402203853_1564147789":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1402203853","r":-1,"directed":false,"target":"1564147789"}}} +{"ae":{"1593573111_1564147789":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1593573111","r":-1,"directed":false,"target":"1564147789"}}} +{"ae":{"007016357X_1564147789":{"g":0,"weight":1,"Weight":1,"b":0,"source":"007016357X","r":-1,"directed":false,"target":"1564147789"}}} +{"an":{"0735622671":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"More About Soft...","full-label":"More About Software Requirements: Thorny Issues and Practical Advice","z":0,"y":450.6344,"x":9.644896,"size":7.2105265}}} +{"ae":{"0735622671_0201702258":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735622671","r":-1,"directed":false,"target":"0201702258"}}} +{"ae":{"0735605351_0735622671":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735605351","r":-1,"directed":false,"target":"0735622671"}}} +{"ae":{"0735618798_0735622671":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735618798","r":-1,"directed":false,"target":"0735622671"}}} +{"an":{"1576810607":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"The Software Re...","full-label":"The Software Requirements Memory Jogger: A Pocket Guide to Help Software And Business Teams Develop And Manage Requirements (Memory Jogger) (Memory Jogger)","z":0,"y":505.5493,"x":-25.016754,"size":7.2105265}}} +{"ae":{"1576810607_0201702258":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1576810607","r":-1,"directed":false,"target":"0201702258"}}} +{"ae":{"1576810607_0735622671":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1576810607","r":-1,"directed":false,"target":"0735622671"}}} +{"ae":{"0735618798_1576810607":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735618798","r":-1,"directed":false,"target":"1576810607"}}} +{"an":{"073561993X":{"g":0.7137255,"b":0.31764707,"r":0.007843138,"Label":"Agile Project M...","full-label":"Agile Project Management with Scrum (Microsoft Professional)","z":0,"y":699.60754,"x":-132.95636,"size":9.315789}}} +{"ae":{"073561993X_0321278658":{"g":0,"weight":1,"Weight":1,"b":0,"source":"073561993X","r":-1,"directed":false,"target":"0321278658"}}} +{"ae":{"073561993X_0321205685":{"g":0,"weight":1,"Weight":1,"b":0,"source":"073561993X","r":-1,"directed":false,"target":"0321205685"}}} +{"ae":{"0131479415_073561993X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131479415","r":-1,"directed":false,"target":"073561993X"}}} +{"an":{"0130676349":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Agile Software ...","full-label":"Agile Software Development with SCRUM (Series in Agile Software Development)","z":0,"y":663.07556,"x":-183.60324,"size":8.263158}}} +{"ae":{"0130676349_0321278658":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0130676349","r":-1,"directed":false,"target":"0321278658"}}} +{"ae":{"0130676349_0321205685":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0130676349","r":-1,"directed":false,"target":"0321205685"}}} +{"ae":{"0130676349_073561993X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0130676349","r":-1,"directed":false,"target":"073561993X"}}} +{"ae":{"0131479415_0130676349":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131479415","r":-1,"directed":false,"target":"0130676349"}}} +{"an":{"0131111558":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Agile and Itera...","full-label":"Agile and Iterative Development: A Manager's Guide (The Agile Software Development Series)","z":0,"y":664.2758,"x":-93.77115,"size":7.2105265}}} +{"ae":{"0131111558_0321205685":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131111558","r":-1,"directed":false,"target":"0321205685"}}} +{"ae":{"0131111558_073561993X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131111558","r":-1,"directed":false,"target":"073561993X"}}} +{"ae":{"0131111558_0130676349":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131111558","r":-1,"directed":false,"target":"0130676349"}}} +{"ae":{"0131479415_0131111558":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131479415","r":-1,"directed":false,"target":"0131111558"}}} +{"an":{"0977616649":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Agile Retrospec...","full-label":"Agile Retrospectives: Making Good Teams Great","z":0,"y":705.85986,"x":-45.105034,"size":7.2105265}}} +{"ae":{"0977616649_0321205685":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0977616649","r":-1,"directed":false,"target":"0321205685"}}} +{"ae":{"0131479415_0977616649":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131479415","r":-1,"directed":false,"target":"0977616649"}}} +{"ae":{"073561993X_0977616649":{"g":0,"weight":1,"Weight":1,"b":0,"source":"073561993X","r":-1,"directed":false,"target":"0977616649"}}} +{"an":{"0131019082":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"UNIX Internals:...","full-label":"UNIX Internals: The New Frontiers","z":0,"y":-559.86865,"x":-210.19647,"size":7.2105265}}} +{"ae":{"0201433079_0131019082":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201433079","r":-1,"directed":false,"target":"0131019082"}}} +{"ae":{"0132017997_0131019082":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0132017997","r":-1,"directed":false,"target":"0131019082"}}} +{"an":{"0131429388":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Operating Syste...","full-label":"Operating Systems Design and Implementation (3rd Edition) (Prentice Hall Software Series)","z":0,"y":-421.6291,"x":19.108128,"size":7.2105265}}} +{"ae":{"0321486811_0131429388":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321486811","r":-1,"directed":false,"target":"0131429388"}}} +{"ae":{"0132017997_0131429388":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0132017997","r":-1,"directed":false,"target":"0131429388"}}} +{"ae":{"0596005652_0131429388":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596005652","r":-1,"directed":false,"target":"0131429388"}}} +{"an":{"020107981X":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"The AWK Program...","full-label":"The AWK Programming Language","z":0,"y":-390.62692,"x":-42.375908,"size":7.2105265}}} +{"ae":{"013937681X_020107981X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"013937681X","r":-1,"directed":false,"target":"020107981X"}}} +{"ae":{"020103669X_020107981X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"020103669X","r":-1,"directed":false,"target":"020107981X"}}} +{"ae":{"B00007FYIJ_020107981X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"B00007FYIJ","r":-1,"directed":false,"target":"020107981X"}}} +{"an":{"1590595009":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"The Best Softwa...","full-label":"The Best Software Writing I: Selected and Introduced by Joel Spolsky","z":0,"y":351.14722,"x":26.525759,"size":7.2105265}}} +{"ae":{"0201835959_1590595009":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201835959","r":-1,"directed":false,"target":"1590595009"}}} +{"ae":{"0932633439_1590595009":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0932633439","r":-1,"directed":false,"target":"1590595009"}}} +{"ae":{"1590598385_1590595009":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1590598385","r":-1,"directed":false,"target":"1590595009"}}} +{"ae":{"1590593898_1590595009":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1590593898","r":-1,"directed":false,"target":"1590595009"}}} +{"an":{"1893115941":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"User Interface ...","full-label":"User Interface Design for Programmers","z":0,"y":387.98962,"x":83.04119,"size":7.2105265}}} +{"ae":{"1893115941_1590595009":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1893115941","r":-1,"directed":false,"target":"1590595009"}}} +{"ae":{"0932633439_1893115941":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0932633439","r":-1,"directed":false,"target":"1893115941"}}} +{"ae":{"1590598385_1893115941":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1590598385","r":-1,"directed":false,"target":"1893115941"}}} +{"ae":{"1590593898_1893115941":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1590593898","r":-1,"directed":false,"target":"1893115941"}}} +{"ae":{"0321344758_1893115941":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321344758","r":-1,"directed":false,"target":"1893115941"}}} +{"an":{"0387251456":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"All of Nonparam...","full-label":"All of Nonparametric Statistics (Springer Texts in Statistics)","z":0,"y":-206.37216,"x":506.32635,"size":7.2105265}}} +{"ae":{"0387310738_0387251456":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0387251456"}}} +{"ae":{"0387952845_0387251456":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387952845","r":-1,"directed":false,"target":"0387251456"}}} +{"ae":{"0387402721_0387251456":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387402721","r":-1,"directed":false,"target":"0387251456"}}} +{"an":{"0534243126":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Statistical Inf...","full-label":"Statistical Inference","z":0,"y":-411.00665,"x":558.5759,"size":8.263158}}} +{"ae":{"0387402721_0534243126":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387402721","r":-1,"directed":false,"target":"0534243126"}}} +{"an":{"052168689X":{"g":0.9098039,"b":0.09411765,"r":0.011764706,"Label":"Data Analysis U...","full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models","z":0,"y":-211.1217,"x":612.3422,"size":11.421053}}} +{"ae":{"052168689X_158488388X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"052168689X","r":-1,"directed":false,"target":"158488388X"}}} +{"ae":{"052168689X_0387251456":{"g":0,"weight":1,"Weight":1,"b":0,"source":"052168689X","r":-1,"directed":false,"target":"0387251456"}}} +{"ae":{"0387402721_052168689X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387402721","r":-1,"directed":false,"target":"052168689X"}}} +{"an":{"0387212396":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Monte Carlo Sta...","full-label":"Monte Carlo Statistical Methods (Springer Texts in Statistics)","z":0,"y":-108.19612,"x":584.9987,"size":8.263158}}} +{"ae":{"0387952845_0387212396":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387952845","r":-1,"directed":false,"target":"0387212396"}}} +{"ae":{"158488388X_0387212396":{"g":0,"weight":1,"Weight":1,"b":0,"source":"158488388X","r":-1,"directed":false,"target":"0387212396"}}} +{"ae":{"052168689X_0387212396":{"g":0,"weight":1,"Weight":1,"b":0,"source":"052168689X","r":-1,"directed":false,"target":"0387212396"}}} +{"an":{"0387713840":{"g":0.6117647,"b":0.43137255,"r":0.007843138,"Label":"Bayesian Comput...","full-label":"Bayesian Computation with R (Use R)","z":0,"y":-162.64337,"x":702.4732,"size":8.263158}}} +{"ae":{"0387713840_052168689X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387713840","r":-1,"directed":false,"target":"052168689X"}}} +{"ae":{"158488388X_0387713840":{"g":0,"weight":1,"Weight":1,"b":0,"source":"158488388X","r":-1,"directed":false,"target":"0387713840"}}} +{"an":{"0340814055":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Bayesian Statis...","full-label":"Bayesian Statistics: An Introduction (Arnold Publication)","z":0,"y":-85.99846,"x":675.00586,"size":7.2105265}}} +{"ae":{"158488388X_0340814055":{"g":0,"weight":1,"Weight":1,"b":0,"source":"158488388X","r":-1,"directed":false,"target":"0340814055"}}} +{"ae":{"052168689X_0340814055":{"g":0,"weight":1,"Weight":1,"b":0,"source":"052168689X","r":-1,"directed":false,"target":"0340814055"}}} +{"ae":{"0387212396_0340814055":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387212396","r":-1,"directed":false,"target":"0340814055"}}} +{"an":{"0130085197":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Digital Image P...","full-label":"Digital Image Processing Using MATLAB(R)","z":0,"y":-455.24603,"x":448.78314,"size":7.2105265}}} +{"ae":{"0471056693_0130085197":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0471056693","r":-1,"directed":false,"target":"0130085197"}}} +{"ae":{"0471429775_0130085197":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0471429775","r":-1,"directed":false,"target":"0130085197"}}} +{"an":{"0198538642":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Neural Networks...","full-label":"Neural Networks for Pattern Recognition","z":0,"y":-348.96432,"x":394.62173,"size":7.2105265}}} +{"ae":{"0387310738_0198538642":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0198538642"}}} +{"ae":{"0471056693_0198538642":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0471056693","r":-1,"directed":false,"target":"0198538642"}}} +{"ae":{"0471429775_0198538642":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0471429775","r":-1,"directed":false,"target":"0198538642"}}} +{"an":{"0321321367":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Introduction to...","full-label":"Introduction to Data Mining","z":0,"y":-94.97894,"x":274.04404,"size":7.2105265}}} +{"ae":{"0596529325_0321321367":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"0321321367"}}} +{"ae":{"0387310738_0321321367":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0321321367"}}} +{"ae":{"0387952845_0321321367":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387952845","r":-1,"directed":false,"target":"0321321367"}}} +{"ae":{"0120884070_0321321367":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0120884070","r":-1,"directed":false,"target":"0321321367"}}} +{"ae":{"1558609016_0321321367":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1558609016","r":-1,"directed":false,"target":"0321321367"}}} +{"an":{"0321356985":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Practical Busin...","full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)","z":0,"y":-405.2215,"x":326.93936,"size":7.2105265}}} +{"ae":{"1558609016_0321356985":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1558609016","r":-1,"directed":false,"target":"0321356985"}}} +{"an":{"0521780195":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"An Introduction...","full-label":"An Introduction to Support Vector Machines and Other Kernel-based Learning Methods","z":0,"y":-107.06083,"x":364.59818,"size":7.2105265}}} +{"ae":{"0387310738_0521780195":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0521780195"}}} +{"ae":{"0387952845_0521780195":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387952845","r":-1,"directed":false,"target":"0521780195"}}} +{"ae":{"0120884070_0521780195":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0120884070","r":-1,"directed":false,"target":"0521780195"}}} +{"ae":{"0262194759_0521780195":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262194759","r":-1,"directed":false,"target":"0521780195"}}} +{"an":{"0521813972":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Kernel Methods ...","full-label":"Kernel Methods for Pattern Analysis","z":0,"y":-236.61261,"x":422.45425,"size":7.2105265}}} +{"ae":{"0521813972_0521780195":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521813972","r":-1,"directed":false,"target":"0521780195"}}} +{"ae":{"0387310738_0521813972":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0521813972"}}} +{"ae":{"0387952845_0521813972":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387952845","r":-1,"directed":false,"target":"0521813972"}}} +{"ae":{"0471056693_0521813972":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0471056693","r":-1,"directed":false,"target":"0521813972"}}} +{"ae":{"0262194759_0521813972":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262194759","r":-1,"directed":false,"target":"0521813972"}}} +{"an":{"0262026171":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Predicting Stru...","full-label":"Predicting Structured Data (Neural Information Processing)","z":0,"y":-64.705124,"x":225.3389,"size":7.2105265}}} +{"ae":{"0596529325_0262026171":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"0262026171"}}} +{"ae":{"0387310738_0262026171":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0262026171"}}} +{"ae":{"0262033585_0262026171":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262033585","r":-1,"directed":false,"target":"0262026171"}}} +{"an":{"0262026252":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Large-Scale Ker...","full-label":"Large-Scale Kernel Machines (Neural Information Processing)","z":0,"y":-2.0626152,"x":227.94237,"size":7.2105265}}} +{"ae":{"0262026252_0262026171":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262026252","r":-1,"directed":false,"target":"0262026171"}}} +{"ae":{"0596529325_0262026252":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596529325","r":-1,"directed":false,"target":"0262026252"}}} +{"ae":{"0262033585_0262026252":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262033585","r":-1,"directed":false,"target":"0262026252"}}} +{"an":{"0521592712":{"g":0.5137255,"b":0.54509807,"r":0.003921569,"Label":"Probability The...","full-label":"Probability Theory: The Logic of Science","z":0,"y":-64.77292,"x":524.1757,"size":7.2105265}}} +{"ae":{"0387310738_0521592712":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387310738","r":-1,"directed":false,"target":"0521592712"}}} +{"ae":{"0521642981_0521592712":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521642981","r":-1,"directed":false,"target":"0521592712"}}} +{"an":{"0471241954":{"g":0.11764706,"b":1,"r":0,"Label":"Information The...","full-label":"Information Theory, Inference & Learning Algorithms","z":0,"y":-12.986139,"x":547.7208,"size":3}}} +{"ae":{"0521642981_0471241954":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521642981","r":-1,"directed":false,"target":"0471241954"}}} +{"an":{"0387984887":{"g":0.11764706,"b":1,"r":0,"Label":"Introductory Gr...","full-label":"Introductory Graph Theory","z":0,"y":-723.9367,"x":238.72516,"size":3}}} +{"ae":{"0486247759_0387984887":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486247759","r":-1,"directed":false,"target":"0387984887"}}} +{"an":{"0486432327":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Introductory Gr...","full-label":"Introductory Graph Theory","z":0,"y":-658.5119,"x":233.28464,"size":4.0526314}}} +{"ae":{"0486432327_0486678709":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486432327","r":-1,"directed":false,"target":"0486678709"}}} +{"ae":{"0486247759_0486432327":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486247759","r":-1,"directed":false,"target":"0486432327"}}} +{"an":{"0070054894":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Introductory Gr...","full-label":"Introductory Graph Theory","z":0,"y":-680.18835,"x":167.87582,"size":4.0526314}}} +{"ae":{"0070054894_0486678709":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0070054894","r":-1,"directed":false,"target":"0486678709"}}} +{"ae":{"0486247759_0070054894":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486247759","r":-1,"directed":false,"target":"0070054894"}}} +{"an":{"0199218420":{"g":0.11764706,"b":1,"r":0,"Label":"Introduction to...","full-label":"Introduction to Graph Theory (Dover Books on Advanced Mathematics)","z":0,"y":-744.3491,"x":158.60902,"size":3}}} +{"ae":{"0486678709_0199218420":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486678709","r":-1,"directed":false,"target":"0199218420"}}} +{"an":{"0674341163":{"g":0.11764706,"b":1,"r":0,"Label":"Algorithmic Gam...","full-label":"Algorithmic Game Theory","z":0,"y":-533.2027,"x":156.52112,"size":3}}} +{"ae":{"0521872820_0674341163":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521872820","r":-1,"directed":false,"target":"0674341163"}}} +{"an":{"0262033429":{"g":0.11764706,"b":1,"r":0,"Label":"Algorithmic Gam...","full-label":"Algorithmic Game Theory","z":0,"y":-508.34384,"x":224.90204,"size":3}}} +{"ae":{"0521872820_0262033429":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521872820","r":-1,"directed":false,"target":"0262033429"}}} +{"an":{"0262650401":{"g":0.11764706,"b":1,"r":0,"Label":"Algorithmic Gam...","full-label":"Algorithmic Game Theory","z":0,"y":-444.0247,"x":261.9213,"size":3}}} +{"ae":{"0521872820_0262650401":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521872820","r":-1,"directed":false,"target":"0262650401"}}} +{"an":{"0596005954":{"g":0.11764706,"b":1,"r":0,"Label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)","z":0,"y":-595.48,"x":49.774117,"size":3}}} +{"ae":{"B00007FYIJ_0596005954":{"g":0,"weight":1,"Weight":1,"b":0,"source":"B00007FYIJ","r":-1,"directed":false,"target":"0596005954"}}} +{"an":{"0596528124":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)","z":0,"y":-469.0175,"x":-35.77359,"size":4.0526314}}} +{"ae":{"0596528124_020107981X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596528124","r":-1,"directed":false,"target":"020107981X"}}} +{"ae":{"B00007FYIJ_0596528124":{"g":0,"weight":1,"Weight":1,"b":0,"source":"B00007FYIJ","r":-1,"directed":false,"target":"0596528124"}}} +{"an":{"0596009658":{"g":0.11764706,"b":1,"r":0,"Label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)","z":0,"y":-566.7628,"x":-33.898182,"size":3}}} +{"ae":{"B00007FYIJ_0596009658":{"g":0,"weight":1,"Weight":1,"b":0,"source":"B00007FYIJ","r":-1,"directed":false,"target":"0596009658"}}} +{"an":{"B00007FYGT":{"g":0.11764706,"b":1,"r":0,"Label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)","z":0,"y":-664.65,"x":33.833057,"size":3}}} +{"ae":{"B00007FYIJ_B00007FYGT":{"g":0,"weight":1,"Weight":1,"b":0,"source":"B00007FYIJ","r":-1,"directed":false,"target":"B00007FYGT"}}} +{"an":{"0596003528":{"g":0.11764706,"b":1,"r":0,"Label":"sed & awk (2nd ...","full-label":"sed & awk (2nd Edition)","z":0,"y":-661.5192,"x":95.11659,"size":3}}} +{"ae":{"B00007FYIJ_0596003528":{"g":0,"weight":1,"Weight":1,"b":0,"source":"B00007FYIJ","r":-1,"directed":false,"target":"0596003528"}}} +{"an":{"1558603204":{"g":0.11764706,"b":1,"r":0,"Label":"Engineering a C...","full-label":"Engineering a Compiler","z":0,"y":-354.21463,"x":237.20203,"size":3}}} +{"ae":{"155860698X_1558603204":{"g":0,"weight":1,"Weight":1,"b":0,"source":"155860698X","r":-1,"directed":false,"target":"1558603204"}}} +{"an":{"1558602860":{"g":0.11764706,"b":1,"r":0,"Label":"Engineering a C...","full-label":"Engineering a Compiler","z":0,"y":-338.80972,"x":293.46094,"size":3}}} +{"ae":{"155860698X_1558602860":{"g":0,"weight":1,"Weight":1,"b":0,"source":"155860698X","r":-1,"directed":false,"target":"1558602860"}}} +{"an":{"0072231459":{"g":0.11764706,"b":1,"r":0,"Label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)","z":0,"y":-588.454,"x":262.54166,"size":3}}} +{"ae":{"0072253517_0072231459":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0072253517","r":-1,"directed":false,"target":"0072231459"}}} +{"an":{"0072230665":{"g":0.11764706,"b":1,"r":0,"Label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)","z":0,"y":-632.1786,"x":317.4004,"size":3}}} +{"ae":{"0072253517_0072230665":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0072253517","r":-1,"directed":false,"target":"0072230665"}}} +{"an":{"0072263172":{"g":0.11764706,"b":1,"r":0,"Label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)","z":0,"y":-691.4929,"x":313.34164,"size":3}}} +{"ae":{"0072253517_0072263172":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0072253517","r":-1,"directed":false,"target":"0072263172"}}} +{"an":{"0072263059":{"g":0.11764706,"b":1,"r":0,"Label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)","z":0,"y":-650.8133,"x":386.43298,"size":3}}} +{"ae":{"0072253517_0072263059":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0072253517","r":-1,"directed":false,"target":"0072263059"}}} +{"an":{"0072230789":{"g":0.11764706,"b":1,"r":0,"Label":"Oracle Database...","full-label":"Oracle Database 10g: The Complete Reference (Osborne ORACLE Press Series)","z":0,"y":-570.23303,"x":342.69553,"size":3}}} +{"ae":{"0072253517_0072230789":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0072253517","r":-1,"directed":false,"target":"0072230789"}}} +{"an":{"1886411972":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Write Great Cod...","full-label":"Write Great Code: Volume 1: Understanding the Machine","z":0,"y":114.039055,"x":-80.567184,"size":4.0526314}}} +{"ae":{"1886411972_1593270658":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1886411972","r":-1,"directed":false,"target":"1593270658"}}} +{"ae":{"1593270038_1886411972":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1593270038","r":-1,"directed":false,"target":"1886411972"}}} +{"an":{"0521520436":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Write Great Cod...","full-label":"Write Great Code: Volume 1: Understanding the Machine","z":0,"y":132.582,"x":-10.431211,"size":4.0526314}}} +{"ae":{"0521520436_1593270658":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521520436","r":-1,"directed":false,"target":"1593270658"}}} +{"ae":{"1593270038_0521520436":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1593270038","r":-1,"directed":false,"target":"0521520436"}}} +{"an":{"1593270569":{"g":0.11764706,"b":1,"r":0,"Label":"Write Great Cod...","full-label":"Write Great Code, Volume 2: Thinking Low-Level, Writing High-Level","z":0,"y":48.569935,"x":48.87398,"size":3}}} +{"ae":{"1593270658_1593270569":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1593270658","r":-1,"directed":false,"target":"1593270569"}}} +{"an":{"0672325861":{"g":0.11764706,"b":1,"r":0,"Label":"Programming in ...","full-label":"Programming in C (3rd Edition) (Developer's Library)","z":0,"y":-353.59326,"x":-358.4906,"size":3}}} +{"ae":{"0672326663_0672325861":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0672326663","r":-1,"directed":false,"target":"0672325861"}}} +{"an":{"0201604612":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"C Traps and Pit...","full-label":"C Traps and Pitfalls","z":0,"y":-485.2193,"x":-244.09819,"size":4.0526314}}} +{"ae":{"0201604612_0131774298":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201604612","r":-1,"directed":false,"target":"0131774298"}}} +{"ae":{"0201179288_0201604612":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201179288","r":-1,"directed":false,"target":"0201604612"}}} +{"an":{"0673999866":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"C Traps and Pit...","full-label":"C Traps and Pitfalls","z":0,"y":-418.40884,"x":-255.2087,"size":4.0526314}}} +{"ae":{"0673999866_0131774298":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0673999866","r":-1,"directed":false,"target":"0131774298"}}} +{"ae":{"0201179288_0673999866":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201179288","r":-1,"directed":false,"target":"0673999866"}}} +{"an":{"0764568523":{"g":0.11764706,"b":1,"r":0,"Label":"C For Dummies, ...","full-label":"C For Dummies, 2nd Edition","z":0,"y":-279.16614,"x":-355.41663,"size":3}}} +{"ae":{"0764570684_0764568523":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0764570684","r":-1,"directed":false,"target":"0764568523"}}} +{"an":{"0470088702":{"g":0.11764706,"b":1,"r":0,"Label":"C For Dummies, ...","full-label":"C For Dummies, 2nd Edition","z":0,"y":-138.21872,"x":-268.42166,"size":3}}} +{"ae":{"0764570684_0470088702":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0764570684","r":-1,"directed":false,"target":"0470088702"}}} +{"an":{"0672326973":{"g":0.11764706,"b":1,"r":0,"Label":"C Primer Plus (...","full-label":"C Primer Plus (5th Edition)","z":0,"y":-420.49127,"x":-318.6007,"size":3}}} +{"ae":{"0672326965_0672326973":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0672326965","r":-1,"directed":false,"target":"0672326973"}}} +{"an":{"0764517953":{"g":0.11764706,"b":1,"r":0,"Label":"C All-in-One De...","full-label":"C All-in-One Desk Reference for Dummies","z":0,"y":-426.81747,"x":-388.88943,"size":3}}} +{"ae":{"0764570692_0764517953":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0764570692","r":-1,"directed":false,"target":"0764517953"}}} +{"an":{"0470124512":{"g":0.11764706,"b":1,"r":0,"Label":"C All-in-One De...","full-label":"C All-in-One Desk Reference for Dummies","z":0,"y":-485.11365,"x":-329.20377,"size":3}}} +{"ae":{"0764570692_0470124512":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0764570692","r":-1,"directed":false,"target":"0470124512"}}} +{"an":{"020163354X":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Unix Network Pr...","full-label":"Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition) (Addison-Wesley Professional Computing Series)","z":0,"y":-674.7581,"x":-159.23567,"size":4.0526314}}} +{"ae":{"020163354X_0130810819":{"g":0,"weight":1,"Weight":1,"b":0,"source":"020163354X","r":-1,"directed":false,"target":"0130810819"}}} +{"ae":{"0131411551_020163354X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131411551","r":-1,"directed":false,"target":"020163354X"}}} +{"an":{"0201633469":{"g":0.11764706,"b":1,"r":0,"Label":"Unix Network Pr...","full-label":"Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition) (Addison-Wesley Professional Computing Series)","z":0,"y":-736.2101,"x":-109.269646,"size":3}}} +{"ae":{"0131411551_0201633469":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131411551","r":-1,"directed":false,"target":"0201633469"}}} +{"an":{"013490012X":{"g":0.11764706,"b":1,"r":0,"Label":"UNIX Network Pr...","full-label":"UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition) (The Unix Networking Reference Series , Vol 2)","z":0,"y":-741.6606,"x":-177.12825,"size":3}}} +{"ae":{"0130810819_013490012X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0130810819","r":-1,"directed":false,"target":"013490012X"}}} +{"an":{"0201633922":{"g":0.11764706,"b":1,"r":0,"Label":"UNIX Network Pr...","full-label":"UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition) (The Unix Networking Reference Series , Vol 2)","z":0,"y":-716.23254,"x":-238.79796,"size":3}}} +{"ae":{"0130810819_0201633922":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0130810819","r":-1,"directed":false,"target":"0201633922"}}} +{"an":{"0130424110":{"g":0.11764706,"b":1,"r":0,"Label":"Advanced UNIX P...","full-label":"Advanced UNIX Programming (2nd Edition) (Addison-Wesley Professional Computing Series)","z":0,"y":-547.35785,"x":-280.84406,"size":3}}} +{"ae":{"0131411543_0130424110":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131411543","r":-1,"directed":false,"target":"0130424110"}}} +{"an":{"B00006AVQ0":{"g":0.11764706,"b":1,"r":0,"Label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition","z":0,"y":-666.8465,"x":-86.44827,"size":3}}} +{"ae":{"0596005652_B00006AVQ0":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596005652","r":-1,"directed":false,"target":"B00006AVQ0"}}} +{"an":{"0596002556":{"g":0.11764706,"b":1,"r":0,"Label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition","z":0,"y":-754.8534,"x":28.5382,"size":3}}} +{"ae":{"0596005652_0596002556":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596005652","r":-1,"directed":false,"target":"0596002556"}}} +{"an":{"0672327201":{"g":0.11764706,"b":1,"r":0,"Label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition","z":0,"y":-703.9416,"x":-23.878574,"size":3}}} +{"ae":{"0596005652_0672327201":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596005652","r":-1,"directed":false,"target":"0672327201"}}} +{"an":{"0131679848":{"g":0.11764706,"b":1,"r":0,"Label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition","z":0,"y":-764.08167,"x":-45.112415,"size":3}}} +{"ae":{"0596005652_0131679848":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596005652","r":-1,"directed":false,"target":"0131679848"}}} +{"an":{"059600222X":{"g":0.11764706,"b":1,"r":0,"Label":"Understanding t...","full-label":"Understanding the Linux Kernel, Third Edition","z":0,"y":-739.4378,"x":87.27902,"size":3}}} +{"ae":{"0596005652_059600222X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596005652","r":-1,"directed":false,"target":"059600222X"}}} +{"an":{"0201710919":{"g":0.11764706,"b":1,"r":0,"Label":"Extreme Program...","full-label":"Extreme Programming Explained: Embrace Change (2nd Edition) (The XP Series)","z":0,"y":609.05396,"x":-221.86447,"size":3}}} +{"ae":{"0321278658_0201710919":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321278658","r":-1,"directed":false,"target":"0201710919"}}} +{"an":{"0131495054":{"g":0.11764706,"b":1,"r":0,"Label":"Working Effecti...","full-label":"Working Effectively with Legacy Code (Robert C. Martin Series)","z":0,"y":465.43176,"x":-208.24176,"size":3}}} +{"ae":{"0131177052_0131495054":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131177052","r":-1,"directed":false,"target":"0131495054"}}} +{"an":{"0321213351":{"g":0.11764706,"b":1,"r":0,"Label":"Working Effecti...","full-label":"Working Effectively with Legacy Code (Robert C. Martin Series)","z":0,"y":419.72504,"x":-137.90485,"size":3}}} +{"ae":{"0131177052_0321213351":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131177052","r":-1,"directed":false,"target":"0321213351"}}} +{"an":{"0131858580":{"g":0.11764706,"b":1,"r":0,"Label":"Enterprise Inte...","full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)","z":0,"y":611.7827,"x":-325.37512,"size":3}}} +{"ae":{"0321200683_0131858580":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321200683","r":-1,"directed":false,"target":"0131858580"}}} +{"an":{"0596006756":{"g":0.11764706,"b":1,"r":0,"Label":"Enterprise Inte...","full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)","z":0,"y":656.3292,"x":-274.1945,"size":3}}} +{"ae":{"0321200683_0596006756":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321200683","r":-1,"directed":false,"target":"0596006756"}}} +{"an":{"0132344823":{"g":0.11764706,"b":1,"r":0,"Label":"Enterprise Inte...","full-label":"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (The Addison-Wesley Signature Series)","z":0,"y":583.8467,"x":-390.451,"size":3}}} +{"ae":{"0321200683_0132344823":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321200683","r":-1,"directed":false,"target":"0132344823"}}} +{"an":{"0321268202":{"g":0.11764706,"b":1,"r":0,"Label":"Domain-Driven D...","full-label":"Domain-Driven Design: Tackling Complexity in the Heart of Software","z":0,"y":560.4819,"x":-265.32617,"size":3}}} +{"ae":{"0321125215_0321268202":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321125215","r":-1,"directed":false,"target":"0321268202"}}} +{"an":{"0596526849":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Head First Soft...","full-label":"Head First Software Development (Brain-Friendly Guides)","z":0,"y":534.1906,"x":-447.51886,"size":4.0526314}}} +{"ae":{"0596526849_059610197X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596526849","r":-1,"directed":false,"target":"059610197X"}}} +{"ae":{"0596527357_0596526849":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596527357","r":-1,"directed":false,"target":"0596526849"}}} +{"an":{"0596514824":{"g":0.11764706,"b":1,"r":0,"Label":"Head First Soft...","full-label":"Head First Software Development (Brain-Friendly Guides)","z":0,"y":544.29584,"x":-532.8829,"size":3}}} +{"ae":{"0596527357_0596514824":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596527357","r":-1,"directed":false,"target":"0596514824"}}} +{"an":{"0596527748":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Head First Soft...","full-label":"Head First Software Development (Brain-Friendly Guides)","z":0,"y":486.38263,"x":-494.813,"size":4.0526314}}} +{"ae":{"0596527748_059610197X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596527748","r":-1,"directed":false,"target":"059610197X"}}} +{"ae":{"0596527357_0596527748":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596527357","r":-1,"directed":false,"target":"0596527748"}}} +{"an":{"0596102259":{"g":0.11764706,"b":1,"r":0,"Label":"Head First HTML...","full-label":"Head First HTML with CSS & XHTML","z":0,"y":596.32294,"x":-472.2861,"size":3}}} +{"ae":{"059610197X_0596102259":{"g":0,"weight":1,"Weight":1,"b":0,"source":"059610197X","r":-1,"directed":false,"target":"0596102259"}}} +{"an":{"1590596978":{"g":0.11764706,"b":1,"r":0,"Label":"SCJP Sun Certif...","full-label":"SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press Study Guides)","z":0,"y":647.3819,"x":-405.7443,"size":3}}} +{"ae":{"0072253606_1590596978":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0072253606","r":-1,"directed":false,"target":"1590596978"}}} +{"an":{"1933988134":{"g":0.11764706,"b":1,"r":0,"Label":"SCJP Sun Certif...","full-label":"SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press Study Guides)","z":0,"y":684.1061,"x":-337.33966,"size":3}}} +{"ae":{"0072253606_1933988134":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0072253606","r":-1,"directed":false,"target":"1933988134"}}} +{"an":{"0201704315":{"g":0.11764706,"b":1,"r":0,"Label":"C++ Templates: ...","full-label":"C++ Templates: The Complete Guide","z":0,"y":128.935,"x":-368.1072,"size":3}}} +{"ae":{"0201734842_0201704315":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201734842","r":-1,"directed":false,"target":"0201704315"}}} +{"an":{"1592009123":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Writing Effecti...","full-label":"Writing Effective Use Cases (The Agile Software Development Series)","z":0,"y":564.33344,"x":-11.410382,"size":4.0526314}}} +{"ae":{"1592009123_1576810607":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1592009123","r":-1,"directed":false,"target":"1576810607"}}} +{"ae":{"0201702258_1592009123":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201702258","r":-1,"directed":false,"target":"1592009123"}}} +{"an":{"0321419499":{"g":0.11764706,"b":1,"r":0,"Label":"Writing Effecti...","full-label":"Writing Effective Use Cases (The Agile Software Development Series)","z":0,"y":523.61383,"x":-94.40438,"size":3}}} +{"ae":{"0201702258_0321419499":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0201702258","r":-1,"directed":false,"target":"0321419499"}}} +{"an":{"0393061140":{"g":0.11764706,"b":1,"r":0,"Label":"The Moscow Puzz...","full-label":"The Moscow Puzzles: 359 Mathematical Recreations (Math & Logic Puzzles)","z":0,"y":374.52405,"x":-476.51328,"size":3}}} +{"ae":{"0486270785_0393061140":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486270785","r":-1,"directed":false,"target":"0393061140"}}} +{"an":{"0970825307":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Brain Busters! ...","full-label":"Brain Busters! Mind-Stretching Puzzles in Math and Logic","z":0,"y":325.6344,"x":-521.7306,"size":4.0526314}}} +{"ae":{"0970825307_0970825315":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0970825307","r":-1,"directed":false,"target":"0970825315"}}} +{"ae":{"0486427552_0970825307":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0486427552","r":-1,"directed":false,"target":"0970825307"}}} +{"an":{"1885003994":{"g":0.11764706,"b":1,"r":0,"Label":"So You Think Yo...","full-label":"So You Think You're Smart: 150 Fun and Challenging Brain Teasers","z":0,"y":418.81104,"x":-533.7107,"size":3}}} +{"ae":{"0970825315_1885003994":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0970825315","r":-1,"directed":false,"target":"1885003994"}}} +{"an":{"0140318755":{"g":0.11764706,"b":1,"r":0,"Label":"So You Think Yo...","full-label":"So You Think You're Smart: 150 Fun and Challenging Brain Teasers","z":0,"y":477.80264,"x":-569.28815,"size":3}}} +{"ae":{"0970825315_0140318755":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0970825315","r":-1,"directed":false,"target":"0140318755"}}} +{"an":{"0321392353":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Information Arc...","full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites","z":0,"y":569.60986,"x":182.91531,"size":4.0526314}}} +{"ae":{"0321392353_0596008031":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321392353","r":-1,"directed":false,"target":"0596008031"}}} +{"ae":{"0596527349_0321392353":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596527349","r":-1,"directed":false,"target":"0321392353"}}} +{"an":{"0596007655":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Information Arc...","full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites","z":0,"y":502.7566,"x":179.6867,"size":4.0526314}}} +{"ae":{"0596007655_0596008031":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596007655","r":-1,"directed":false,"target":"0596008031"}}} +{"ae":{"0596527349_0596007655":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596527349","r":-1,"directed":false,"target":"0596007655"}}} +{"an":{"0735712026":{"g":0.11764706,"b":1,"r":0,"Label":"Information Arc...","full-label":"Information Architecture for the World Wide Web: Designing Large-Scale Web Sites","z":0,"y":730.85767,"x":84.787384,"size":3}}} +{"ae":{"0596527349_0735712026":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596527349","r":-1,"directed":false,"target":"0735712026"}}} +{"an":{"0321350316":{"g":0.11764706,"b":1,"r":0,"Label":"Don't Make Me T...","full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition","z":0,"y":523.7247,"x":50.705395,"size":3}}} +{"ae":{"0321344758_0321350316":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321344758","r":-1,"directed":false,"target":"0321350316"}}} +{"an":{"0465067107":{"g":0.11764706,"b":1,"r":0,"Label":"Don't Make Me T...","full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition","z":0,"y":593.9687,"x":72.58714,"size":3}}} +{"ae":{"0321344758_0465067107":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321344758","r":-1,"directed":false,"target":"0465067107"}}} +{"an":{"156205810X":{"g":0.11764706,"b":1,"r":0,"Label":"Don't Make Me T...","full-label":"Don't Make Me Think: A Common Sense Approach to Web Usability, 2nd Edition","z":0,"y":657.47644,"x":90.575966,"size":3}}} +{"ae":{"0321344758_156205810X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321344758","r":-1,"directed":false,"target":"156205810X"}}} +{"an":{"0262134748":{"g":0.11764706,"b":1,"r":0,"Label":"Designing Inter...","full-label":"Designing Interfaces: Patterns for Effective Interaction Design","z":0,"y":623.4221,"x":146.6429,"size":3}}} +{"ae":{"0596008031_0262134748":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596008031","r":-1,"directed":false,"target":"0262134748"}}} +{"an":{"0672328844":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Programming Rub...","full-label":"Programming Ruby: The Pragmatic Programmers' Guide, Second Edition","z":0,"y":310.36115,"x":709.19763,"size":4.0526314}}} +{"ae":{"0672328844_0596523696":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0672328844","r":-1,"directed":false,"target":"0596523696"}}} +{"ae":{"0974514055_0672328844":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0974514055","r":-1,"directed":false,"target":"0672328844"}}} +{"an":{"0596527314":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Ajax on Rails","full-label":"Ajax on Rails","z":0,"y":211.29233,"x":736.7965,"size":4.0526314}}} +{"ae":{"0596527314_0596523696":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596527314","r":-1,"directed":false,"target":"0596523696"}}} +{"ae":{"0596527446_0596527314":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596527446","r":-1,"directed":false,"target":"0596527314"}}} +{"an":{"0596101015":{"g":0.11764706,"b":1,"r":0,"Label":"MySQL Cookbook","full-label":"MySQL Cookbook","z":0,"y":151.80904,"x":688.94104,"size":3}}} +{"ae":{"059652708X_0596101015":{"g":0,"weight":1,"Weight":1,"b":0,"source":"059652708X","r":-1,"directed":false,"target":"0596101015"}}} +{"an":{"0596008643":{"g":0.11764706,"b":1,"r":0,"Label":"MySQL Cookbook","full-label":"MySQL Cookbook","z":0,"y":122.30027,"x":750.10034,"size":3}}} +{"ae":{"059652708X_0596008643":{"g":0,"weight":1,"Weight":1,"b":0,"source":"059652708X","r":-1,"directed":false,"target":"0596008643"}}} +{"an":{"0672327120":{"g":0.11764706,"b":1,"r":0,"Label":"MySQL Cookbook","full-label":"MySQL Cookbook","z":0,"y":74.74654,"x":703.43616,"size":3}}} +{"ae":{"059652708X_0672327120":{"g":0,"weight":1,"Weight":1,"b":0,"source":"059652708X","r":-1,"directed":false,"target":"0672327120"}}} +{"an":{"0672328704":{"g":0.11764706,"b":1,"r":0,"Label":"MySQL Stored Pr...","full-label":"MySQL Stored Procedure Programming","z":0,"y":100.1636,"x":634.91046,"size":3}}} +{"ae":{"0596100892_0672328704":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596100892","r":-1,"directed":false,"target":"0672328704"}}} +{"an":{"1590595351":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"MySQL Stored Pr...","full-label":"MySQL Stored Procedure Programming","z":0,"y":208.81096,"x":524.2969,"size":4.0526314}}} +{"ae":{"1590595351_159059505X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1590595351","r":-1,"directed":false,"target":"159059505X"}}} +{"ae":{"0596100892_1590595351":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0596100892","r":-1,"directed":false,"target":"1590595351"}}} +{"an":{"0071452818":{"g":0.11764706,"b":1,"r":0,"Label":"Physical Comput...","full-label":"Physical Computing: Sensing and Controlling the Physical World with Computers","z":0,"y":377.70667,"x":649.66473,"size":3}}} +{"ae":{"159200346X_0071452818":{"g":0,"weight":1,"Weight":1,"b":0,"source":"159200346X","r":-1,"directed":false,"target":"0071452818"}}} +{"an":{"0945053312":{"g":0.11764706,"b":1,"r":0,"Label":"Physical Comput...","full-label":"Physical Computing: Sensing and Controlling the Physical World with Computers","z":0,"y":381.1438,"x":569.95404,"size":3}}} +{"ae":{"159200346X_0945053312":{"g":0,"weight":1,"Weight":1,"b":0,"source":"159200346X","r":-1,"directed":false,"target":"0945053312"}}} +{"an":{"0262134721":{"g":0.11764706,"b":1,"r":0,"Label":"Creative Code: ...","full-label":"Creative Code: Aesthetics + Computation","z":0,"y":401.00623,"x":462.98563,"size":3}}} +{"ae":{"0500285179_0262134721":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0500285179","r":-1,"directed":false,"target":"0262134721"}}} +{"an":{"0262632446":{"g":0.11764706,"b":1,"r":0,"Label":"Creative Code: ...","full-label":"Creative Code: Aesthetics + Computation","z":0,"y":481.9665,"x":485.50568,"size":3}}} +{"ae":{"0500285179_0262632446":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0500285179","r":-1,"directed":false,"target":"0262632446"}}} +{"an":{"0789305259":{"g":0.11764706,"b":1,"r":0,"Label":"Creative Code: ...","full-label":"Creative Code: Aesthetics + Computation","z":0,"y":458.5767,"x":419.96222,"size":3}}} +{"ae":{"0500285179_0789305259":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0500285179","r":-1,"directed":false,"target":"0789305259"}}} +{"an":{"0596514921":{"g":0.11764706,"b":1,"r":0,"Label":"The Best of MAK...","full-label":"The Best of MAKE (Make)","z":0,"y":491.1052,"x":578.5498,"size":3}}} +{"ae":{"059651428X_0596514921":{"g":0,"weight":1,"Weight":1,"b":0,"source":"059651428X","r":-1,"directed":false,"target":"0596514921"}}} +{"an":{"0596510543":{"g":0.11764706,"b":1,"r":0,"Label":"The Best of MAK...","full-label":"The Best of MAKE (Make)","z":0,"y":539.2342,"x":533.1962,"size":3}}} +{"ae":{"059651428X_0596510543":{"g":0,"weight":1,"Weight":1,"b":0,"source":"059651428X","r":-1,"directed":false,"target":"0596510543"}}} +{"an":{"B0007RNI5K":{"g":0.11764706,"b":1,"r":0,"Label":"The Best of MAK...","full-label":"The Best of MAKE (Make)","z":0,"y":439.91678,"x":622.054,"size":3}}} +{"ae":{"059651428X_B0007RNI5K":{"g":0,"weight":1,"Weight":1,"b":0,"source":"059651428X","r":-1,"directed":false,"target":"B0007RNI5K"}}} +{"an":{"0142005207":{"g":0.11764706,"b":1,"r":0,"Label":"The Back of the...","full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures","z":0,"y":635.2183,"x":426.578,"size":3}}} +{"ae":{"1591841992_0142005207":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1591841992","r":-1,"directed":false,"target":"0142005207"}}} +{"an":{"1594482918":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"The Back of the...","full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures","z":0,"y":647.67004,"x":301.49673,"size":4.0526314}}} +{"ae":{"1594482918_0979777704":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1594482918","r":-1,"directed":false,"target":"0979777704"}}} +{"ae":{"1591841992_1594482918":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1591841992","r":-1,"directed":false,"target":"1594482918"}}} +{"an":{"006135323X":{"g":0.11764706,"b":1,"r":0,"Label":"The Back of the...","full-label":"The Back of the Napkin: Solving Problems and Selling Ideas with Pictures","z":0,"y":585.92194,"x":477.09006,"size":3}}} +{"ae":{"1591841992_006135323X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1591841992","r":-1,"directed":false,"target":"006135323X"}}} +{"an":{"0131875108":{"g":0.11764706,"b":1,"r":0,"Label":"Beyond Bullet P...","full-label":"Beyond Bullet Points: Using Microsoft® Office PowerPoint® 2007 to Create Presentations That Inform, Motivate, and Inspire","z":0,"y":532.1315,"x":434.25726,"size":3}}} +{"ae":{"0735623872_0131875108":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735623872","r":-1,"directed":false,"target":"0131875108"}}} +{"an":{"0615142230":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Beyond Bullet P...","full-label":"Beyond Bullet Points: Using Microsoft® Office PowerPoint® 2007 to Create Presentations That Inform, Motivate, and Inspire","z":0,"y":593.4767,"x":379.72318,"size":4.0526314}}} +{"ae":{"0615142230_0195320697":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0615142230","r":-1,"directed":false,"target":"0195320697"}}} +{"ae":{"0735623872_0615142230":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735623872","r":-1,"directed":false,"target":"0615142230"}}} +{"an":{"0316113506":{"g":0.11764706,"b":1,"r":0,"Label":"Brain Rules: 12...","full-label":"Brain Rules: 12 Principles for Surviving and Thriving at Work, Home, and School (Book & DVD)","z":0,"y":676.97363,"x":357.69986,"size":3}}} +{"ae":{"0979777704_0316113506":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0979777704","r":-1,"directed":false,"target":"0316113506"}}} +{"an":{"1596912839":{"g":0.11764706,"b":1,"r":0,"Label":"Brain Rules: 12...","full-label":"Brain Rules: 12 Principles for Surviving and Thriving at Work, Home, and School (Book & DVD)","z":0,"y":707.7223,"x":276.19553,"size":3}}} +{"ae":{"0979777704_1596912839":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0979777704","r":-1,"directed":false,"target":"1596912839"}}} +{"an":{"1591841666":{"g":0.11764706,"b":1,"r":0,"Label":"Made to Stick: ...","full-label":"Made to Stick: Why Some Ideas Survive and Others Die","z":0,"y":727.2439,"x":145.80682,"size":3}}} +{"ae":{"1400064287_1591841666":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1400064287","r":-1,"directed":false,"target":"1591841666"}}} +{"an":{"0307341518":{"g":0.11764706,"b":1,"r":0,"Label":"Made to Stick: ...","full-label":"Made to Stick: Why Some Ideas Survive and Others Die","z":0,"y":736.4305,"x":208.01755,"size":3}}} +{"ae":{"1400064287_0307341518":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1400064287","r":-1,"directed":false,"target":"0307341518"}}} +{"an":{"1591841437":{"g":0.11764706,"b":1,"r":0,"Label":"Made to Stick: ...","full-label":"Made to Stick: Why Some Ideas Survive and Others Die","z":0,"y":671.71924,"x":202.16228,"size":3}}} +{"ae":{"1400064287_1591841437":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1400064287","r":-1,"directed":false,"target":"1591841437"}}} +{"an":{"088730995X":{"g":0.11764706,"b":1,"r":0,"Label":"The Art of the ...","full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything","z":0,"y":427.41473,"x":217.43306,"size":3}}} +{"ae":{"1591840562_088730995X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1591840562","r":-1,"directed":false,"target":"088730995X"}}} +{"an":{"0060517123":{"g":0.11764706,"b":1,"r":0,"Label":"The Art of the ...","full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything","z":0,"y":359.8359,"x":298.51633,"size":3}}} +{"ae":{"1591840562_0060517123":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1591840562","r":-1,"directed":false,"target":"0060517123"}}} +{"an":{"0971187304":{"g":0.11764706,"b":1,"r":0,"Label":"The Art of the ...","full-label":"The Art of the Start: The Time-Tested, Battle-Hardened Guide for Anyone Starting Anything","z":0,"y":493.63806,"x":241.9685,"size":3}}} +{"ae":{"1591840562_0971187304":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1591840562","r":-1,"directed":false,"target":"0971187304"}}} +{"an":{"0596527756":{"g":0.11764706,"b":1,"r":0,"Label":"Java Concurrenc...","full-label":"Java Concurrency in Practice","z":0,"y":-13.689792,"x":460.86444,"size":3}}} +{"ae":{"0321349601_0596527756":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321349601","r":-1,"directed":false,"target":"0596527756"}}} +{"an":{"0321356683":{"g":0.11764706,"b":1,"r":0,"Label":"Java Concurrenc...","full-label":"Java Concurrency in Practice","z":0,"y":44.34194,"x":477.8656,"size":3}}} +{"ae":{"0321349601_0321356683":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321349601","r":-1,"directed":false,"target":"0321356683"}}} +{"an":{"032133678X":{"g":0.11764706,"b":1,"r":0,"Label":"Java Concurrenc...","full-label":"Java Concurrency in Practice","z":0,"y":118.60619,"x":451.26514,"size":3}}} +{"ae":{"0321349601_032133678X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321349601","r":-1,"directed":false,"target":"032133678X"}}} +{"an":{"1932394885":{"g":0.11764706,"b":1,"r":0,"Label":"Java Concurrenc...","full-label":"Java Concurrency in Practice","z":0,"y":84.29291,"x":527.02313,"size":3}}} +{"ae":{"0321349601_1932394885":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321349601","r":-1,"directed":false,"target":"1932394885"}}} +{"an":{"0201310058":{"g":0.11764706,"b":1,"r":0,"Label":"Java Concurrenc...","full-label":"Java Concurrency in Practice","z":0,"y":14.863149,"x":376.19165,"size":3}}} +{"ae":{"0321349601_0201310058":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321349601","r":-1,"directed":false,"target":"0201310058"}}} +{"an":{"0201342758":{"g":0.11764706,"b":1,"r":0,"Label":"The Haskell Sch...","full-label":"The Haskell School of Expression: Learning Functional Programming through Multimedia","z":0,"y":290.0569,"x":254.46898,"size":3}}} +{"ae":{"0521644089_0201342758":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521644089","r":-1,"directed":false,"target":"0201342758"}}} +{"an":{"0954300653":{"g":0.11764706,"b":1,"r":0,"Label":"The Haskell Roa...","full-label":"The Haskell Road to Logic, Maths and Programming (Texts in Computing S.)","z":0,"y":336.99832,"x":134.02669,"size":3}}} +{"ae":{"0954300696_0954300653":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0954300696","r":-1,"directed":false,"target":"0954300653"}}} +{"an":{"1581313055":{"g":0.11764706,"b":1,"r":0,"Label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition","z":0,"y":364.87656,"x":-582.2536,"size":3}}} +{"ae":{"0971015848_1581313055":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0971015848","r":-1,"directed":false,"target":"1581313055"}}} +{"an":{"0471757225":{"g":0.11764706,"b":1,"r":0,"Label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition","z":0,"y":314.81964,"x":-640.98413,"size":3}}} +{"ae":{"0971015848_0471757225":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0971015848","r":-1,"directed":false,"target":"0471757225"}}} +{"an":{"0070534489":{"g":0.11764706,"b":1,"r":0,"Label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition","z":0,"y":378.6172,"x":-657.1407,"size":3}}} +{"ae":{"0971015848_0070534489":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0971015848","r":-1,"directed":false,"target":"0070534489"}}} +{"an":{"0979003903":{"g":0.11764706,"b":1,"r":0,"Label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition","z":0,"y":275.52344,"x":-598.27454,"size":3}}} +{"ae":{"0971015848_0979003903":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0971015848","r":-1,"directed":false,"target":"0979003903"}}} +{"an":{"0471444014":{"g":0.11764706,"b":1,"r":0,"Label":"Case in Point:C...","full-label":"Case in Point:Complete Case Interview Preparation - 5th edition","z":0,"y":430.77203,"x":-616.5192,"size":3}}} +{"ae":{"0971015848_0471444014":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0971015848","r":-1,"directed":false,"target":"0471444014"}}} +{"an":{"0486296725":{"g":0.11764706,"b":1,"r":0,"Label":"Prisoner's Dile...","full-label":"Prisoner's Dilemma","z":0,"y":-259.3475,"x":-414.32166,"size":3}}} +{"ae":{"038541580X_0486296725":{"g":0,"weight":1,"Weight":1,"b":0,"source":"038541580X","r":-1,"directed":false,"target":"0486296725"}}} +{"an":{"0486251012":{"g":0.11764706,"b":1,"r":0,"Label":"Prisoner's Dile...","full-label":"Prisoner's Dilemma","z":0,"y":-195.45746,"x":-452.7332,"size":3}}} +{"ae":{"038541580X_0486251012":{"g":0,"weight":1,"Weight":1,"b":0,"source":"038541580X","r":-1,"directed":false,"target":"0486251012"}}} +{"an":{"0465005640":{"g":0.11764706,"b":1,"r":0,"Label":"Prisoner's Dile...","full-label":"Prisoner's Dilemma","z":0,"y":-174.9718,"x":-331.02826,"size":3}}} +{"ae":{"038541580X_0465005640":{"g":0,"weight":1,"Weight":1,"b":0,"source":"038541580X","r":-1,"directed":false,"target":"0465005640"}}} +{"an":{"0812975219":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street","z":0,"y":-310.72745,"x":-466.21826,"size":4.0526314}}} +{"ae":{"0812975219_0470192739":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0812975219","r":-1,"directed":false,"target":"0470192739"}}} +{"ae":{"0809045990_0812975219":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0809045990","r":-1,"directed":false,"target":"0812975219"}}} +{"an":{"047004389X":{"g":0.11764706,"b":1,"r":0,"Label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street","z":0,"y":-231.6494,"x":-649.9751,"size":3}}} +{"ae":{"0809045990_047004389X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0809045990","r":-1,"directed":false,"target":"047004389X"}}} +{"an":{"0231143729":{"g":0.11764706,"b":1,"r":0,"Label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street","z":0,"y":-297.3498,"x":-618.3281,"size":3}}} +{"ae":{"0809045990_0231143729":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0809045990","r":-1,"directed":false,"target":"0231143729"}}} +{"an":{"1400063515":{"g":0.11764706,"b":1,"r":0,"Label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street","z":0,"y":-246.68918,"x":-575.6279,"size":3}}} +{"ae":{"0809045990_1400063515":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0809045990","r":-1,"directed":false,"target":"1400063515"}}} +{"an":{"0471794473":{"g":0.11764706,"b":1,"r":0,"Label":"Fortune's Formu...","full-label":"Fortune's Formula: The Untold Story of the Scientific Betting System That Beat the Casinos and Wall Street","z":0,"y":-282.87665,"x":-517.4418,"size":3}}} +{"ae":{"0809045990_0471794473":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0809045990","r":-1,"directed":false,"target":"0471794473"}}} +{"an":{"0691121273":{"g":0.11764706,"b":1,"r":0,"Label":"Paradoxes","full-label":"Paradoxes","z":0,"y":-140.97049,"x":-646.463,"size":3}}} +{"ae":{"0521483476_0691121273":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521483476","r":-1,"directed":false,"target":"0691121273"}}} +{"an":{"0415228093":{"g":0.11764706,"b":1,"r":0,"Label":"Paradoxes","full-label":"Paradoxes","z":0,"y":-229.17096,"x":-716.3982,"size":3}}} +{"ae":{"0521483476_0415228093":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521483476","r":-1,"directed":false,"target":"0415228093"}}} +{"an":{"0195179862":{"g":0.11764706,"b":1,"r":0,"Label":"Paradoxes","full-label":"Paradoxes","z":0,"y":-93.88228,"x":-561.39484,"size":3}}} +{"ae":{"0521483476_0195179862":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521483476","r":-1,"directed":false,"target":"0195179862"}}} +{"an":{"0486296644":{"g":0.11764706,"b":1,"r":0,"Label":"Paradoxes","full-label":"Paradoxes","z":0,"y":-180.61368,"x":-599.8265,"size":3}}} +{"ae":{"0521483476_0486296644":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521483476","r":-1,"directed":false,"target":"0486296644"}}} +{"an":{"0131499068":{"g":0.11764706,"b":1,"r":0,"Label":"Options, Future...","full-label":"Options, Futures and Other Derivatives (6th Edition)","z":0,"y":-539.9033,"x":-446.1053,"size":3}}} +{"ae":{"0131499084_0131499068":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131499084","r":-1,"directed":false,"target":"0131499068"}}} +{"an":{"0387249680":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Options, Future...","full-label":"Options, Futures and Other Derivatives (6th Edition)","z":0,"y":-540.58734,"x":-521.3218,"size":4.0526314}}} +{"ae":{"0387249680_0387401016":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387249680","r":-1,"directed":false,"target":"0387401016"}}} +{"ae":{"0131499084_0387249680":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131499084","r":-1,"directed":false,"target":"0387249680"}}} +{"an":{"155738486X":{"g":0.11764706,"b":1,"r":0,"Label":"Options, Future...","full-label":"Options, Futures and Other Derivatives (6th Edition)","z":0,"y":-596.14856,"x":-477.81775,"size":3}}} +{"ae":{"0131499084_155738486X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131499084","r":-1,"directed":false,"target":"155738486X"}}} +{"an":{"1581313047":{"g":0.11764706,"b":1,"r":0,"Label":"Vault Guide to ...","full-label":"Vault Guide to Advanced Finance & Quantitative Interviews","z":0,"y":-623.7731,"x":-416.53787,"size":3}}} +{"ae":{"1581311729_1581313047":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1581311729","r":-1,"directed":false,"target":"1581313047"}}} +{"an":{"1581311702":{"g":0.11764706,"b":1,"r":0,"Label":"Vault Guide to ...","full-label":"Vault Guide to Advanced Finance & Quantitative Interviews","z":0,"y":-662.7854,"x":-366.03986,"size":3}}} +{"ae":{"1581311729_1581311702":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1581311729","r":-1,"directed":false,"target":"1581311702"}}} +{"an":{"1581315295":{"g":0.11764706,"b":1,"r":0,"Label":"Vault Guide to ...","full-label":"Vault Guide to Advanced Finance & Quantitative Interviews","z":0,"y":-585.1343,"x":-354.39368,"size":3}}} +{"ae":{"1581311729_1581315295":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1581311729","r":-1,"directed":false,"target":"1581315295"}}} +{"an":{"0387004513":{"g":0.11764706,"b":1,"r":0,"Label":"Stochastic Calc...","full-label":"Stochastic Calculus for Finance II: Continuous-Time Models (Springer Finance)","z":0,"y":-501.4407,"x":-577.5136,"size":3}}} +{"ae":{"0387401016_0387004513":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387401016","r":-1,"directed":false,"target":"0387004513"}}} +{"an":{"3540221492":{"g":0.11764706,"b":1,"r":0,"Label":"Stochastic Calc...","full-label":"Stochastic Calculus for Finance II: Continuous-Time Models (Springer Finance)","z":0,"y":-444.20938,"x":-623.39264,"size":3}}} +{"ae":{"0387401016_3540221492":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387401016","r":-1,"directed":false,"target":"3540221492"}}} +{"an":{"0470050624":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"My Life as a Qu...","full-label":"My Life as a Quant: Reflections on Physics and Finance","z":0,"y":-374.59216,"x":-603.86804,"size":4.0526314}}} +{"ae":{"0470050624_1432706810":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0470050624","r":-1,"directed":false,"target":"1432706810"}}} +{"ae":{"0470192739_0470050624":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0470192739","r":-1,"directed":false,"target":"0470050624"}}} +{"an":{"0375758259":{"g":0.11764706,"b":1,"r":0,"Label":"My Life as a Qu...","full-label":"My Life as a Quant: Reflections on Physics and Finance","z":0,"y":-302.12616,"x":-693.6197,"size":3}}} +{"ae":{"0470192739_0375758259":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0470192739","r":-1,"directed":false,"target":"0375758259"}}} +{"an":{"0471457329":{"g":0.11764706,"b":1,"r":0,"Label":"My Life as a Qu...","full-label":"My Life as a Quant: Reflections on Physics and Finance","z":0,"y":-366.65808,"x":-669.4673,"size":3}}} +{"ae":{"0470192739_0471457329":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0470192739","r":-1,"directed":false,"target":"0471457329"}}} +{"an":{"0470058269":{"g":0.11764706,"b":1,"r":0,"Label":"Starting Your C...","full-label":"Starting Your Career as a Wall Street Quant: A Practical, No-BS Guide to Getting a Job in Quantitative Finance and Launching a Lucrative Career","z":0,"y":-421.82468,"x":-551.6105,"size":3}}} +{"ae":{"1432706810_0470058269":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1432706810","r":-1,"directed":false,"target":"0470058269"}}} +{"an":{"1593573642":{"g":0.11764706,"b":1,"r":0,"Label":"Resume Magic: T...","full-label":"Resume Magic: Trade Secrets of a Professional Resume Writer (Resume Magic Trade Secrets of a Professional Resume Writer)","z":0,"y":67.74474,"x":-701.2481,"size":3}}} +{"ae":{"1593573111_1593573642":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1593573111","r":-1,"directed":false,"target":"1593573642"}}} +{"an":{"081447280X":{"g":0.11764706,"b":1,"r":0,"Label":"Resume Magic: T...","full-label":"Resume Magic: Trade Secrets of a Professional Resume Writer (Resume Magic Trade Secrets of a Professional Resume Writer)","z":0,"y":-17.721815,"x":-699.0147,"size":3}}} +{"ae":{"1593573111_081447280X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1593573111","r":-1,"directed":false,"target":"081447280X"}}} +{"an":{"1593570007":{"g":0.11764706,"b":1,"r":0,"Label":"Expert Resumes ...","full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)","z":0,"y":-120.30424,"x":-758.42316,"size":3}}} +{"ae":{"1593571275_1593570007":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1593571275","r":-1,"directed":false,"target":"1593570007"}}} +{"an":{"1593570929":{"g":0.11764706,"b":1,"r":0,"Label":"Expert Resumes ...","full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)","z":0,"y":-166.58008,"x":-714.09894,"size":3}}} +{"ae":{"1593571275_1593570929":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1593571275","r":-1,"directed":false,"target":"1593570929"}}} +{"an":{"1593573669":{"g":0.11764706,"b":1,"r":0,"Label":"Expert Resumes ...","full-label":"Expert Resumes For Computer And Web Jobs (Expert Resumes)","z":0,"y":-86.974365,"x":-702.00507,"size":3}}} +{"ae":{"1593571275_1593573669":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1593571275","r":-1,"directed":false,"target":"1593573669"}}} +{"an":{"0072126833":{"g":0.11764706,"b":1,"r":0,"Label":"The IT Career B...","full-label":"The IT Career Builder's Toolkit","z":0,"y":26.473019,"x":-453.67737,"size":3}}} +{"ae":{"1587131560_0072126833":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1587131560","r":-1,"directed":false,"target":"0072126833"}}} +{"an":{"0132253569":{"g":0.11764706,"b":1,"r":0,"Label":"The IT Career B...","full-label":"The IT Career Builder's Toolkit","z":0,"y":-47.895813,"x":-518.6119,"size":3}}} +{"ae":{"1587131560_0132253569":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1587131560","r":-1,"directed":false,"target":"0132253569"}}} +{"an":{"1933639261":{"g":0.11764706,"b":1,"r":0,"Label":"The IT Career B...","full-label":"The IT Career Builder's Toolkit","z":0,"y":-114.83552,"x":-467.0333,"size":3}}} +{"ae":{"1587131560_1933639261":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1587131560","r":-1,"directed":false,"target":"1933639261"}}} +{"an":{"0071454055":{"g":0.11764706,"b":1,"r":0,"Label":"Perfect Phrases...","full-label":"Perfect Phrases for the Perfect Interview: Hundreds of Ready-to-Use Phrases That Succinctly Demonstrate Your Skills, Your Experience and Your Value in Any Interview Situation (Perfect Phrases)","z":0,"y":304.5515,"x":-704.0522,"size":3}}} +{"ae":{"0071449825_0071454055":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071449825","r":-1,"directed":false,"target":"0071454055"}}} +{"an":{"0071454063":{"g":0.11764706,"b":1,"r":0,"Label":"Perfect Phrases...","full-label":"Perfect Phrases for the Perfect Interview: Hundreds of Ready-to-Use Phrases That Succinctly Demonstrate Your Skills, Your Experience and Your Value in Any Interview Situation (Perfect Phrases)","z":0,"y":240.69109,"x":-716.87103,"size":3}}} +{"ae":{"0071449825_0071454063":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0071449825","r":-1,"directed":false,"target":"0071454063"}}} +{"an":{"156414772X":{"g":0.11764706,"b":1,"r":0,"Label":"Competency-Base...","full-label":"Competency-Based Interviews: Master the Tough New Interview Style And Give Them the Answers That Will Win You the Job","z":0,"y":122.23238,"x":-750.4106,"size":3}}} +{"ae":{"1564148696_156414772X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1564148696","r":-1,"directed":false,"target":"156414772X"}}} +{"an":{"0814473016":{"g":0.11764706,"b":1,"r":0,"Label":"Competency-Base...","full-label":"Competency-Based Interviews: Master the Tough New Interview Style And Give Them the Answers That Will Win You the Job","z":0,"y":178.92029,"x":-727.77875,"size":3}}} +{"ae":{"1564148696_0814473016":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1564148696","r":-1,"directed":false,"target":"0814473016"}}} +{"an":{"0425166864":{"g":0.11764706,"b":1,"r":0,"Label":"Best Answers to...","full-label":"Best Answers to the 201 Most Frequently Asked Interview Questions","z":0,"y":45.02811,"x":-761.02905,"size":3}}} +{"ae":{"007016357X_0425166864":{"g":0,"weight":1,"Weight":1,"b":0,"source":"007016357X","r":-1,"directed":false,"target":"0425166864"}}} +{"an":{"1418040010":{"g":0.11764706,"b":1,"r":0,"Label":"101 Great Answe...","full-label":"101 Great Answers to the Toughest Interview Questions","z":0,"y":-33.007484,"x":-763.2473,"size":3}}} +{"ae":{"1418040002_1418040010":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1418040002","r":-1,"directed":false,"target":"1418040010"}}} +{"an":{"0735623988":{"g":0.11764706,"b":1,"r":0,"Label":"More About Soft...","full-label":"More About Software Requirements: Thorny Issues and Practical Advice","z":0,"y":623.9994,"x":-0.4292228,"size":3}}} +{"ae":{"0735622671_0735623988":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735622671","r":-1,"directed":false,"target":"0735623988"}}} +{"an":{"0201786060":{"g":0.11764706,"b":1,"r":0,"Label":"The Software Re...","full-label":"The Software Requirements Memory Jogger: A Pocket Guide to Help Software And Business Teams Develop And Manage Requirements (Memory Jogger) (Memory Jogger)","z":0,"y":683.9743,"x":21.571146,"size":3}}} +{"ae":{"1576810607_0201786060":{"g":0,"weight":1,"Weight":1,"b":0,"source":"1576810607","r":-1,"directed":false,"target":"0201786060"}}} +{"an":{"0735623376":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Agile Project M...","full-label":"Agile Project Management with Scrum (Microsoft Professional)","z":0,"y":750.79596,"x":-172.22278,"size":4.0526314}}} +{"ae":{"0735623376_0130676349":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0735623376","r":-1,"directed":false,"target":"0130676349"}}} +{"ae":{"073561993X_0735623376":{"g":0,"weight":1,"Weight":1,"b":0,"source":"073561993X","r":-1,"directed":false,"target":"0735623376"}}} +{"an":{"0321219775":{"g":0.11764706,"b":1,"r":0,"Label":"Agile and Itera...","full-label":"Agile and Iterative Development: A Manager's Guide (The Agile Software Development Series)","z":0,"y":720.0596,"x":-239.57832,"size":3}}} +{"ae":{"0131111558_0321219775":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131111558","r":-1,"directed":false,"target":"0321219775"}}} +{"an":{"0976694026":{"g":0.11764706,"b":1,"r":0,"Label":"Agile Retrospec...","full-label":"Agile Retrospectives: Making Good Teams Great","z":0,"y":766.6067,"x":-70.484276,"size":3}}} +{"ae":{"0977616649_0976694026":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0977616649","r":-1,"directed":false,"target":"0976694026"}}} +{"an":{"097451408X":{"g":0.11764706,"b":1,"r":0,"Label":"Agile Retrospec...","full-label":"Agile Retrospectives: Making Good Teams Great","z":0,"y":764.30554,"x":17.343336,"size":3}}} +{"ae":{"0977616649_097451408X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0977616649","r":-1,"directed":false,"target":"097451408X"}}} +{"an":{"0201633388":{"g":0.11764706,"b":1,"r":0,"Label":"UNIX Internals:...","full-label":"UNIX Internals: The New Frontiers","z":0,"y":-642.97,"x":-233.3823,"size":3}}} +{"ae":{"0131019082_0201633388":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131019082","r":-1,"directed":false,"target":"0201633388"}}} +{"an":{"0471164836":{"g":0.11764706,"b":1,"r":0,"Label":"UNIX Internals:...","full-label":"UNIX Internals: The New Frontiers","z":0,"y":-616.243,"x":-292.68976,"size":3}}} +{"ae":{"0131019082_0471164836":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131019082","r":-1,"directed":false,"target":"0471164836"}}} +{"an":{"1573980137":{"g":0.11764706,"b":1,"r":0,"Label":"UNIX Internals:...","full-label":"UNIX Internals: The New Frontiers","z":0,"y":-688.2993,"x":-299.78052,"size":3}}} +{"ae":{"0131019082_1573980137":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131019082","r":-1,"directed":false,"target":"1573980137"}}} +{"an":{"0136006639":{"g":0.11764706,"b":1,"r":0,"Label":"Operating Syste...","full-label":"Operating Systems Design and Implementation (3rd Edition) (Prentice Hall Software Series)","z":0,"y":-476.00925,"x":68.968346,"size":3}}} +{"ae":{"0131429388_0136006639":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131429388","r":-1,"directed":false,"target":"0136006639"}}} +{"an":{"0130661023":{"g":0.11764706,"b":1,"r":0,"Label":"Operating Syste...","full-label":"Operating Systems Design and Implementation (3rd Edition) (Prentice Hall Software Series)","z":0,"y":-538.025,"x":86.01699,"size":3}}} +{"ae":{"0131429388_0130661023":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0131429388","r":-1,"directed":false,"target":"0130661023"}}} +{"an":{"0596000707":{"g":0.11764706,"b":1,"r":0,"Label":"The AWK Program...","full-label":"The AWK Programming Language","z":0,"y":-522.43524,"x":-85.97639,"size":3}}} +{"ae":{"020107981X_0596000707":{"g":0,"weight":1,"Weight":1,"b":0,"source":"020107981X","r":-1,"directed":false,"target":"0596000707"}}} +{"an":{"0387988645":{"g":0.11764706,"b":1,"r":0,"Label":"All of Nonparam...","full-label":"All of Nonparametric Statistics (Springer Texts in Statistics)","z":0,"y":-275.41473,"x":563.97003,"size":3}}} +{"ae":{"0387251456_0387988645":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387251456","r":-1,"directed":false,"target":"0387988645"}}} +{"an":{"0195073401":{"g":0.11764706,"b":1,"r":0,"Label":"Statistical Inf...","full-label":"Statistical Inference","z":0,"y":-364.8723,"x":665.21234,"size":3}}} +{"ae":{"0534243126_0195073401":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0534243126","r":-1,"directed":false,"target":"0195073401"}}} +{"an":{"0393957330":{"g":0.11764706,"b":1,"r":0,"Label":"Statistical Inf...","full-label":"Statistical Inference","z":0,"y":-533.9997,"x":548.1735,"size":3}}} +{"ae":{"0534243126_0393957330":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0534243126","r":-1,"directed":false,"target":"0393957330"}}} +{"an":{"0691010188":{"g":0.11764706,"b":1,"r":0,"Label":"Statistical Inf...","full-label":"Statistical Inference","z":0,"y":-423.11694,"x":632.5881,"size":3}}} +{"ae":{"0534243126_0691010188":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0534243126","r":-1,"directed":false,"target":"0691010188"}}} +{"an":{"026212274X":{"g":0.11764706,"b":1,"r":0,"Label":"Statistical Inf...","full-label":"Statistical Inference","z":0,"y":-353.2611,"x":584.2197,"size":3}}} +{"ae":{"0534243126_026212274X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0534243126","r":-1,"directed":false,"target":"026212274X"}}} +{"an":{"0393957357":{"g":0.11764706,"b":1,"r":0,"Label":"Statistical Inf...","full-label":"Statistical Inference","z":0,"y":-478.2198,"x":590.71014,"size":3}}} +{"ae":{"0534243126_0393957357":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0534243126","r":-1,"directed":false,"target":"0393957357"}}} +{"an":{"159385191X":{"g":0.11764706,"b":1,"r":0,"Label":"Data Analysis U...","full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models","z":0,"y":-241.29527,"x":668.16547,"size":3}}} +{"ae":{"052168689X_159385191X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"052168689X","r":-1,"directed":false,"target":"159385191X"}}} +{"an":{"0521671930":{"g":0.11764706,"b":1,"r":0,"Label":"Data Analysis U...","full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models","z":0,"y":-300.30582,"x":630.7561,"size":3}}} +{"ae":{"052168689X_0521671930":{"g":0,"weight":1,"Weight":1,"b":0,"source":"052168689X","r":-1,"directed":false,"target":"0521671930"}}} +{"an":{"0470510242":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Data Analysis U...","full-label":"Data Analysis Using Regression and Multilevel/Hierarchical Models","z":0,"y":-213.26959,"x":735.52325,"size":4.0526314}}} +{"ae":{"0470510242_0387713840":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0470510242","r":-1,"directed":false,"target":"0387713840"}}} +{"ae":{"052168689X_0470510242":{"g":0,"weight":1,"Weight":1,"b":0,"source":"052168689X","r":-1,"directed":false,"target":"0470510242"}}} +{"an":{"0387763694":{"g":0.11764706,"b":1,"r":0,"Label":"Monte Carlo Sta...","full-label":"Monte Carlo Statistical Methods (Springer Texts in Statistics)","z":0,"y":-143.90007,"x":642.4245,"size":3}}} +{"ae":{"0387212396_0387763694":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387212396","r":-1,"directed":false,"target":"0387763694"}}} +{"an":{"0387715983":{"g":0.11764706,"b":1,"r":0,"Label":"Monte Carlo Sta...","full-label":"Monte Carlo Statistical Methods (Springer Texts in Statistics)","z":0,"y":-17.888166,"x":699.2885,"size":3}}} +{"ae":{"0387212396_0387715983":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387212396","r":-1,"directed":false,"target":"0387715983"}}} +{"an":{"0387389792":{"g":0.11764706,"b":1,"r":0,"Label":"Bayesian Comput...","full-label":"Bayesian Computation with R (Use R)","z":0,"y":-290.79358,"x":710.96564,"size":3}}} +{"ae":{"0387713840_0387389792":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387713840","r":-1,"directed":false,"target":"0387389792"}}} +{"an":{"0387759689":{"g":0.11764706,"b":1,"r":0,"Label":"Bayesian Comput...","full-label":"Bayesian Computation with R (Use R)","z":0,"y":-50.57084,"x":760.0144,"size":3}}} +{"ae":{"0387713840_0387759689":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387713840","r":-1,"directed":false,"target":"0387759689"}}} +{"an":{"0387747303":{"g":0.11764706,"b":1,"r":0,"Label":"Bayesian Comput...","full-label":"Bayesian Computation with R (Use R)","z":0,"y":-118.23844,"x":756.2983,"size":3}}} +{"ae":{"0387713840_0387747303":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0387713840","r":-1,"directed":false,"target":"0387747303"}}} +{"an":{"0198568320":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Bayesian Statis...","full-label":"Bayesian Statistics: An Introduction (Arnold Publication)","z":0,"y":-43.39428,"x":619.1004,"size":4.0526314}}} +{"ae":{"0198568320_0521592712":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0198568320","r":-1,"directed":false,"target":"0521592712"}}} +{"ae":{"0340814055_0198568320":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0340814055","r":-1,"directed":false,"target":"0198568320"}}} +{"an":{"0470141158":{"g":0.11764706,"b":1,"r":0,"Label":"Bayesian Statis...","full-label":"Bayesian Statistics: An Introduction (Arnold Publication)","z":0,"y":27.200222,"x":759.5262,"size":3}}} +{"ae":{"0340814055_0470141158":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0340814055","r":-1,"directed":false,"target":"0470141158"}}} +{"an":{"013168728X":{"g":0.11764706,"b":1,"r":0,"Label":"Digital Image P...","full-label":"Digital Image Processing Using MATLAB(R)","z":0,"y":-607.3915,"x":463.51703,"size":3}}} +{"ae":{"0130085197_013168728X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0130085197","r":-1,"directed":false,"target":"013168728X"}}} +{"an":{"0534400116":{"g":0.11764706,"b":1,"r":0,"Label":"Digital Image P...","full-label":"Digital Image Processing Using MATLAB(R)","z":0,"y":-472.3175,"x":512.36127,"size":3}}} +{"ae":{"0130085197_0534400116":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0130085197","r":-1,"directed":false,"target":"0534400116"}}} +{"an":{"0470108770":{"g":0.11764706,"b":1,"r":0,"Label":"Digital Image P...","full-label":"Digital Image Processing Using MATLAB(R)","z":0,"y":-545.5174,"x":482.2078,"size":3}}} +{"ae":{"0130085197_0470108770":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0130085197","r":-1,"directed":false,"target":"0470108770"}}} +{"an":{"0132733501":{"g":0.11764706,"b":1,"r":0,"Label":"Neural Networks...","full-label":"Neural Networks for Pattern Recognition","z":0,"y":-394.58878,"x":463.72,"size":3}}} +{"ae":{"0198538642_0132733501":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0198538642","r":-1,"directed":false,"target":"0132733501"}}} +{"an":{"0521717701":{"g":0.11764706,"b":1,"r":0,"Label":"Neural Networks...","full-label":"Neural Networks for Pattern Recognition","z":0,"y":-350.153,"x":507.62265,"size":3}}} +{"ae":{"0198538642_0521717701":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0198538642","r":-1,"directed":false,"target":"0521717701"}}} +{"an":{"1590598342":{"g":0.11764706,"b":1,"r":0,"Label":"Practical Busin...","full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)","z":0,"y":-507.765,"x":406.9494,"size":3}}} +{"ae":{"0321356985_1590598342":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321356985","r":-1,"directed":false,"target":"1590598342"}}} +{"an":{"0471267155":{"g":0.11764706,"b":1,"r":0,"Label":"Practical Busin...","full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)","z":0,"y":-424.99463,"x":386.78235,"size":3}}} +{"ae":{"0321356985_0471267155":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321356985","r":-1,"directed":false,"target":"0471267155"}}} +{"an":{"0072260904":{"g":0.11764706,"b":1,"r":0,"Label":"Practical Busin...","full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)","z":0,"y":-571.95825,"x":403.8247,"size":3}}} +{"ae":{"0321356985_0072260904":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321356985","r":-1,"directed":false,"target":"0072260904"}}} +{"an":{"0672327821":{"g":0.11764706,"b":1,"r":0,"Label":"Practical Busin...","full-label":"Practical Business Intelligence with SQL Server 2005 (Microsoft Windows Server System Series)","z":0,"y":-480.1167,"x":340.04443,"size":3}}} +{"ae":{"0321356985_0672327821":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0321356985","r":-1,"directed":false,"target":"0672327821"}}} +{"an":{"0262072882":{"g":0.21568628,"b":0.8862745,"r":0,"Label":"Predicting Stru...","full-label":"Predicting Structured Data (Neural Information Processing)","z":0,"y":-39.60066,"x":157.18886,"size":4.0526314}}} +{"ae":{"0262072882_0262026252":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262072882","r":-1,"directed":false,"target":"0262026252"}}} +{"ae":{"0262026171_0262072882":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262026171","r":-1,"directed":false,"target":"0262072882"}}} +{"an":{"0262072815":{"g":0.11764706,"b":1,"r":0,"Label":"Large-Scale Ker...","full-label":"Large-Scale Kernel Machines (Neural Information Processing)","z":0,"y":17.648542,"x":304.14923,"size":3}}} +{"ae":{"0262026252_0262072815":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0262026252","r":-1,"directed":false,"target":"0262072815"}}} +{"an":{"080186982X":{"g":0.11764706,"b":1,"r":0,"Label":"Probability The...","full-label":"Probability Theory: The Logic of Science","z":0,"y":42.67559,"x":581.4557,"size":3}}} +{"ae":{"0521592712_080186982X":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521592712","r":-1,"directed":false,"target":"080186982X"}}} +{"an":{"1602063281":{"g":0.11764706,"b":1,"r":0,"Label":"Probability The...","full-label":"Probability Theory: The Logic of Science","z":0,"y":21.275562,"x":644.2773,"size":3}}} +{"ae":{"0521592712_1602063281":{"g":0,"weight":1,"Weight":1,"b":0,"source":"0521592712","r":-1,"directed":false,"target":"1602063281"}}} From 09b177698b0f5000669fea398526a59ab729d2b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Wed, 6 Jan 2016 23:12:01 +0100 Subject: [PATCH 05/30] StreamingServer tests passing --- modules/StreamingServer/pom.xml | 30 +- .../server/impl/DynamicGraphWriter.java | 321 +++++++++--------- .../impl/GraphBufferedEventHandler.java | 9 +- .../streaming/server/impl/GraphWriter.java | 36 +- .../server/impl/ServerOperationExecutor.java | 69 ++-- .../GraphStreamingEventProcessorTest.java | 19 +- .../streaming/server/test/MainServer.java | 8 +- .../streaming/server/test/MainServer2.java | 12 +- 8 files changed, 253 insertions(+), 251 deletions(-) diff --git a/modules/StreamingServer/pom.xml b/modules/StreamingServer/pom.xml index b67f83e428..0bbf9e0bf9 100644 --- a/modules/StreamingServer/pom.xml +++ b/modules/StreamingServer/pom.xml @@ -21,15 +21,29 @@ 3.1.0 - org.eclipse.jetty.websocket - websocket-server - 9.3.6.v20151106 + org.eclipse.jetty + jetty-websocket + 8.1.17.v20150415 + + + org.eclipse.jetty + jetty-server + 8.1.17.v20150415 + + + org.eclipse.jetty + jetty-servlet + 8.1.17.v20150415 + + + org.gephi.streaming + api + 1.0.0 org.gephi.streaming impl 1.0.0 - @@ -48,6 +62,12 @@ org.gephi project-api + + junit + junit + 4.10 + test + @@ -59,7 +79,7 @@ Unknown $sourcecode_url - + org.gephi.streaming.server diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/DynamicGraphWriter.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/DynamicGraphWriter.java index 34c2473029..af226b57b4 100644 --- a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/DynamicGraphWriter.java +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/DynamicGraphWriter.java @@ -46,16 +46,12 @@ Development and Distribution License("CDDL") (collectively, the import java.util.List; import java.util.Map; import java.util.TreeSet; +import org.gephi.graph.api.Column; -import org.gephi.data.attributes.api.AttributeRow; -import org.gephi.data.attributes.api.AttributeValue; -import org.gephi.data.attributes.type.DynamicType; -import org.gephi.data.attributes.type.Interval; -import org.gephi.data.properties.PropertiesColumn; -import org.gephi.dynamic.api.DynamicModel; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; import org.gephi.graph.api.Node; +import org.gephi.graph.impl.GraphStoreConfiguration; import org.gephi.streaming.api.GraphEventHandler; import org.gephi.streaming.api.event.ElementType; import org.gephi.streaming.api.event.EventType; @@ -83,144 +79,148 @@ public void writeGraph(GraphEventHandler operationSupport) { Map> remotion = new HashMap>(); Map> changing = new HashMap>(); - for (Node node: graph.getNodes()) { - - String nodeId = (String) node.getId(); - DynamicType timeInterval = (DynamicType)node.getAttribute(DynamicModel.TIMEINTERVAL_COLUMN); - - List ranges = timeInterval.getIntervals(); - for (Interval range: ranges) { - double created = range.getLow(); - double removed = range.getHigh(); - - if (created < minRange) { - minRange = created; - } - if (removed > maxRange) { - maxRange = removed; - } - - List createdAt = creation.get(created); - if (createdAt == null) { - createdAt = new ArrayList(); - creation.put(created, createdAt); - } - - { - Map attributes = getNodeAttributes(node); - createdAt.add(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, nodeId, attributes)); - } - - List removedAt = remotion.get(removed); - if (removedAt == null) { - removedAt = new ArrayList(); - remotion.put(removed, removedAt); - } - { - Map attributes = new HashMap(); - removedAt.add(eventBuilder.graphEvent(ElementType.NODE, EventType.REMOVE, nodeId, attributes)); - } - - AttributeRow row = (AttributeRow) node.getAttributes(); - if (row != null) { - for (AttributeValue attributeValue : row.getValues()) { - if (attributeValue.getValue() != null - && attributeValue.getColumn().getType().isDynamicType() - && !attributeValue.getColumn().getId().equals(DynamicModel.TIMEINTERVAL_COLUMN)) { - - DynamicType dynamicValue = (DynamicType)attributeValue.getValue(); - List intervals = dynamicValue.getIntervals(); - - for (Interval interval: intervals) { - - Object value = interval.getValue(); - - List changedAt = changing.get(interval.getLow()); - if (changedAt == null) { - changedAt = new ArrayList(); - changing.put(interval.getLow(), changedAt); - } - Map attributes = new HashMap(); - attributes.put(attributeValue.getColumn().getTitle(), value); - - changedAt.add(eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, nodeId, attributes)); - - } - } - } - } - } - } - - for (Edge edge: graph.getEdges()) { - - String edgeId = (String) edge.getId(); - DynamicType timeInterval = (DynamicType)edge.getAttributes(DynamicModel.TIMEINTERVAL_COLUMN); - - List ranges = timeInterval.getIntervals(); - for (Interval range: ranges) { - double created = range.getLow(); - double removed = range.getHigh(); - - if (created < minRange) { - minRange = created; - } - if (removed > maxRange) { - maxRange = removed; - } - - List createdAt = creation.get(created); - if (createdAt == null) { - createdAt = new ArrayList(); - creation.put(created, createdAt); - } - { - Map attributes = getEdgeAttributes(edge); - String sourceId = (String) edge.getSource().getId(); - String targetId = (String) edge.getTarget().getId(); - createdAt.add(eventBuilder.edgeAddedEvent(edgeId, sourceId, targetId, edge.isDirected(), attributes)); - } - - List removedAt = remotion.get(removed); - if (removedAt == null) { - removedAt = new ArrayList(); - remotion.put(removed, removedAt); - } - { - Map attributes = new HashMap(); - removedAt.add(0,eventBuilder.graphEvent(ElementType.EDGE, EventType.REMOVE, edgeId, attributes)); - } - - AttributeRow row = (AttributeRow) edge.getAttributes(); - if (row != null) { - for (AttributeValue attributeValue : row.getValues()) { - if (attributeValue.getValue() != null - && attributeValue.getColumn().getType().isDynamicType() - && !attributeValue.getColumn().getId().equals(DynamicModel.TIMEINTERVAL_COLUMN)) { - - DynamicType dynamicValue = (DynamicType)attributeValue.getValue(); - List intervals = dynamicValue.getIntervals(); - - for (Interval interval: intervals) { - - Object value = interval.getValue(); - - List changedAt = changing.get(interval.getLow()); - if (changedAt == null) { - changedAt = new ArrayList(); - changing.put(interval.getLow(), changedAt); - } - Map attributes = new HashMap(); - attributes.put(attributeValue.getColumn().getTitle(), value); - - changedAt.add(eventBuilder.graphEvent(ElementType.EDGE, EventType.CHANGE, edgeId, attributes)); - - } - } - } - } - } - } + // TODO: Implement handling of dynamic graphs + if (true) + throw new Exception("Handling of dynamic graphs is not supported"); + +// for (Node node: graph.getNodes()) { +// +// String nodeId = (String) node.getId(); +// DynamicType timeInterval = (DynamicType)node.getAttribute(DynamicModel.TIMEINTERVAL_COLUMN); +// +// List ranges = timeInterval.getIntervals(); +// for (Interval range: ranges) { +// double created = range.getLow(); +// double removed = range.getHigh(); +// +// if (created < minRange) { +// minRange = created; +// } +// if (removed > maxRange) { +// maxRange = removed; +// } +// +// List createdAt = creation.get(created); +// if (createdAt == null) { +// createdAt = new ArrayList(); +// creation.put(created, createdAt); +// } +// +// { +// Map attributes = getNodeAttributes(node); +// createdAt.add(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, nodeId, attributes)); +// } +// +// List removedAt = remotion.get(removed); +// if (removedAt == null) { +// removedAt = new ArrayList(); +// remotion.put(removed, removedAt); +// } +// { +// Map attributes = new HashMap(); +// removedAt.add(eventBuilder.graphEvent(ElementType.NODE, EventType.REMOVE, nodeId, attributes)); +// } +// +// AttributeRow row = (AttributeRow) node.getAttributes(); +// if (row != null) { +// for (AttributeValue attributeValue : row.getValues()) { +// if (attributeValue.getValue() != null +// && attributeValue.getColumn().getType().isDynamicType() +// && !attributeValue.getColumn().getId().equals(DynamicModel.TIMEINTERVAL_COLUMN)) { +// +// DynamicType dynamicValue = (DynamicType)attributeValue.getValue(); +// List intervals = dynamicValue.getIntervals(); +// +// for (Interval interval: intervals) { +// +// Object value = interval.getValue(); +// +// List changedAt = changing.get(interval.getLow()); +// if (changedAt == null) { +// changedAt = new ArrayList(); +// changing.put(interval.getLow(), changedAt); +// } +// Map attributes = new HashMap(); +// attributes.put(attributeValue.getColumn().getTitle(), value); +// +// changedAt.add(eventBuilder.graphEvent(ElementType.NODE, EventType.CHANGE, nodeId, attributes)); +// +// } +// } +// } +// } +// } +// } +// +// for (Edge edge: graph.getEdges()) { +// +// String edgeId = (String) edge.getId(); +// DynamicType timeInterval = (DynamicType)edge.getAttributes(DynamicModel.TIMEINTERVAL_COLUMN); +// +// List ranges = timeInterval.getIntervals(); +// for (Interval range: ranges) { +// double created = range.getLow(); +// double removed = range.getHigh(); +// +// if (created < minRange) { +// minRange = created; +// } +// if (removed > maxRange) { +// maxRange = removed; +// } +// +// List createdAt = creation.get(created); +// if (createdAt == null) { +// createdAt = new ArrayList(); +// creation.put(created, createdAt); +// } +// { +// Map attributes = getEdgeAttributes(edge); +// String sourceId = (String) edge.getSource().getId(); +// String targetId = (String) edge.getTarget().getId(); +// createdAt.add(eventBuilder.edgeAddedEvent(edgeId, sourceId, targetId, edge.isDirected(), attributes)); +// } +// +// List removedAt = remotion.get(removed); +// if (removedAt == null) { +// removedAt = new ArrayList(); +// remotion.put(removed, removedAt); +// } +// { +// Map attributes = new HashMap(); +// removedAt.add(0,eventBuilder.graphEvent(ElementType.EDGE, EventType.REMOVE, edgeId, attributes)); +// } +// +// AttributeRow row = (AttributeRow) edge.getAttributes(); +// if (row != null) { +// for (AttributeValue attributeValue : row.getValues()) { +// if (attributeValue.getValue() != null +// && attributeValue.getColumn().getType().isDynamicType() +// && !attributeValue.getColumn().getId().equals(DynamicModel.TIMEINTERVAL_COLUMN)) { +// +// DynamicType dynamicValue = (DynamicType)attributeValue.getValue(); +// List intervals = dynamicValue.getIntervals(); +// +// for (Interval interval: intervals) { +// +// Object value = interval.getValue(); +// +// List changedAt = changing.get(interval.getLow()); +// if (changedAt == null) { +// changedAt = new ArrayList(); +// changing.put(interval.getLow(), changedAt); +// } +// Map attributes = new HashMap(); +// attributes.put(attributeValue.getColumn().getTitle(), value); +// +// changedAt.add(eventBuilder.graphEvent(ElementType.EDGE, EventType.CHANGE, edgeId, attributes)); +// +// } +// } +// } +// } +// } +// } List pre; pre = creation.get(Double.NEGATIVE_INFINITY); @@ -286,15 +286,13 @@ public void writeGraph(GraphEventHandler operationSupport) { private Map getNodeAttributes(Node node) { Map attributes = new HashMap(); - AttributeRow row = (AttributeRow) node.getAttributes(); - - if (row != null) - for (AttributeValue attributeValue: row.getValues()) { - if (attributeValue.getColumn().getIndex()!=PropertiesColumn.NODE_ID.getIndex() - && attributeValue.getValue()!=null - && !attributeValue.getColumn().getType().isDynamicType()) - attributes.put(attributeValue.getColumn().getTitle(), attributeValue.getValue()); + for (Column column: node.getAttributeColumns()) { + if (column.getIndex() != GraphStoreConfiguration.ELEMENT_ID_INDEX) { + Object value = node.getAttribute(column); + if (value != null) + attributes.put(column.getTitle(), value); } + } if (sendVizData) { attributes.put("x", node.x()); @@ -305,7 +303,7 @@ private Map getNodeAttributes(Node node) { attributes.put("g", node.g()); attributes.put("b", node.b()); - attributes.put("size", node.getAttribute("size")); + attributes.put("size", node.size()); } return attributes; @@ -313,21 +311,20 @@ private Map getNodeAttributes(Node node) { private Map getEdgeAttributes(Edge edge) { Map attributes = new HashMap(); - AttributeRow row = (AttributeRow) edge.getAttributes(); - if (row != null) - for (AttributeValue attributeValue: row.getValues()) { - if (attributeValue.getColumn().getIndex()!=PropertiesColumn.EDGE_ID.getIndex() - && attributeValue.getValue()!=null - && !attributeValue.getColumn().getType().isDynamicType()) - attributes.put(attributeValue.getColumn().getTitle(), attributeValue.getValue()); + for (Column column: edge.getAttributeColumns()) { + if (column.getIndex() != GraphStoreConfiguration.ELEMENT_ID_INDEX) { + Object value = edge.getAttribute(column); + if (value != null) + attributes.put(column.getTitle(), value); } + } if (sendVizData) { - + attributes.put("r", edge.r()); attributes.put("g", edge.g()); attributes.put("b", edge.b()); - + attributes.put("weight", edge.getWeight()); } diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphBufferedEventHandler.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphBufferedEventHandler.java index 55a9634261..4aa33bfc19 100644 --- a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphBufferedEventHandler.java +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphBufferedEventHandler.java @@ -41,13 +41,9 @@ Development and Distribution License("CDDL") (collectively, the */ package org.gephi.streaming.server.impl; -import org.gephi.data.attributes.api.AttributeController; -import org.gephi.data.attributes.api.AttributeModel; -import org.gephi.dynamic.api.DynamicModel; import org.gephi.graph.api.Graph; import org.gephi.streaming.api.CompositeGraphEventHandler; import org.gephi.streaming.api.GraphEventHandler; -import org.openide.util.Lookup; /** * @author panisson @@ -60,10 +56,7 @@ public class GraphBufferedEventHandler extends CompositeGraphEventHandler { public GraphBufferedEventHandler(Graph graph) { // check if graph is dynamic - AttributeModel attributeModel = Lookup.getDefault().lookup(AttributeController.class) - .getModel(graph.getModel().getWorkspace()); - if (attributeModel.getNodeTable().hasColumn(DynamicModel.TIMEINTERVAL_COLUMN) - && attributeModel.getEdgeTable().hasColumn(DynamicModel.TIMEINTERVAL_COLUMN)) { + if (graph.getModel().isDynamic()) { this.graphWriter = new DynamicGraphWriter(graph, false); } else { this.graphWriter = new GraphWriter(graph, true); diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphWriter.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphWriter.java index 96dcb15d4f..438db0c23e 100644 --- a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphWriter.java +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphWriter.java @@ -45,13 +45,12 @@ Development and Distribution License("CDDL") (collectively, the import java.util.HashSet; import java.util.Map; import java.util.Set; +import org.gephi.graph.api.Column; -import org.gephi.data.attributes.api.AttributeRow; -import org.gephi.data.attributes.api.AttributeValue; -import org.gephi.data.properties.PropertiesColumn; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; import org.gephi.graph.api.Node; +import org.gephi.graph.impl.GraphStoreConfiguration; import org.gephi.streaming.api.CompositeGraphEventHandler; import org.gephi.streaming.api.event.GraphEventBuilder; import org.gephi.streaming.api.GraphEventHandler; @@ -123,14 +122,13 @@ public void writeGraph(GraphEventHandler operationSupport) { private Map getNodeAttributes(Node node) { Map attributes = new HashMap(); - AttributeRow row = (AttributeRow) node.getAttributes(); - - if (row != null) - for (AttributeValue attributeValue: row.getValues()) { - if (attributeValue.getColumn().getIndex()!=PropertiesColumn.NODE_ID.getIndex() - && attributeValue.getValue()!=null) - attributes.put(attributeValue.getColumn().getTitle(), attributeValue.getValue()); + for (Column column: node.getAttributeColumns()) { + if (column.getIndex() != GraphStoreConfiguration.ELEMENT_ID_INDEX) { + Object value = node.getAttribute(column); + if (value != null) + attributes.put(column.getTitle(), value); } + } if (sendVizData) { attributes.put("x", node.x()); @@ -141,7 +139,7 @@ private Map getNodeAttributes(Node node) { attributes.put("g", node.g()); attributes.put("b", node.b()); - attributes.put("size", node.getAttribute("size")); + attributes.put("size", node.size()); } return attributes; @@ -149,20 +147,20 @@ private Map getNodeAttributes(Node node) { private Map getEdgeAttributes(Edge edge) { Map attributes = new HashMap(); - AttributeRow row = (AttributeRow) edge.getAttributes(); - if (row != null) - for (AttributeValue attributeValue: row.getValues()) { - if (attributeValue.getColumn().getIndex()!=PropertiesColumn.EDGE_ID.getIndex() - && attributeValue.getValue()!=null) - attributes.put(attributeValue.getColumn().getTitle(), attributeValue.getValue()); + for (Column column: edge.getAttributeColumns()) { + if (column.getIndex() != GraphStoreConfiguration.ELEMENT_ID_INDEX) { + Object value = edge.getAttribute(column); + if (value != null) + attributes.put(column.getTitle(), value); } + } if (sendVizData) { - + attributes.put("r", edge.r()); attributes.put("g", edge.g()); attributes.put("b", edge.b()); - + attributes.put("weight", edge.getWeight()); } diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerOperationExecutor.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerOperationExecutor.java index aef09f28a9..606e8491a4 100644 --- a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerOperationExecutor.java +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerOperationExecutor.java @@ -48,14 +48,12 @@ Development and Distribution License("CDDL") (collectively, the import java.net.SocketException; import java.util.HashMap; import java.util.Map; +import org.gephi.graph.api.Column; -import org.gephi.data.attributes.api.AttributeController; -import org.gephi.data.attributes.api.AttributeRow; -import org.gephi.data.attributes.api.AttributeValue; -import org.gephi.data.properties.PropertiesColumn; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; import org.gephi.graph.api.Node; +import org.gephi.graph.impl.GraphStoreConfiguration; import org.gephi.streaming.api.CompositeGraphEventHandler; import org.gephi.streaming.api.Graph2EventListener; import org.gephi.streaming.api.GraphEventHandler; @@ -95,9 +93,9 @@ public ServerOperationExecutor(Graph graph, ClientManagerImpl clientManager) { eventBuilder = new GraphEventBuilder(this); Graph2EventListener changeListener = new Graph2EventListener(graph, graphBufferedOperationSupport); - graph.getGraphModel().addGraphListener(changeListener); - AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); - ac.getModel().addAttributeListener(changeListener); +// graph.getGraphModel().addGraphListener(changeListener); +// AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); +// ac.getModel().addAttributeListener(changeListener); } /** @@ -150,7 +148,7 @@ public void executeGetNode(String id, String format, OutputStream outputStream) try { Node node = graph.getNode(id); if (node != null) { - String nodeId = node.getNodeData().getId(); + String nodeId = node.getId().toString(); writer.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, nodeId, getNodeAttributes(node))); } } finally { @@ -178,9 +176,9 @@ public void executeGetEdge(String id, String format, OutputStream outputStream) try { Edge edge = graph.getEdge(id); if (edge != null) { - String edgeId = edge.getEdgeData().getId(); - String sourceId = edge.getSource().getNodeData().getId(); - String targetId = edge.getTarget().getNodeData().getId(); + String edgeId = edge.getId().toString(); + String sourceId = edge.getSource().getId().toString(); + String targetId = edge.getTarget().getId().toString(); writer.handleGraphEvent(eventBuilder.edgeAddedEvent(edgeId, sourceId, targetId, edge.isDirected(), getEdgeAttributes(edge))); } } finally { @@ -226,25 +224,24 @@ public void executeUpdateGraph(String format, InputStream inputStream, OutputStr private Map getNodeAttributes(Node node) { Map attributes = new HashMap(); - AttributeRow row = (AttributeRow) node.getNodeData().getAttributes(); - - if (row != null) - for (AttributeValue attributeValue: row.getValues()) { - if (attributeValue.getColumn().getIndex()!=PropertiesColumn.NODE_ID.getIndex() - && attributeValue.getValue()!=null) - attributes.put(attributeValue.getColumn().getTitle(), attributeValue.getValue()); + for (Column column: node.getAttributeColumns()) { + if (column.getIndex() != GraphStoreConfiguration.ELEMENT_ID_INDEX) { + Object value = node.getAttribute(column); + if (value != null) + attributes.put(column.getTitle(), value); } + } if (sendVizData) { - attributes.put("x", node.getNodeData().x()); - attributes.put("y", node.getNodeData().y()); - attributes.put("z", node.getNodeData().z()); + attributes.put("x", node.x()); + attributes.put("y", node.y()); + attributes.put("z", node.z()); - attributes.put("r", node.getNodeData().r()); - attributes.put("g", node.getNodeData().g()); - attributes.put("b", node.getNodeData().b()); + attributes.put("r", node.r()); + attributes.put("g", node.g()); + attributes.put("b", node.b()); - attributes.put("size", node.getNodeData().getSize()); + attributes.put("size", node.size()); } return attributes; @@ -252,20 +249,20 @@ private Map getNodeAttributes(Node node) { private Map getEdgeAttributes(Edge edge) { Map attributes = new HashMap(); - AttributeRow row = (AttributeRow) edge.getEdgeData().getAttributes(); - if (row != null) - for (AttributeValue attributeValue: row.getValues()) { - if (attributeValue.getColumn().getIndex()!=PropertiesColumn.EDGE_ID.getIndex() - && attributeValue.getValue()!=null) - attributes.put(attributeValue.getColumn().getTitle(), attributeValue.getValue()); + for (Column column: edge.getAttributeColumns()) { + if (column.getIndex() != GraphStoreConfiguration.ELEMENT_ID_INDEX) { + Object value = edge.getAttribute(column); + if (value != null) + attributes.put(column.getTitle(), value); } + } if (sendVizData) { - - attributes.put("r", edge.getEdgeData().r()); - attributes.put("g", edge.getEdgeData().g()); - attributes.put("b", edge.getEdgeData().b()); - + + attributes.put("r", edge.r()); + attributes.put("g", edge.g()); + attributes.put("b", edge.b()); + attributes.put("weight", edge.getWeight()); } diff --git a/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/GraphStreamingEventProcessorTest.java b/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/GraphStreamingEventProcessorTest.java index 71ec3477b2..f5cd2c5d56 100644 --- a/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/GraphStreamingEventProcessorTest.java +++ b/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/GraphStreamingEventProcessorTest.java @@ -47,11 +47,11 @@ Development and Distribution License("CDDL") (collectively, the import java.util.Collections; import java.util.HashSet; import java.util.Set; -import org.gephi.data.attributes.api.AttributeController; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphController; import org.gephi.graph.api.GraphModel; +import org.gephi.graph.api.GraphObserver; import org.gephi.project.api.ProjectController; import org.gephi.streaming.api.CompositeGraphEventHandler; import org.gephi.streaming.api.Graph2EventListener; @@ -68,6 +68,7 @@ Development and Distribution License("CDDL") (collectively, the import org.gephi.streaming.api.event.GraphEventBuilder; import org.gephi.streaming.impl.StreamingConnectionImpl; import org.gephi.streaming.server.impl.FilteredGraphEventHandler; +import static org.junit.Assert.assertTrue; import org.junit.Test; import org.openide.util.Exceptions; import org.openide.util.Lookup; @@ -90,13 +91,13 @@ public void testAll() throws IOException { String streamType = "DGS"; URL url = this.getClass().getResource(DGS_RESOURCE); - AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); - ac.getModel(); +// AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); +// ac.getModel(); GraphController graphController = Lookup.getDefault().lookup(GraphController.class); - GraphModel graphModel = graphController.getModel(); + GraphModel graphModel = graphController.getGraphModel(); - Graph graph = graphModel.getHierarchicalMixedGraph(); + Graph graph = graphModel.getGraph(); ByteArrayOutputStream out = new ByteArrayOutputStream(); StreamWriterFactory factory = Lookup.getDefault().lookup(StreamWriterFactory.class); @@ -115,9 +116,10 @@ public void handleGraphEvent(GraphEvent event) { GraphEventHandler composite = new CompositeGraphEventHandler(graphUpdaterHandler, eventCollector); Graph2EventListener listener = new Graph2EventListener(graph, new FilteredGraphEventHandler(streamWriter, processedEvents)); + GraphObserver graphObserver = graphModel.createGraphObserver(graph, true); // Graph2EventListener listener = new Graph2EventListener(graph, streamWriter); - graphModel.addGraphListener(listener); - ac.getModel().addAttributeListener(listener); +// graphModel.addGraphListener(listener); +// ac.getModel().addAttributeListener(listener); StreamingConnection connection = connectToStream(url, streamType, composite); @@ -129,6 +131,9 @@ public void handleGraphEvent(GraphEvent event) { } catch (InterruptedException ex) { Exceptions.printStackTrace(ex); } + + assertTrue(graphObserver.hasGraphChanged()); + listener.graphChanged(graphObserver.getDiff()); System.out.println(out.toString()); diff --git a/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/MainServer.java b/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/MainServer.java index f52e6b21ce..e003075f96 100644 --- a/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/MainServer.java +++ b/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/MainServer.java @@ -43,7 +43,6 @@ Development and Distribution License("CDDL") (collectively, the import java.io.IOException; -import org.gephi.data.attributes.api.AttributeController; import org.gephi.graph.api.GraphController; import org.gephi.graph.api.GraphModel; import org.gephi.project.api.ProjectController; @@ -71,11 +70,8 @@ public void testMainServer() throws IOException { projectController.newProject(); Workspace workspace = projectController.newWorkspace(projectController.getCurrentProject()); - AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); - ac.getModel(); - GraphController graphController = Lookup.getDefault().lookup(GraphController.class); - GraphModel graphModel = graphController.getModel(); + GraphModel graphModel = graphController.getGraphModel(); StreamingServer server = Lookup.getDefault().lookup(StreamingServer.class); @@ -85,7 +81,7 @@ public void testMainServer() throws IOException { authenticationFilter.setPassword(server.getServerSettings().getPassword()); authenticationFilter.setAuthenticationEnabled(server.getServerSettings().isBasicAuthentication()); - ServerControllerImpl serverController = new ServerControllerImpl(graphModel.getHierarchicalMixedGraph()); + ServerControllerImpl serverController = new ServerControllerImpl(graphModel.getGraph()); server.register(serverController, "/graphstream"); server.start(); diff --git a/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/MainServer2.java b/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/MainServer2.java index b081dd27f4..0fe748ff48 100644 --- a/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/MainServer2.java +++ b/modules/StreamingServer/src/test/java/org/gephi/streaming/server/test/MainServer2.java @@ -51,7 +51,6 @@ Development and Distribution License("CDDL") (collectively, the import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; -import org.gephi.data.attributes.api.AttributeController; import org.gephi.graph.api.GraphController; import org.gephi.graph.api.GraphModel; import org.gephi.project.api.ProjectController; @@ -83,13 +82,10 @@ public MainServer2() { projectController.newProject(); Workspace workspace = projectController.newWorkspace(projectController.getCurrentProject()); - AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); - ac.getModel(); - GraphController graphController = Lookup.getDefault().lookup(GraphController.class); - GraphModel graphModel = graphController.getModel(); + GraphModel graphModel = graphController.getGraphModel(); - serverController = new ServerControllerImpl(graphModel.getHierarchicalMixedGraph()); + serverController = new ServerControllerImpl(graphModel.getGraph()); final InputStream fileInputStream = this.getClass().getResourceAsStream(DGS_RESOURCE); @@ -108,7 +104,7 @@ public int read() throws IOException { }; StreamReaderFactory factory = Lookup.getDefault().lookup(StreamReaderFactory.class); - final StreamReader streamReader = factory.createStreamReader("DGS", new GraphUpdaterEventHandler(graphModel.getHierarchicalMixedGraph()), new GraphEventBuilder(this)); + final StreamReader streamReader = factory.createStreamReader("DGS", new GraphUpdaterEventHandler(graphModel.getGraph()), new GraphEventBuilder(this)); new Thread() { @Override @@ -139,7 +135,7 @@ public void service(HttpServletRequest request, HttpServletResponse response) { } public static void main(String[] list) throws Exception { - Server server = new Server(8080); + Server server = new Server(8081); ServletContextHandler context = new ServletContextHandler(); context.setContextPath("/"); context.addServlet(new ServletHolder(new MainServer2()), "/*"); From 646fcdd0e90c08be99901966c7ce0220928618ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Wed, 6 Jan 2016 23:27:36 +0100 Subject: [PATCH 06/30] Add transitive module references --- modules/DesktopStreaming/pom.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/DesktopStreaming/pom.xml b/modules/DesktopStreaming/pom.xml index 1d6c5dce8c..e54ceaa751 100644 --- a/modules/DesktopStreaming/pom.xml +++ b/modules/DesktopStreaming/pom.xml @@ -60,10 +60,18 @@ org.netbeans.api org-openide-util + + org.netbeans.api + org-openide-util-ui + org.gephi project-api + + org.gephi + lib.validation + org.gephi.streaming From 7490a2fb33dc57e44cc3ed27a54dbef29ef37646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Wed, 6 Jan 2016 23:36:10 +0100 Subject: [PATCH 07/30] StreamingImpl skip tests during build --- modules/StreamingImpl/pom.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/StreamingImpl/pom.xml b/modules/StreamingImpl/pom.xml index dbd0b92e27..97c7317f43 100644 --- a/modules/StreamingImpl/pom.xml +++ b/modules/StreamingImpl/pom.xml @@ -48,7 +48,9 @@ test - + + true + From 50fed4d0f401800f0d4f7409e0a0750c3b557f05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Thu, 7 Jan 2016 00:20:06 +0100 Subject: [PATCH 08/30] Adjust Jetty dependencies to build JettyWrapper nbm --- modules/JettyWrapper/pom.xml | 57 ++++++++++++------------------------ 1 file changed, 18 insertions(+), 39 deletions(-) diff --git a/modules/JettyWrapper/pom.xml b/modules/JettyWrapper/pom.xml index c12e5ce053..e8dddf6f86 100644 --- a/modules/JettyWrapper/pom.xml +++ b/modules/JettyWrapper/pom.xml @@ -15,7 +15,21 @@ JettyWrapper - + + org.eclipse.jetty + jetty-websocket + 8.1.17.v20150415 + + + org.eclipse.jetty + jetty-server + 8.1.17.v20150415 + + + org.eclipse.jetty + jetty-servlet + 8.1.17.v20150415 + @@ -24,45 +38,10 @@ org.codehaus.mojo nbm-maven-plugin - Unknown - $sourcecode_url - - javax.servlet - javax.servlet.annotation - javax.servlet.descriptor - javax.servlet.http - org.eclipse.jetty.continuation - org.eclipse.jetty.http - org.eclipse.jetty.http.gzip - org.eclipse.jetty.http.ssl - org.eclipse.jetty.io - org.eclipse.jetty.io.bio - org.eclipse.jetty.io.nio - org.eclipse.jetty.security - org.eclipse.jetty.security.authentication - org.eclipse.jetty.server - org.eclipse.jetty.server.bio - org.eclipse.jetty.server.handler - org.eclipse.jetty.server.handler.jmx - org.eclipse.jetty.server.jmx - org.eclipse.jetty.server.nio - org.eclipse.jetty.server.session - org.eclipse.jetty.server.session.jmx - org.eclipse.jetty.server.ssl - org.eclipse.jetty.servlet - org.eclipse.jetty.servlet.jmx - org.eclipse.jetty.servlet.listener - org.eclipse.jetty.util - org.eclipse.jetty.util.ajax - org.eclipse.jetty.util.component - org.eclipse.jetty.util.log - org.eclipse.jetty.util.resource - org.eclipse.jetty.util.security - org.eclipse.jetty.util.ssl - org.eclipse.jetty.util.statistic - org.eclipse.jetty.util.thread - org.eclipse.jetty.websocket + javax.servlet + javax.servlet.* + org.eclipse.jetty.* From 7fb6441729af0375233ea079ad7e6232dde13fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Thu, 7 Jan 2016 12:00:39 +0100 Subject: [PATCH 09/30] Add dependencies to all other modules on GraphStreaming --- modules/GraphStreaming/pom.xml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/modules/GraphStreaming/pom.xml b/modules/GraphStreaming/pom.xml index c7be7a344d..d5633b4a82 100644 --- a/modules/GraphStreaming/pom.xml +++ b/modules/GraphStreaming/pom.xml @@ -15,7 +15,31 @@ GraphStreaming - + + org.gephi.streaming + api + 1.0.0 + + + org.gephi.streaming + impl + 1.0.0 + + + org.gephi.streaming + server + 1.0.0 + + + org.gephi.desktop + streaming + 1.0.0 + + + org.gephi.lib + jetty + 1.0.0 + From d9bf5deb52f622935ee61b8236811bbf21436335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Thu, 14 Jan 2016 00:53:21 +0100 Subject: [PATCH 10/30] Module configurations set to pass build validation --- modules/DesktopStreaming/src/main/nbm/manifest.mf | 1 + modules/GraphStreaming/pom.xml | 1 + .../main/resources/org/gephi/graphstreaming/Bundle.properties | 2 +- modules/JettyWrapper/src/main/nbm/manifest.mf | 1 + modules/StreamingAPI/src/main/nbm/manifest.mf | 1 + modules/StreamingImpl/src/main/nbm/manifest.mf | 1 + modules/StreamingServer/src/main/nbm/manifest.mf | 1 + 7 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/DesktopStreaming/src/main/nbm/manifest.mf b/modules/DesktopStreaming/src/main/nbm/manifest.mf index bfeb5f83c1..59ec3c24cc 100644 --- a/modules/DesktopStreaming/src/main/nbm/manifest.mf +++ b/modules/DesktopStreaming/src/main/nbm/manifest.mf @@ -1,2 +1,3 @@ Manifest-Version: 1.0 OpenIDE-Module-Localizing-Bundle: org/gephi/desktop/streaming/Bundle.properties +AutoUpdate-Show-In-Client: false diff --git a/modules/GraphStreaming/pom.xml b/modules/GraphStreaming/pom.xml index d5633b4a82..c9ec8653bd 100644 --- a/modules/GraphStreaming/pom.xml +++ b/modules/GraphStreaming/pom.xml @@ -51,6 +51,7 @@ Andre Panisson $sourcecode_url gephi.org + CDDL 1.0 and GNU GPL v3 diff --git a/modules/GraphStreaming/src/main/resources/org/gephi/graphstreaming/Bundle.properties b/modules/GraphStreaming/src/main/resources/org/gephi/graphstreaming/Bundle.properties index 4b19337127..6056ad3903 100644 --- a/modules/GraphStreaming/src/main/resources/org/gephi/graphstreaming/Bundle.properties +++ b/modules/GraphStreaming/src/main/resources/org/gephi/graphstreaming/Bundle.properties @@ -1,4 +1,4 @@ -OpenIDE-Module-Display-Category=Plugin +OpenIDE-Module-Display-Category=Tool OpenIDE-Module-Long-Description=\ The purpose of the Graph Streaming API and Plugins is to build a unified framework for streaming graph objects. \ Gephi\u2019s data structure and visualization engine has been built with the idea that a graph is not static and might change continuously. \ diff --git a/modules/JettyWrapper/src/main/nbm/manifest.mf b/modules/JettyWrapper/src/main/nbm/manifest.mf index 213417b83d..12807d180a 100644 --- a/modules/JettyWrapper/src/main/nbm/manifest.mf +++ b/modules/JettyWrapper/src/main/nbm/manifest.mf @@ -1,2 +1,3 @@ Manifest-Version: 1.0 OpenIDE-Module-Localizing-Bundle: org/gephi/lib/jetty/Bundle.properties +AutoUpdate-Show-In-Client: false diff --git a/modules/StreamingAPI/src/main/nbm/manifest.mf b/modules/StreamingAPI/src/main/nbm/manifest.mf index 82f5b054ae..cce6352cef 100644 --- a/modules/StreamingAPI/src/main/nbm/manifest.mf +++ b/modules/StreamingAPI/src/main/nbm/manifest.mf @@ -1,2 +1,3 @@ Manifest-Version: 1.0 OpenIDE-Module-Localizing-Bundle: org/gephi/streaming/api/Bundle.properties +AutoUpdate-Show-In-Client: false diff --git a/modules/StreamingImpl/src/main/nbm/manifest.mf b/modules/StreamingImpl/src/main/nbm/manifest.mf index b5ab519d74..7f294e98cd 100644 --- a/modules/StreamingImpl/src/main/nbm/manifest.mf +++ b/modules/StreamingImpl/src/main/nbm/manifest.mf @@ -1,2 +1,3 @@ Manifest-Version: 1.0 OpenIDE-Module-Localizing-Bundle: org/gephi/streaming/impl/Bundle.properties +AutoUpdate-Show-In-Client: false diff --git a/modules/StreamingServer/src/main/nbm/manifest.mf b/modules/StreamingServer/src/main/nbm/manifest.mf index 7f98f105b9..1de2f86afe 100644 --- a/modules/StreamingServer/src/main/nbm/manifest.mf +++ b/modules/StreamingServer/src/main/nbm/manifest.mf @@ -1,2 +1,3 @@ Manifest-Version: 1.0 OpenIDE-Module-Localizing-Bundle: org/gephi/streaming/server/Bundle.properties +AutoUpdate-Show-In-Client: false From fec795d262d3976c13acded2e42a0c5686e75d87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Sat, 16 Jan 2016 20:28:20 +0100 Subject: [PATCH 11/30] Add observers to replace graph listeners --- .../server/impl/ServerControllerImpl.java | 44 +++++++++++++++++++ .../server/impl/ServerOperationExecutor.java | 9 ++-- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerControllerImpl.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerControllerImpl.java index 5942c5aac0..00c71c4222 100644 --- a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerControllerImpl.java +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerControllerImpl.java @@ -46,12 +46,17 @@ Development and Distribution License("CDDL") (collectively, the import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Timer; +import java.util.TimerTask; import javax.servlet.AsyncContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphObserver; +import org.gephi.graph.api.TableObserver; +import org.gephi.streaming.api.Graph2EventListener; import org.gephi.streaming.server.ServerController; import org.openide.util.Exceptions; @@ -82,10 +87,48 @@ public String getURL() { private final ServerOperationExecutor executor; + private class StreamingGraphObserver extends TimerTask { + + private static final long PERIOD = 10; + private final GraphObserver graphObserver; + private final TableObserver tableObserver; + private Graph2EventListener changeListener; + private final Timer timer; + + public StreamingGraphObserver(GraphObserver graphObserver, TableObserver tableObserver, Graph2EventListener changeListener) { + this.graphObserver = graphObserver; + this.tableObserver = tableObserver; + this.changeListener = changeListener; + + this.timer = new Timer("Streaming Observer Thread", true); + timer.schedule(this, PERIOD, PERIOD); + + } + + public void run() { + if (this.graphObserver.hasGraphChanged()) { + this.changeListener.graphChanged(graphObserver.getDiff()); + } + if(this.tableObserver.hasTableChanged()) { + this.changeListener.attributesChanged(tableObserver.getDiff()); + } + } + + public void shutdown() { + timer.cancel(); + } + } + + private final StreamingGraphObserver streamingGraphObserver; + public ServerControllerImpl(Graph graph) { clientManager = new ClientManagerImpl(); executor = new ServerOperationExecutor(graph, clientManager); + Graph2EventListener changeListener = new Graph2EventListener(graph, executor.getEventHandler()); + GraphObserver graphObserver = graph.getModel().createGraphObserver(graph, true); + TableObserver tableObserver = graph.getModel().getNodeTable().createTableObserver(true); + streamingGraphObserver = new StreamingGraphObserver(graphObserver, tableObserver, changeListener); } /* (non-Javadoc) @@ -200,6 +243,7 @@ public void handle(HttpServletRequest request, HttpServletResponse response) { */ public void stop() { clientManager.stopAll(); + streamingGraphObserver.shutdown(); } private void executeError(HttpServletResponse response, String message) throws IOException { diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerOperationExecutor.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerOperationExecutor.java index 606e8491a4..90823941f5 100644 --- a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerOperationExecutor.java +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerOperationExecutor.java @@ -91,11 +91,6 @@ public ServerOperationExecutor(Graph graph, ClientManagerImpl clientManager) { writerFactory = Lookup.getDefault().lookup(StreamWriterFactory.class); readerFactory = Lookup.getDefault().lookup(StreamReaderFactory.class); eventBuilder = new GraphEventBuilder(this); - - Graph2EventListener changeListener = new Graph2EventListener(graph, graphBufferedOperationSupport); -// graph.getGraphModel().addGraphListener(changeListener); -// AttributeController ac = Lookup.getDefault().lookup(AttributeController.class); -// ac.getModel().addAttributeListener(changeListener); } /** @@ -268,4 +263,8 @@ private Map getEdgeAttributes(Edge edge) { return attributes; } + + public GraphEventHandler getEventHandler() { + return graphBufferedOperationSupport; + } } From 0ad5dd025f563ce149bbf5d2d8d866e58da74605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Tue, 19 Jan 2016 17:47:32 +0100 Subject: [PATCH 12/30] Set plugin category to Import --- .../main/resources/org/gephi/graphstreaming/Bundle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/GraphStreaming/src/main/resources/org/gephi/graphstreaming/Bundle.properties b/modules/GraphStreaming/src/main/resources/org/gephi/graphstreaming/Bundle.properties index 6056ad3903..c40e34d595 100644 --- a/modules/GraphStreaming/src/main/resources/org/gephi/graphstreaming/Bundle.properties +++ b/modules/GraphStreaming/src/main/resources/org/gephi/graphstreaming/Bundle.properties @@ -1,4 +1,4 @@ -OpenIDE-Module-Display-Category=Tool +OpenIDE-Module-Display-Category=Import OpenIDE-Module-Long-Description=\ The purpose of the Graph Streaming API and Plugins is to build a unified framework for streaming graph objects. \ Gephi\u2019s data structure and visualization engine has been built with the idea that a graph is not static and might change continuously. \ From 3532a92a984400dc18380ac16c1d254586b6cd45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Wed, 20 Jan 2016 01:13:31 +0100 Subject: [PATCH 13/30] Test fix and add back test execution --- modules/StreamingImpl/pom.xml | 2 +- .../src/test/java/org/gephi/streaming/test/ListenersTest.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/StreamingImpl/pom.xml b/modules/StreamingImpl/pom.xml index 97c7317f43..08012fe80e 100644 --- a/modules/StreamingImpl/pom.xml +++ b/modules/StreamingImpl/pom.xml @@ -49,7 +49,7 @@ - true + false diff --git a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/ListenersTest.java b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/ListenersTest.java index 97571e598d..9753822dc6 100644 --- a/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/ListenersTest.java +++ b/modules/StreamingImpl/src/test/java/org/gephi/streaming/test/ListenersTest.java @@ -84,6 +84,7 @@ public void runTest() { GraphController graphController = Lookup.getDefault().lookup(GraphController.class); GraphModel graphModel = graphController.getGraphModel(); Graph graph = graphModel.getGraph(); + graph.clear(); GraphEventHandler printerHandler = new GraphEventHandler() { From bcc766d8565252f10817005ec0bfe7e3d86232e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Thu, 21 Jan 2016 19:53:29 +0100 Subject: [PATCH 14/30] Config files cleanup --- build.xml | 14 --- modules/DesktopStreaming/pom.xml | 8 +- modules/GraphStreaming/pom.xml | 12 +- .../gephi/graphstreaming/Bundle.properties | 6 +- modules/StreamingAPI/pom.xml | 4 +- modules/StreamingImpl/pom.xml | 6 +- modules/StreamingServer/pom.xml | 108 +++++++++--------- 7 files changed, 70 insertions(+), 88 deletions(-) delete mode 100644 build.xml diff --git a/build.xml b/build.xml deleted file mode 100644 index 5ccfd4c882..0000000000 --- a/build.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - diff --git a/modules/DesktopStreaming/pom.xml b/modules/DesktopStreaming/pom.xml index e54ceaa751..e4a8bc2419 100644 --- a/modules/DesktopStreaming/pom.xml +++ b/modules/DesktopStreaming/pom.xml @@ -76,12 +76,12 @@ org.gephi.streaming api - 1.0.0 + ${project.version} org.gephi.streaming server - 1.0.0 + ${project.version} @@ -92,8 +92,8 @@ org.codehaus.mojo nbm-maven-plugin - Unknown - $sourcecode_url + Andre Panisson + https://github.com/panisson/gephi-plugins/tree/graph-streaming diff --git a/modules/GraphStreaming/pom.xml b/modules/GraphStreaming/pom.xml index c9ec8653bd..633019fded 100644 --- a/modules/GraphStreaming/pom.xml +++ b/modules/GraphStreaming/pom.xml @@ -18,27 +18,27 @@ org.gephi.streaming api - 1.0.0 + ${project.version} org.gephi.streaming impl - 1.0.0 + ${project.version} org.gephi.streaming server - 1.0.0 + ${project.version} org.gephi.desktop streaming - 1.0.0 + ${project.version} org.gephi.lib jetty - 1.0.0 + ${project.version} @@ -49,7 +49,7 @@ nbm-maven-plugin Andre Panisson - $sourcecode_url + https://github.com/panisson/gephi-plugins/tree/graph-streaming gephi.org CDDL 1.0 and GNU GPL v3 diff --git a/modules/GraphStreaming/src/main/resources/org/gephi/graphstreaming/Bundle.properties b/modules/GraphStreaming/src/main/resources/org/gephi/graphstreaming/Bundle.properties index c40e34d595..7d649b79d3 100644 --- a/modules/GraphStreaming/src/main/resources/org/gephi/graphstreaming/Bundle.properties +++ b/modules/GraphStreaming/src/main/resources/org/gephi/graphstreaming/Bundle.properties @@ -1,8 +1,4 @@ OpenIDE-Module-Display-Category=Import -OpenIDE-Module-Long-Description=\ - The purpose of the Graph Streaming API and Plugins is to build a unified framework for streaming graph objects. \ - Gephi\u2019s data structure and visualization engine has been built with the idea that a graph is not static and might change continuously. \ - By connecting Gephi with external data-sources, we leverage its power to visualize and monitor complex systems or enterprise data in real-time. \ - Moreover, the idea of streaming graph data goes beyond Gephi, and a unified and standardized API could bring interoperability with other available tools for graph and network analysis, as they could start to interoperate with other tools in a distributed and cooperative fashion. +OpenIDE-Module-Long-Description=The purpose of the Graph Streaming API and Plugins is to build a unified framework for streaming graph objects. Gephi\u2019s data structure and visualization engine has been built with the idea that a graph is not static and might change continuously. By connecting Gephi with external data-sources, we leverage its power to visualize and monitor complex systems or enterprise data in real-time. Moreover, the idea of streaming graph data goes beyond Gephi, and a unified and standardized API could bring interoperability with other available tools for graph and network analysis, as they could start to interoperate with other tools in a distributed and cooperative fashion. OpenIDE-Module-Name=Graph Streaming OpenIDE-Module-Short-Description=Graph Streaming Plugin diff --git a/modules/StreamingAPI/pom.xml b/modules/StreamingAPI/pom.xml index 51f0115255..f167c511b3 100644 --- a/modules/StreamingAPI/pom.xml +++ b/modules/StreamingAPI/pom.xml @@ -32,8 +32,8 @@ org.codehaus.mojo nbm-maven-plugin - Unknown - $sourcecode_url + Andre Panisson + https://github.com/panisson/gephi-plugins/tree/graph-streaming org.gephi.streaming.api diff --git a/modules/StreamingImpl/pom.xml b/modules/StreamingImpl/pom.xml index 08012fe80e..e919f83dcf 100644 --- a/modules/StreamingImpl/pom.xml +++ b/modules/StreamingImpl/pom.xml @@ -39,7 +39,7 @@ org.gephi.streaming api - 1.0.0 + ${project.version} junit @@ -57,8 +57,8 @@ org.codehaus.mojo nbm-maven-plugin - Unknown - $sourcecode_url + Andre Panisson + https://github.com/panisson/gephi-plugins/tree/graph-streaming diff --git a/modules/StreamingServer/pom.xml b/modules/StreamingServer/pom.xml index 0bbf9e0bf9..947f427e1e 100644 --- a/modules/StreamingServer/pom.xml +++ b/modules/StreamingServer/pom.xml @@ -16,59 +16,59 @@ - javax.servlet - javax.servlet-api - 3.1.0 - - - org.eclipse.jetty - jetty-websocket - 8.1.17.v20150415 - - - org.eclipse.jetty - jetty-server - 8.1.17.v20150415 - - - org.eclipse.jetty - jetty-servlet - 8.1.17.v20150415 - - - org.gephi.streaming - api - 1.0.0 - - - org.gephi.streaming - impl - 1.0.0 - + javax.servlet + javax.servlet-api + 3.1.0 + + + org.eclipse.jetty + jetty-websocket + 8.1.17.v20150415 + + + org.eclipse.jetty + jetty-server + 8.1.17.v20150415 + + + org.eclipse.jetty + jetty-servlet + 8.1.17.v20150415 + + + org.gephi.streaming + api + ${project.version} + + + org.gephi.streaming + impl + ${project.version} + - - org.netbeans.api - org-openide-util-lookup - - - org.gephi - graph-api - - - org.netbeans.api - org-openide-util - - - org.gephi - project-api - - - junit - junit - 4.10 - test - - + + org.netbeans.api + org-openide-util-lookup + + + org.gephi + graph-api + + + org.netbeans.api + org-openide-util + + + org.gephi + project-api + + + junit + junit + 4.10 + test + + @@ -76,8 +76,8 @@ org.codehaus.mojo nbm-maven-plugin - Unknown - $sourcecode_url + Andre Panisson + https://github.com/panisson/gephi-plugins/tree/graph-streaming org.gephi.streaming.server From 81abc68e344628d5edf885b58d634d6898820ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Thu, 21 Jan 2016 21:41:10 +0100 Subject: [PATCH 15/30] Naming strategy revision --- modules/DesktopStreaming/pom.xml | 12 ++++++------ modules/GraphStreaming/pom.xml | 16 ++++++++-------- modules/StreamingAPI/pom.xml | 4 ++-- modules/StreamingImpl/pom.xml | 8 ++++---- modules/StreamingServer/pom.xml | 12 ++++++------ 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/modules/DesktopStreaming/pom.xml b/modules/DesktopStreaming/pom.xml index e4a8bc2419..118e12fb24 100644 --- a/modules/DesktopStreaming/pom.xml +++ b/modules/DesktopStreaming/pom.xml @@ -7,8 +7,8 @@ 0.9.0 - org.gephi.desktop - streaming + org.gephi + desktop-streaming 1.0.0 nbm @@ -74,13 +74,13 @@ - org.gephi.streaming - api + org.gephi + streaming-api ${project.version} - org.gephi.streaming - server + org.gephi + streaming-server ${project.version} diff --git a/modules/GraphStreaming/pom.xml b/modules/GraphStreaming/pom.xml index 633019fded..6c6ec252a2 100644 --- a/modules/GraphStreaming/pom.xml +++ b/modules/GraphStreaming/pom.xml @@ -16,23 +16,23 @@ - org.gephi.streaming - api + org.gephi + streaming-api ${project.version} - org.gephi.streaming - impl + org.gephi + streaming-impl ${project.version} - org.gephi.streaming - server + org.gephi + streaming-server ${project.version} - org.gephi.desktop - streaming + org.gephi + desktop-streaming ${project.version} diff --git a/modules/StreamingAPI/pom.xml b/modules/StreamingAPI/pom.xml index f167c511b3..f88964b193 100644 --- a/modules/StreamingAPI/pom.xml +++ b/modules/StreamingAPI/pom.xml @@ -7,8 +7,8 @@ 0.9.0 - org.gephi.streaming - api + org.gephi + streaming-api 1.0.0 nbm diff --git a/modules/StreamingImpl/pom.xml b/modules/StreamingImpl/pom.xml index e919f83dcf..e8a610a716 100644 --- a/modules/StreamingImpl/pom.xml +++ b/modules/StreamingImpl/pom.xml @@ -7,8 +7,8 @@ 0.9.0 - org.gephi.streaming - impl + org.gephi + streaming-impl 1.0.0 nbm @@ -37,8 +37,8 @@ graph-api - org.gephi.streaming - api + org.gephi + streaming-api ${project.version} diff --git a/modules/StreamingServer/pom.xml b/modules/StreamingServer/pom.xml index 947f427e1e..5c25529b5c 100644 --- a/modules/StreamingServer/pom.xml +++ b/modules/StreamingServer/pom.xml @@ -7,8 +7,8 @@ 0.9.0 - org.gephi.streaming - server + org.gephi + streaming-server 1.0.0 nbm @@ -36,13 +36,13 @@ 8.1.17.v20150415 - org.gephi.streaming - api + org.gephi + streaming-api ${project.version} - org.gephi.streaming - impl + org.gephi + streaming-impl ${project.version} From 2d9e0d73f62504cf61d67ae499649d1782fb0182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Thu, 21 Jan 2016 22:38:51 +0100 Subject: [PATCH 16/30] Fix references to Jetty library wrapper --- modules/GraphStreaming/pom.xml | 4 ++-- modules/JettyWrapper/pom.xml | 4 ++-- modules/StreamingServer/pom.xml | 25 +++++-------------------- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/modules/GraphStreaming/pom.xml b/modules/GraphStreaming/pom.xml index 6c6ec252a2..52fbfb2be7 100644 --- a/modules/GraphStreaming/pom.xml +++ b/modules/GraphStreaming/pom.xml @@ -36,8 +36,8 @@ ${project.version} - org.gephi.lib - jetty + org.gephi + jetty-library-wrapper ${project.version} diff --git a/modules/JettyWrapper/pom.xml b/modules/JettyWrapper/pom.xml index e8dddf6f86..ef8d8f3c63 100644 --- a/modules/JettyWrapper/pom.xml +++ b/modules/JettyWrapper/pom.xml @@ -7,8 +7,8 @@ 0.9.0 - org.gephi.lib - jetty + org.gephi + jetty-library-wrapper 1.0.0 nbm diff --git a/modules/StreamingServer/pom.xml b/modules/StreamingServer/pom.xml index 5c25529b5c..de9379d27a 100644 --- a/modules/StreamingServer/pom.xml +++ b/modules/StreamingServer/pom.xml @@ -15,26 +15,6 @@ StreamingServer - - javax.servlet - javax.servlet-api - 3.1.0 - - - org.eclipse.jetty - jetty-websocket - 8.1.17.v20150415 - - - org.eclipse.jetty - jetty-server - 8.1.17.v20150415 - - - org.eclipse.jetty - jetty-servlet - 8.1.17.v20150415 - org.gephi streaming-api @@ -45,6 +25,11 @@ streaming-impl ${project.version} + + org.gephi + jetty-library-wrapper + ${project.version} + org.netbeans.api From 12a011fb6edcf2f55ab7a92b3c65a1f5b4af7fd1 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 4 Feb 2016 18:33:31 +0100 Subject: [PATCH 17/30] Add answer to README FAQ --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 619029d48b..b6d501cf4d 100644 --- a/README.md +++ b/README.md @@ -198,3 +198,7 @@ Public packages are configured in the module's `pom.xml` file. Edit the ` Date: Thu, 11 Feb 2016 20:11:32 +0100 Subject: [PATCH 18/30] Add NBM signing step in plugins --- modules/pom.xml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/modules/pom.xml b/modules/pom.xml index 6c457cea8a..0c8242ebb7 100644 --- a/modules/pom.xml +++ b/modules/pom.xml @@ -557,8 +557,28 @@ - + + + release + + + + org.codehaus.mojo + nbm-maven-plugin + true + + + keystore.ks + gephi + ${keystore.password} + + + + + + + release-pom From 8db9e6a64d127169077abff8223fe698124b045a Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 14 Feb 2016 22:12:57 +0100 Subject: [PATCH 19/30] Update version to Gephi 0.9.1 --- modules/pom.xml | 4 ++-- pom.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/pom.xml b/modules/pom.xml index 0c8242ebb7..5a55108243 100644 --- a/modules/pom.xml +++ b/modules/pom.xml @@ -4,7 +4,7 @@ org.gephi gephi-plugin-parent - 0.9.0 + 0.9.1 pom gephi-plugins-parent @@ -46,7 +46,7 @@ UTF-8 - 0.9.0 + 0.9.1 RELEASE81 diff --git a/pom.xml b/pom.xml index 4dc0605b72..839cbc6e88 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.gephi gephi-plugins - 0.9.0 + 0.9.1 pom gephi-plugins @@ -17,7 +17,7 @@ - 0.9.0 + 0.9.1 ${project.build.directory}/plugins_clusters github From 50546800c9c638c7b9471ab97cd7a07697c1660d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Mon, 15 Feb 2016 12:32:22 +0100 Subject: [PATCH 20/30] Correctly handle not initialized projects --- .../streaming/StreamingUIController.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingUIController.java b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingUIController.java index df033129db..0c838d9dd7 100644 --- a/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingUIController.java +++ b/modules/DesktopStreaming/src/main/java/org/gephi/desktop/streaming/StreamingUIController.java @@ -186,15 +186,18 @@ public void synchronize(StreamingConnection connection) { // Get active graph instance - Project and Graph API ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); Project project = projectController.getCurrentProject(); - if (project==null) - projectController.newProject(); + if (project==null) { + logger.log(Level.WARNING, "Project not initialized during synchronize."); + return; + } Workspace workspace = projectController.getCurrentWorkspace(); - if (workspace==null) - workspace = projectController.newWorkspace(projectController.getCurrentProject()); -// projectController.openWorkspace(workspace); + if (workspace==null){ + logger.log(Level.WARNING, "Workspace not initialized during synchronize."); + return; + } GraphController graphController = Lookup.getDefault().lookup(GraphController.class); - GraphModel graphModel = graphController.getGraphModel(); + GraphModel graphModel = graphController.getGraphModel(workspace); Graph graph = graphModel.getGraph(); graph.clear(); @@ -257,18 +260,18 @@ public void startMaster() { ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class); Project project = projectController.getCurrentProject(); if (project==null) { - //TODO: Invalid project + logger.log(Level.WARNING, "Project not initialized during startMaster."); return; } Workspace workspace = projectController.getCurrentWorkspace(); if (workspace==null) { - //TODO: Invalid workspace + logger.log(Level.WARNING, "Workspace not initialized during startMaster."); return; } GraphController graphController = Lookup.getDefault().lookup(GraphController.class); - Graph graph = graphController.getGraphModel().getGraph(); + Graph graph = graphController.getGraphModel(workspace).getGraph(); WorkspaceInformation wi = workspace.getLookup().lookup(WorkspaceInformation.class); String context = "/"+wi.getName().replaceAll(" ", "").toLowerCase(); From d8d74ccd189dbe80213e84f559a81370d697fffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Mon, 15 Feb 2016 12:46:07 +0100 Subject: [PATCH 21/30] Correctly test property columns --- .../java/org/gephi/streaming/api/Graph2EventListener.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Graph2EventListener.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Graph2EventListener.java index 76074f7f47..37998eab28 100644 --- a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Graph2EventListener.java +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Graph2EventListener.java @@ -50,7 +50,6 @@ Development and Distribution License("CDDL") (collectively, the import org.gephi.graph.api.GraphDiff; import org.gephi.graph.api.Node; import org.gephi.graph.api.TableDiff; -import org.gephi.graph.impl.GraphStoreConfiguration; import org.gephi.streaming.api.event.ElementType; import org.gephi.streaming.api.event.EventType; import org.gephi.streaming.api.event.GraphEventBuilder; @@ -107,7 +106,7 @@ public void graphChanged(GraphDiff graphDiff) { } public void attributesChanged(TableDiff tableDiff) { - //TODO: Reimplement this method + //TODO: Reimplement this method, check diffs in columnObserver when available // switch (event.getEventType()) { // case ADD_COLUMN: @@ -206,7 +205,7 @@ public void attributesChanged(TableDiff tableDiff) { private Map getNodeAttributes(Node node) { Map attributes = new HashMap(); for (Column column: node.getAttributeColumns()) { - if (column.getIndex() != GraphStoreConfiguration.ELEMENT_ID_INDEX) { + if (!column.isProperty()) { Object value = node.getAttribute(column); if (value != null) attributes.put(column.getTitle(), value); @@ -231,7 +230,7 @@ private Map getNodeAttributes(Node node) { private Map getEdgeAttributes(Edge edge) { Map attributes = new HashMap(); for (Column column: edge.getAttributeColumns()) { - if (column.getIndex() != GraphStoreConfiguration.ELEMENT_ID_INDEX) { + if (!column.isProperty()) { Object value = edge.getAttribute(column); if (value != null) attributes.put(column.getTitle(), value); From d3a477e571fb7369b20c6bfbed8ba99cc049eccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Mon, 15 Feb 2016 18:20:09 +0100 Subject: [PATCH 22/30] Correctly handle graph locks and other cleanups --- .../api/GraphUpdaterEventHandler.java | 69 +++++++------------ 1 file changed, 25 insertions(+), 44 deletions(-) diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java index 9e77252aea..fdeb9051a5 100644 --- a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java @@ -214,34 +214,34 @@ private void edgeAdded(String edgeId, String fromNodeId, String toNodeId, return; } + graph.writeLock(); + Node source = graph.getNode(fromNodeId); if (source==null) { log("Edge added event ignored for edge "+edgeId+": Source node "+fromNodeId+" not found"); + graph.writeUnlock(); return; } Node target = graph.getNode(toNodeId); if (target==null) { log("Edge added event ignored for edge "+edgeId+": Target node "+toNodeId+" not found"); + graph.writeUnlock(); return; } - - if(source!=null && target!=null) { - edge = factory.newEdge(edgeId, source, target,0 , 1.0f, directed); - - if (attributes!=null && attributes.size() > 0) { - for(Map.Entry entry: attributes.entrySet()) { - this.addEdgeAttribute(edge, entry.getKey(), entry.getValue()); - } + edge = factory.newEdge(edgeId, source, target,0 , 1.0f, directed); + + if (attributes!=null && attributes.size() > 0) { + for(Map.Entry entry: attributes.entrySet()) { + this.addEdgeAttribute(edge, entry.getKey(), entry.getValue()); } - - graph.writeLock(); - graph.addEdge(edge); - - // graph.setId(edge, edgeId); - graph.writeUnlock(); } + + + graph.addEdge(edge); + + graph.writeUnlock(); } private void edgeChanged(String edgeId, Map attributes) { @@ -265,9 +265,7 @@ private void edgeChanged(String edgeId, Map attributes) { private void edgeRemoved(String edgeId) { Edge edge = graph.getEdge(edgeId); if (edge!=null) { - graph.writeLock(); graph.removeEdge(edge); - graph.writeUnlock(); } else { log("Edge removed event ignored for edge "+edgeId+": Edge not found"); } @@ -284,10 +282,8 @@ private void nodeAdded(String nodeId, Map attributes) { } } - graph.writeLock(); // graph.setId(node, nodeId); graph.addNode(node); - graph.writeUnlock(); } else { log("Node added event ignored for node "+nodeId+": Node already exists"); } @@ -314,14 +310,7 @@ private void nodeChanged(String nodeId, Map attributes) { private void nodeRemoved(String nodeId) { Node node = graph.getNode(nodeId); if (node!=null) { - graph.writeLock(); - - for (Edge edge: graph.getEdges(node).toArray()) { - graph.removeEdge(edge); - } - graph.removeNode(node); - graph.writeUnlock(); } else { log("Node changed event ignored for node "+nodeId+": Node not found"); } @@ -333,12 +322,10 @@ private void addNodeAttribute(Node node, String attributeName, Object value) { injectNodeProperty(p, value, node); } - else if (!node.getAttributeKeys().isEmpty()) { - if (!graph.getModel().getNodeTable().hasColumn(attributeName)) { - graph.getModel().getNodeTable().addColumn(attributeName, value.getClass()); - } - node.setAttribute(attributeName, value); + if (!graph.getModel().getNodeTable().hasColumn(attributeName)) { + graph.getModel().getNodeTable().addColumn(attributeName, value.getClass()); } + node.setAttribute(attributeName, value); } private void addEdgeAttribute(Edge edge, String attributeName, Object value) { @@ -346,12 +333,11 @@ private void addEdgeAttribute(Edge edge, String attributeName, Object value) { if (p != null) { injectEdgeProperty(p, value, edge); } - else if (! edge.getAttributeKeys().isEmpty()) { - if (!graph.getModel().getEdgeTable().hasColumn(attributeName)) { - graph.getModel().getEdgeTable().addColumn(attributeName, value.getClass()); - } - edge.setAttribute(attributeName, value); + + if (!graph.getModel().getEdgeTable().hasColumn(attributeName)) { + graph.getModel().getEdgeTable().addColumn(attributeName, value.getClass()); } + edge.setAttribute(attributeName, value); } private void injectNodeProperty(NodeProperties p, Object value, Node node) { @@ -499,6 +485,7 @@ private void injectEdgeProperty(EdgeProperties p, Object value, private void applyFilter(FilterEvent filterEvent) { + graph.writeLock(); Filter filter = filterEvent.getFilter(); if (filter instanceof NodeFilter) { NodeFilter nodeFilter = (NodeFilter)filter; @@ -511,24 +498,21 @@ private void applyFilter(FilterEvent filterEvent) { log("Unsupported FilterEvent of type ADD"); break; case CHANGE: - graph.writeLock(); if (attributes!=null && attributes.size() > 0) { for(Map.Entry entry: attributes.entrySet()) { this.addNodeAttribute(node, entry.getKey(), entry.getValue()); } } - graph.writeUnlock(); + break; case REMOVE: - graph.writeLock(); graph.removeNode(node); - graph.writeUnlock(); break; } } } } - + if (filter instanceof EdgeFilter) { EdgeFilter edgeFilter = (EdgeFilter)filter; Map attributes = filterEvent.getAttributes(); @@ -540,23 +524,20 @@ private void applyFilter(FilterEvent filterEvent) { log("Unsupported FilterEvent of type ADD"); break; case CHANGE: - graph.writeLock(); if (attributes!=null && attributes.size() > 0) { for(Map.Entry entry: attributes.entrySet()) { this.addEdgeAttribute(edge, entry.getKey(), entry.getValue()); } } - graph.writeUnlock(); break; case REMOVE: - graph.writeLock(); graph.removeEdge(edge); - graph.writeUnlock(); break; } } } } + graph.writeUnlock(); } From 606f5d8fee9b99bcbf622de7ce8b986dfce6255b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Mon, 15 Feb 2016 18:28:10 +0100 Subject: [PATCH 23/30] Delete empty and useless files --- .../resources/org/gephi/streaming/impl/cacerts | Bin 1266 -> 0 bytes .../resources/org/gephi/streaming/impl/layer.xml | 4 ---- 2 files changed, 4 deletions(-) delete mode 100644 modules/StreamingImpl/src/main/resources/org/gephi/streaming/impl/cacerts delete mode 100644 modules/StreamingImpl/src/main/resources/org/gephi/streaming/impl/layer.xml diff --git a/modules/StreamingImpl/src/main/resources/org/gephi/streaming/impl/cacerts b/modules/StreamingImpl/src/main/resources/org/gephi/streaming/impl/cacerts deleted file mode 100644 index b367cf1352a11eec84f4da60c018b41bafe65f67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1266 zcmezO_TO6u1_mY|W(3o$xs}x|XvNx5y7rA<^0WTY;R+~rLcV0$DR#pas#%YG!2Apinp)72|Or9Zz{6Gaj4u>#X zNPbafUOr5K9WKCUzyngjCCuucT9A=xC}AK5;&Ti0gZT=Kz^Ya|iK}PE37o{7>iSrs77?>GY z7#bOy7#l?axrRvG!RzoQMkVAxW@KexZerwTFlb`rVrpV!WH|8e?vz|6pO7gXT6<>Q zNNE3eIH6nZh^6}4{4I-1K5L%cv+DfSf6r&+n)tdNtywicpztMYkXW#uzE|Vt)HmB| zD)}Ea&-D;$I`U6{Q-8yD>7QZ0-=FBI&wcM&{?I;5dxg@aV+XFV@q1eH94Y9Hp0G25 zJ)@CvQ--p3$2EKTxn=T2-WTkBw@eFJYd8JH?!T{7_)7S88*F3^*lW6`ce?p1@d>vM zN%cFF2l>v>ZE6cD+}c#3CC?;rY#$&0%=?a}Y@62EaKf%*epF zxbdGs<8K35VET~dV-aH!NscR7oOtHj8A*LJLGQw7eY5|+c?=p)gXEQ28V?&Z9%#U- zW?|!;C5Tx&VvUSGK{B+=pf;bg>2#bYyC)&!{9Pu#lZNLNgK`Qk~7s<-}5Ez*5=dG~=+ zOWof6x^(|SlgYIe*H<(jdVM%EW3Ao=gR9|6MH_M!Ha?KHo!_nf;K>uN%PqG%7kpy0 z^}ltnx^DM{*gQ+VugXQ1*LIo7&K7&|kh%0@-_oZS=gY2ID*OGoV~OCyeZI5j&fIoI z@2t{>%L^YnwoW;;F7eki@!c6I>wml_Uu%$ z6l77eeZSUm>48_%|FbSPRAEaB+{|eAO{u9PeBG6$X; - - - From 71a4135771e5b4a630119cb578158d4de2f85304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Mon, 15 Feb 2016 18:33:38 +0100 Subject: [PATCH 24/30] Delete more empty files --- .../src/main/resources/org/gephi/streaming/server/layer.xml | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 modules/StreamingServer/src/main/resources/org/gephi/streaming/server/layer.xml diff --git a/modules/StreamingServer/src/main/resources/org/gephi/streaming/server/layer.xml b/modules/StreamingServer/src/main/resources/org/gephi/streaming/server/layer.xml deleted file mode 100644 index c16a8a0fed..0000000000 --- a/modules/StreamingServer/src/main/resources/org/gephi/streaming/server/layer.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - From 6ca0084e5b64ccf0c88cb1420ba11fd09687bb41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Mon, 15 Feb 2016 18:57:24 +0100 Subject: [PATCH 25/30] Refactor of getNodeAttributes and getEdgeAttributes into AttributeUtils class --- .../gephi/streaming/api/AttributeUtils.java | 99 +++++++++++++++++++ .../streaming/api/Graph2EventListener.java | 53 +--------- .../server/impl/DynamicGraphWriter.java | 60 ----------- .../streaming/server/impl/GraphWriter.java | 57 +---------- .../server/impl/ServerOperationExecutor.java | 55 +---------- 5 files changed, 109 insertions(+), 215 deletions(-) create mode 100644 modules/StreamingAPI/src/main/java/org/gephi/streaming/api/AttributeUtils.java diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/AttributeUtils.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/AttributeUtils.java new file mode 100644 index 0000000000..12aa9566b6 --- /dev/null +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/AttributeUtils.java @@ -0,0 +1,99 @@ +/* +Copyright 2008-2016 Gephi +Authors : Andre Panisson +Website : http://www.gephi.org + +This file is part of Gephi. + +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2011 Gephi Consortium. All rights reserved. + +The contents of this file are subject to the terms of either the GNU +General Public License Version 3 only ("GPL") or the Common +Development and Distribution License("CDDL") (collectively, the +"License"). You may not use this file except in compliance with the +License. You can obtain a copy of the License at +http://gephi.org/about/legal/license-notice/ +or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the +specific language governing permissions and limitations under the +License. When distributing the software, include this License Header +Notice in each file and include the License files at +/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the +License Header, with the fields enclosed by brackets [] replaced by +your own identifying information: +"Portions Copyrighted [year] [name of copyright owner]" + +If you wish your version of this file to be governed by only the CDDL +or only the GPL Version 3, indicate your decision by adding +"[Contributor] elects to include this software in this distribution +under the [CDDL or GPL Version 3] license." If you do not indicate a +single choice of license, a recipient has the option to distribute +your version of this file under either the CDDL, the GPL Version 3 or +to extend the choice of license to its licensees as provided above. +However, if you add GPL Version 3 code and therefore, elected the GPL +Version 3 license, then the option applies only if the new code is +made subject to such option by the copyright holder. + +Contributor(s): + +Portions Copyrighted 2016 Gephi Consortium. + */ +package org.gephi.streaming.api; + +import java.util.HashMap; +import java.util.Map; +import org.gephi.graph.api.Column; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Node; + +/** + * Utility methods for retrieving node/edge attributes. + * + * @author panisson + */ +public class AttributeUtils { + private static final boolean sendVizData = true; + + public static Map getNodeAttributes(Node node) { + Map attributes = new HashMap(); + for (Column column : node.getAttributeColumns()) { + if (!column.isProperty()) { + Object value = node.getAttribute(column); + if (value != null) { + attributes.put(column.getTitle(), value); + } + } + } + if (sendVizData) { + attributes.put("x", node.x()); + attributes.put("y", node.y()); + attributes.put("z", node.z()); + attributes.put("r", node.r()); + attributes.put("g", node.g()); + attributes.put("b", node.b()); + attributes.put("size", node.size()); + } + return attributes; + } + + public static Map getEdgeAttributes(Edge edge) { + Map attributes = new HashMap(); + for (Column column : edge.getAttributeColumns()) { + if (!column.isProperty()) { + Object value = edge.getAttribute(column); + if (value != null) { + attributes.put(column.getTitle(), value); + } + } + } + if (sendVizData) { + attributes.put("r", edge.r()); + attributes.put("g", edge.g()); + attributes.put("b", edge.b()); + attributes.put("weight", edge.getWeight()); + } + return attributes; + } + +} diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Graph2EventListener.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Graph2EventListener.java index 37998eab28..604017b9e6 100644 --- a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Graph2EventListener.java +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/Graph2EventListener.java @@ -63,7 +63,6 @@ public class Graph2EventListener { // implements GraphListener, AttributeListene private GraphEventHandler eventHandler; private GraphEventBuilder eventBuilder; private Graph graph; - private boolean sendVizData = true; public Graph2EventListener(Graph graph, GraphEventHandler eventHandler) { this.graph = graph; @@ -77,7 +76,7 @@ public void graphChanged(GraphDiff graphDiff) { String nodeId = node.getId().toString(); org.gephi.streaming.api.event.GraphEvent e = eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, nodeId, - getNodeAttributes(node)); + AttributeUtils.getNodeAttributes(node)); eventHandler.handleGraphEvent(e); } @@ -86,7 +85,7 @@ public void graphChanged(GraphDiff graphDiff) { org.gephi.streaming.api.event.GraphEvent e = eventBuilder.edgeAddedEvent(edgeId, edge.getSource().getId().toString(), edge.getTarget().getId().toString(), edge.isDirected(), - getEdgeAttributes(edge)); + AttributeUtils.getEdgeAttributes(edge)); eventHandler.handleGraphEvent(e); } @@ -201,52 +200,4 @@ public void attributesChanged(TableDiff tableDiff) { // break; // } } - - private Map getNodeAttributes(Node node) { - Map attributes = new HashMap(); - for (Column column: node.getAttributeColumns()) { - if (!column.isProperty()) { - Object value = node.getAttribute(column); - if (value != null) - attributes.put(column.getTitle(), value); - } - } - - if (sendVizData) { - attributes.put("x", node.x()); - attributes.put("y", node.y()); - attributes.put("z", node.z()); - - attributes.put("r", node.r()); - attributes.put("g", node.g()); - attributes.put("b", node.b()); - - attributes.put("size", node.size()); - } - - return attributes; - } - - private Map getEdgeAttributes(Edge edge) { - Map attributes = new HashMap(); - for (Column column: edge.getAttributeColumns()) { - if (!column.isProperty()) { - Object value = edge.getAttribute(column); - if (value != null) - attributes.put(column.getTitle(), value); - } - } - - if (sendVizData) { - - attributes.put("r", edge.r()); - attributes.put("g", edge.g()); - attributes.put("b", edge.b()); - - attributes.put("weight", edge.getWeight()); - } - - return attributes; - } - } diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/DynamicGraphWriter.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/DynamicGraphWriter.java index af226b57b4..941164c1b7 100644 --- a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/DynamicGraphWriter.java +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/DynamicGraphWriter.java @@ -283,64 +283,4 @@ public void writeGraph(GraphEventHandler operationSupport) { graph.readUnlock(); } } - - private Map getNodeAttributes(Node node) { - Map attributes = new HashMap(); - for (Column column: node.getAttributeColumns()) { - if (column.getIndex() != GraphStoreConfiguration.ELEMENT_ID_INDEX) { - Object value = node.getAttribute(column); - if (value != null) - attributes.put(column.getTitle(), value); - } - } - - if (sendVizData) { - attributes.put("x", node.x()); - attributes.put("y", node.y()); - attributes.put("z", node.z()); - - attributes.put("r", node.r()); - attributes.put("g", node.g()); - attributes.put("b", node.b()); - - attributes.put("size", node.size()); - } - - return attributes; - } - - private Map getEdgeAttributes(Edge edge) { - Map attributes = new HashMap(); - for (Column column: edge.getAttributeColumns()) { - if (column.getIndex() != GraphStoreConfiguration.ELEMENT_ID_INDEX) { - Object value = edge.getAttribute(column); - if (value != null) - attributes.put(column.getTitle(), value); - } - } - - if (sendVizData) { - - attributes.put("r", edge.r()); - attributes.put("g", edge.g()); - attributes.put("b", edge.b()); - - attributes.put("weight", edge.getWeight()); - } - - return attributes; - } - - private class ChangingData { - private Object object; - private String attributeName; - private Object attributeValue; - - public ChangingData(Object object, String attributeName, Object attributeValue) { - this.object = object; - this.attributeName = attributeName; - this.attributeValue = attributeValue; - } - } - } diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphWriter.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphWriter.java index 438db0c23e..dedae79ccd 100644 --- a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphWriter.java +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphWriter.java @@ -41,16 +41,13 @@ Development and Distribution License("CDDL") (collectively, the */ package org.gephi.streaming.server.impl; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Set; -import org.gephi.graph.api.Column; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; import org.gephi.graph.api.Node; -import org.gephi.graph.impl.GraphStoreConfiguration; +import org.gephi.streaming.api.AttributeUtils; import org.gephi.streaming.api.CompositeGraphEventHandler; import org.gephi.streaming.api.event.GraphEventBuilder; import org.gephi.streaming.api.GraphEventHandler; @@ -85,7 +82,7 @@ public void writeGraph(GraphEventHandler operationSupport) { String nodeId = (String) node.getId(); operationSupport.handleGraphEvent( eventBuilder.graphEvent(ElementType.NODE, - EventType.ADD, nodeId, getNodeAttributes(node))); + EventType.ADD, nodeId, AttributeUtils.getNodeAttributes(node))); writtenNodes.add(nodeId); for (Edge edge: graph.getEdges(node)) { @@ -97,7 +94,7 @@ public void writeGraph(GraphEventHandler operationSupport) { && writtenNodes.contains(targetId) ) { operationSupport.handleGraphEvent( eventBuilder.edgeAddedEvent(edgeId, sourceId, - targetId, edge.isDirected(), getEdgeAttributes(edge))); + targetId, edge.isDirected(), AttributeUtils.getEdgeAttributes(edge))); writtenEdges.add(edgeId); } } @@ -119,52 +116,4 @@ public void writeGraph(GraphEventHandler operationSupport) { graph.readUnlock(); } } - - private Map getNodeAttributes(Node node) { - Map attributes = new HashMap(); - for (Column column: node.getAttributeColumns()) { - if (column.getIndex() != GraphStoreConfiguration.ELEMENT_ID_INDEX) { - Object value = node.getAttribute(column); - if (value != null) - attributes.put(column.getTitle(), value); - } - } - - if (sendVizData) { - attributes.put("x", node.x()); - attributes.put("y", node.y()); - attributes.put("z", node.z()); - - attributes.put("r", node.r()); - attributes.put("g", node.g()); - attributes.put("b", node.b()); - - attributes.put("size", node.size()); - } - - return attributes; - } - - private Map getEdgeAttributes(Edge edge) { - Map attributes = new HashMap(); - for (Column column: edge.getAttributeColumns()) { - if (column.getIndex() != GraphStoreConfiguration.ELEMENT_ID_INDEX) { - Object value = edge.getAttribute(column); - if (value != null) - attributes.put(column.getTitle(), value); - } - } - - if (sendVizData) { - - attributes.put("r", edge.r()); - attributes.put("g", edge.g()); - attributes.put("b", edge.b()); - - attributes.put("weight", edge.getWeight()); - } - - return attributes; - } - } diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerOperationExecutor.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerOperationExecutor.java index 90823941f5..95a29a40dc 100644 --- a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerOperationExecutor.java +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerOperationExecutor.java @@ -54,6 +54,7 @@ Development and Distribution License("CDDL") (collectively, the import org.gephi.graph.api.Graph; import org.gephi.graph.api.Node; import org.gephi.graph.impl.GraphStoreConfiguration; +import org.gephi.streaming.api.AttributeUtils; import org.gephi.streaming.api.CompositeGraphEventHandler; import org.gephi.streaming.api.Graph2EventListener; import org.gephi.streaming.api.GraphEventHandler; @@ -79,7 +80,6 @@ public class ServerOperationExecutor { private final Graph graph; private final StreamWriterFactory writerFactory; private final StreamReaderFactory readerFactory; - private boolean sendVizData = true; private final GraphEventBuilder eventBuilder; private ClientManagerImpl clientManager; @@ -144,7 +144,8 @@ public void executeGetNode(String id, String format, OutputStream outputStream) Node node = graph.getNode(id); if (node != null) { String nodeId = node.getId().toString(); - writer.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, nodeId, getNodeAttributes(node))); + writer.handleGraphEvent(eventBuilder.graphEvent(ElementType.NODE, EventType.ADD, nodeId, + AttributeUtils.getNodeAttributes(node))); } } finally { graph.readUnlock(); @@ -174,7 +175,8 @@ public void executeGetEdge(String id, String format, OutputStream outputStream) String edgeId = edge.getId().toString(); String sourceId = edge.getSource().getId().toString(); String targetId = edge.getTarget().getId().toString(); - writer.handleGraphEvent(eventBuilder.edgeAddedEvent(edgeId, sourceId, targetId, edge.isDirected(), getEdgeAttributes(edge))); + writer.handleGraphEvent(eventBuilder.edgeAddedEvent(edgeId, sourceId, targetId, edge.isDirected(), + AttributeUtils.getEdgeAttributes(edge))); } } finally { graph.readUnlock(); @@ -217,53 +219,6 @@ public void executeUpdateGraph(String format, InputStream inputStream, OutputStr reader.processStream(inputStream); } - private Map getNodeAttributes(Node node) { - Map attributes = new HashMap(); - for (Column column: node.getAttributeColumns()) { - if (column.getIndex() != GraphStoreConfiguration.ELEMENT_ID_INDEX) { - Object value = node.getAttribute(column); - if (value != null) - attributes.put(column.getTitle(), value); - } - } - - if (sendVizData) { - attributes.put("x", node.x()); - attributes.put("y", node.y()); - attributes.put("z", node.z()); - - attributes.put("r", node.r()); - attributes.put("g", node.g()); - attributes.put("b", node.b()); - - attributes.put("size", node.size()); - } - - return attributes; - } - - private Map getEdgeAttributes(Edge edge) { - Map attributes = new HashMap(); - for (Column column: edge.getAttributeColumns()) { - if (column.getIndex() != GraphStoreConfiguration.ELEMENT_ID_INDEX) { - Object value = edge.getAttribute(column); - if (value != null) - attributes.put(column.getTitle(), value); - } - } - - if (sendVizData) { - - attributes.put("r", edge.r()); - attributes.put("g", edge.g()); - attributes.put("b", edge.b()); - - attributes.put("weight", edge.getWeight()); - } - - return attributes; - } - public GraphEventHandler getEventHandler() { return graphBufferedOperationSupport; } From 919b70349eee607031fc56e501e6a4ee3c6046bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Mon, 15 Feb 2016 19:06:35 +0100 Subject: [PATCH 26/30] Structured blocks for graph lock/unlock --- .../api/GraphUpdaterEventHandler.java | 132 +++++++++--------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java index fdeb9051a5..f018e8ea60 100644 --- a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java @@ -215,33 +215,33 @@ private void edgeAdded(String edgeId, String fromNodeId, String toNodeId, } graph.writeLock(); - - Node source = graph.getNode(fromNodeId); - if (source==null) { - log("Edge added event ignored for edge "+edgeId+": Source node "+fromNodeId+" not found"); - graph.writeUnlock(); - return; - } + try { + Node source = graph.getNode(fromNodeId); + if (source==null) { + log("Edge added event ignored for edge "+edgeId+": Source node "+fromNodeId+" not found"); + return; + } - Node target = graph.getNode(toNodeId); - if (target==null) { - log("Edge added event ignored for edge "+edgeId+": Target node "+toNodeId+" not found"); - graph.writeUnlock(); - return; - } - - edge = factory.newEdge(edgeId, source, target,0 , 1.0f, directed); + Node target = graph.getNode(toNodeId); + if (target==null) { + log("Edge added event ignored for edge "+edgeId+": Target node "+toNodeId+" not found"); + return; + } - if (attributes!=null && attributes.size() > 0) { - for(Map.Entry entry: attributes.entrySet()) { - this.addEdgeAttribute(edge, entry.getKey(), entry.getValue()); + edge = factory.newEdge(edgeId, source, target,0 , 1.0f, directed); + + if (attributes!=null && attributes.size() > 0) { + for(Map.Entry entry: attributes.entrySet()) { + this.addEdgeAttribute(edge, entry.getKey(), entry.getValue()); + } } - } - graph.addEdge(edge); + graph.addEdge(edge); - graph.writeUnlock(); + } finally { + graph.writeUnlock(); + } } private void edgeChanged(String edgeId, Map attributes) { @@ -486,59 +486,61 @@ private void injectEdgeProperty(EdgeProperties p, Object value, private void applyFilter(FilterEvent filterEvent) { graph.writeLock(); - Filter filter = filterEvent.getFilter(); - if (filter instanceof NodeFilter) { - NodeFilter nodeFilter = (NodeFilter)filter; - Map attributes = filterEvent.getAttributes(); - for (Node node: graph.getNodes().toArray()) { - if (nodeFilter.evaluate(graph, node)) { - - switch (filterEvent.getEventType()) { - case ADD: - log("Unsupported FilterEvent of type ADD"); - break; - case CHANGE: - if (attributes!=null && attributes.size() > 0) { - for(Map.Entry entry: attributes.entrySet()) { - this.addNodeAttribute(node, entry.getKey(), entry.getValue()); + try { + Filter filter = filterEvent.getFilter(); + if (filter instanceof NodeFilter) { + NodeFilter nodeFilter = (NodeFilter)filter; + Map attributes = filterEvent.getAttributes(); + for (Node node: graph.getNodes().toArray()) { + if (nodeFilter.evaluate(graph, node)) { + + switch (filterEvent.getEventType()) { + case ADD: + log("Unsupported FilterEvent of type ADD"); + break; + case CHANGE: + if (attributes!=null && attributes.size() > 0) { + for(Map.Entry entry: attributes.entrySet()) { + this.addNodeAttribute(node, entry.getKey(), entry.getValue()); + } } - } - - break; - case REMOVE: - graph.removeNode(node); - break; + + break; + case REMOVE: + graph.removeNode(node); + break; + } } } } - } - - if (filter instanceof EdgeFilter) { - EdgeFilter edgeFilter = (EdgeFilter)filter; - Map attributes = filterEvent.getAttributes(); - for (Edge edge: graph.getEdges().toArray()) { - if (edgeFilter.evaluate(graph, edge)) { - - switch (filterEvent.getEventType()) { - case ADD: - log("Unsupported FilterEvent of type ADD"); - break; - case CHANGE: - if (attributes!=null && attributes.size() > 0) { - for(Map.Entry entry: attributes.entrySet()) { - this.addEdgeAttribute(edge, entry.getKey(), entry.getValue()); + + if (filter instanceof EdgeFilter) { + EdgeFilter edgeFilter = (EdgeFilter)filter; + Map attributes = filterEvent.getAttributes(); + for (Edge edge: graph.getEdges().toArray()) { + if (edgeFilter.evaluate(graph, edge)) { + + switch (filterEvent.getEventType()) { + case ADD: + log("Unsupported FilterEvent of type ADD"); + break; + case CHANGE: + if (attributes!=null && attributes.size() > 0) { + for(Map.Entry entry: attributes.entrySet()) { + this.addEdgeAttribute(edge, entry.getKey(), entry.getValue()); + } } - } - break; - case REMOVE: - graph.removeEdge(edge); - break; + break; + case REMOVE: + graph.removeEdge(edge); + break; + } } } } + } finally { + graph.writeUnlock(); } - graph.writeUnlock(); - } } From 1c8105234953112d749b90f504b04033dc95ffb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Mon, 15 Feb 2016 19:21:52 +0100 Subject: [PATCH 27/30] Corrections on Exception handling --- .../streaming/server/impl/BasicAuthenticationFilter.java | 7 +++++-- .../java/org/gephi/streaming/server/impl/GraphWriter.java | 3 ++- .../gephi/streaming/server/impl/ServerControllerImpl.java | 4 ++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/BasicAuthenticationFilter.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/BasicAuthenticationFilter.java index 2b4bdf045c..33cb8809a0 100644 --- a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/BasicAuthenticationFilter.java +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/BasicAuthenticationFilter.java @@ -42,6 +42,8 @@ Development and Distribution License("CDDL") (collectively, the package org.gephi.streaming.server.impl; import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -57,6 +59,7 @@ Development and Distribution License("CDDL") (collectively, the */ public class BasicAuthenticationFilter implements AuthenticationFilter { + private static final Logger logger = Logger.getLogger(BasicAuthenticationFilter.class.getName()); private static final String REALM = "Gephi GraphStreaming"; private String user; @@ -94,11 +97,11 @@ private boolean doAuthenticate(HttpServletRequest request, HttpServletResponse r encoded = encoded.replace("Basic ", ""); - String decoded = null; + String decoded; try { decoded = new String(Base64.decode(encoded)); } catch (Exception e) { - e.printStackTrace(); + logger.log(Level.WARNING, "Unable to decode authorization header", e); return false; } diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphWriter.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphWriter.java index dedae79ccd..4ef04631e8 100644 --- a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphWriter.java +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/GraphWriter.java @@ -53,6 +53,7 @@ Development and Distribution License("CDDL") (collectively, the import org.gephi.streaming.api.GraphEventHandler; import org.gephi.streaming.api.event.ElementType; import org.gephi.streaming.api.event.EventType; +import org.openide.util.Exceptions; /** * @author panisson @@ -111,7 +112,7 @@ public void writeGraph(GraphEventHandler operationSupport) { // } } catch (Exception e) { - e.printStackTrace(); + Exceptions.printStackTrace(e); } finally { graph.readUnlock(); } diff --git a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerControllerImpl.java b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerControllerImpl.java index 00c71c4222..b9312040a6 100644 --- a/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerControllerImpl.java +++ b/modules/StreamingServer/src/main/java/org/gephi/streaming/server/impl/ServerControllerImpl.java @@ -48,6 +48,8 @@ Development and Distribution License("CDDL") (collectively, the import java.io.OutputStream; import java.util.Timer; import java.util.TimerTask; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.servlet.AsyncContext; import javax.servlet.http.HttpServletRequest; @@ -66,6 +68,7 @@ Development and Distribution License("CDDL") (collectively, the */ public class ServerControllerImpl implements ServerController { + private static final Logger logger = Logger.getLogger(ServerControllerImpl.class.getName()); private ClientManagerImpl clientManager; private enum Operations { @@ -230,6 +233,7 @@ public void handle(HttpServletRequest request, HttpServletResponse response) { return; } } catch (Exception e) { + logger.log(Level.WARNING, "Error executing operation", e); e.printStackTrace(); try { executeError(response, "Error: "+e.getMessage()); From 34331c4576f2b275a2016c321f5821466a0f0e1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Mon, 15 Feb 2016 20:14:46 +0100 Subject: [PATCH 28/30] Correct check for updating attributes --- .../api/GraphUpdaterEventHandler.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java index f018e8ea60..1c759703df 100644 --- a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/GraphUpdaterEventHandler.java @@ -320,24 +320,24 @@ private void addNodeAttribute(Node node, String attributeName, Object value) { NodeProperties p = properties.getNodeProperty(attributeName); if (p != null) { injectNodeProperty(p, value, node); + } else { + if (!graph.getModel().getNodeTable().hasColumn(attributeName)) { + graph.getModel().getNodeTable().addColumn(attributeName, value.getClass()); + } + node.setAttribute(attributeName, value); } - - if (!graph.getModel().getNodeTable().hasColumn(attributeName)) { - graph.getModel().getNodeTable().addColumn(attributeName, value.getClass()); - } - node.setAttribute(attributeName, value); } private void addEdgeAttribute(Edge edge, String attributeName, Object value) { EdgeProperties p = properties.getEdgeProperty(attributeName); if (p != null) { injectEdgeProperty(p, value, edge); - } - - if (!graph.getModel().getEdgeTable().hasColumn(attributeName)) { - graph.getModel().getEdgeTable().addColumn(attributeName, value.getClass()); - } + } else { + if (!graph.getModel().getEdgeTable().hasColumn(attributeName)) { + graph.getModel().getEdgeTable().addColumn(attributeName, value.getClass()); + } edge.setAttribute(attributeName, value); + } } private void injectNodeProperty(NodeProperties p, Object value, Node node) { From cc5f8acb066670c6735a795dd1db92b17908a2f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Tue, 16 Feb 2016 19:22:07 +0100 Subject: [PATCH 29/30] Some StreamingAPI code cleanup --- .../java/org/gephi/streaming/api/event/ElementEvent.java | 1 - .../org/gephi/streaming/api/event/GraphEventBuilder.java | 9 ++------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/ElementEvent.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/ElementEvent.java index 87fa898937..62dc066ace 100644 --- a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/ElementEvent.java +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/ElementEvent.java @@ -41,7 +41,6 @@ Development and Distribution License("CDDL") (collectively, the */ package org.gephi.streaming.api.event; -import java.util.Collections; import java.util.Map; /** diff --git a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/GraphEventBuilder.java b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/GraphEventBuilder.java index 9d4e557bb5..8d526a3ad9 100644 --- a/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/GraphEventBuilder.java +++ b/modules/StreamingAPI/src/main/java/org/gephi/streaming/api/event/GraphEventBuilder.java @@ -43,11 +43,6 @@ Development and Distribution License("CDDL") (collectively, the import java.util.Map; -import org.gephi.streaming.api.event.EdgeAddedEvent; -import org.gephi.streaming.api.event.ElementEvent; -import org.gephi.streaming.api.event.ElementType; -import org.gephi.streaming.api.event.EventType; -import org.gephi.streaming.api.event.GraphEvent; /** * An event builder @@ -58,10 +53,10 @@ Development and Distribution License("CDDL") (collectively, the */ public class GraphEventBuilder { - private Object source; + private final Object source; /** - * @param container + * @param source */ public GraphEventBuilder(Object source) { this.source = source; From a80c3ea1c3996c168e449e6cca0856d6ad420037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Panisson?= Date: Tue, 16 Feb 2016 22:29:00 +0100 Subject: [PATCH 30/30] Update Gephi version to 0.9.1 --- modules/DesktopStreaming/pom.xml | 2 +- modules/GraphStreaming/pom.xml | 2 +- modules/JettyWrapper/pom.xml | 2 +- modules/StreamingAPI/pom.xml | 2 +- modules/StreamingImpl/pom.xml | 2 +- modules/StreamingServer/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/DesktopStreaming/pom.xml b/modules/DesktopStreaming/pom.xml index 118e12fb24..8432a89b6c 100644 --- a/modules/DesktopStreaming/pom.xml +++ b/modules/DesktopStreaming/pom.xml @@ -4,7 +4,7 @@ gephi-plugin-parent org.gephi - 0.9.0 + 0.9.1 org.gephi diff --git a/modules/GraphStreaming/pom.xml b/modules/GraphStreaming/pom.xml index 52fbfb2be7..bb04356b29 100644 --- a/modules/GraphStreaming/pom.xml +++ b/modules/GraphStreaming/pom.xml @@ -4,7 +4,7 @@ gephi-plugin-parent org.gephi - 0.9.0 + 0.9.1 org.gephi diff --git a/modules/JettyWrapper/pom.xml b/modules/JettyWrapper/pom.xml index ef8d8f3c63..3e7e2c459d 100644 --- a/modules/JettyWrapper/pom.xml +++ b/modules/JettyWrapper/pom.xml @@ -4,7 +4,7 @@ gephi-plugin-parent org.gephi - 0.9.0 + 0.9.1 org.gephi diff --git a/modules/StreamingAPI/pom.xml b/modules/StreamingAPI/pom.xml index f88964b193..00d29cf4a3 100644 --- a/modules/StreamingAPI/pom.xml +++ b/modules/StreamingAPI/pom.xml @@ -4,7 +4,7 @@ gephi-plugin-parent org.gephi - 0.9.0 + 0.9.1 org.gephi diff --git a/modules/StreamingImpl/pom.xml b/modules/StreamingImpl/pom.xml index e8a610a716..18341a7c6b 100644 --- a/modules/StreamingImpl/pom.xml +++ b/modules/StreamingImpl/pom.xml @@ -4,7 +4,7 @@ gephi-plugin-parent org.gephi - 0.9.0 + 0.9.1 org.gephi diff --git a/modules/StreamingServer/pom.xml b/modules/StreamingServer/pom.xml index de9379d27a..1cd2a155ac 100644 --- a/modules/StreamingServer/pom.xml +++ b/modules/StreamingServer/pom.xml @@ -4,7 +4,7 @@ gephi-plugin-parent org.gephi - 0.9.0 + 0.9.1 org.gephi