]> gerrit.simantics Code Review - simantics/r.git/commitdiff
Share project "org.simantics.r.feature" into "https://www.simantics.org/svn/simantics"
authorlehtonen <lehtonen@ac1ea38d-2e2b-0410-8846-a27921b304fc>
Mon, 11 Jan 2016 11:00:37 +0000 (11:00 +0000)
committerlehtonen <lehtonen@ac1ea38d-2e2b-0410-8846-a27921b304fc>
Mon, 11 Jan 2016 11:00:37 +0000 (11:00 +0000)
Share project "org.simantics.r" into "https://www.simantics.org/svn/simantics"
Share project "org.simantics.r.ontology" into "https://www.simantics.org/svn/simantics"
Share project "org.simantics.r.scl" into "https://www.simantics.org/svn/simantics"
Share project "org.simantics.r.build.feature" into "https://www.simantics.org/svn/simantics"

refs #6246

git-svn-id: https://www.simantics.org/svn/simantics/r/trunk@32249 ac1ea38d-2e2b-0410-8846-a27921b304fc

95 files changed:
org.simantics.r.build.feature/.credentials.properties [new file with mode: 0644]
org.simantics.r.build.feature/.project [new file with mode: 0644]
org.simantics.r.build.feature/buckminster.properties [new file with mode: 0644]
org.simantics.r.build.feature/build.properties [new file with mode: 0644]
org.simantics.r.build.feature/feature.xml [new file with mode: 0644]
org.simantics.r.build.feature/notes.txt [new file with mode: 0644]
org.simantics.r.build.feature/r.cquery [new file with mode: 0644]
org.simantics.r.build.feature/r.rmap [new file with mode: 0644]
org.simantics.r.feature/.project [new file with mode: 0644]
org.simantics.r.feature/build.properties [new file with mode: 0644]
org.simantics.r.feature/feature.xml [new file with mode: 0644]
org.simantics.r.ontology/.classpath [new file with mode: 0644]
org.simantics.r.ontology/.project [new file with mode: 0644]
org.simantics.r.ontology/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
org.simantics.r.ontology/META-INF/MANIFEST.MF [new file with mode: 0644]
org.simantics.r.ontology/build.properties [new file with mode: 0644]
org.simantics.r.ontology/graph.tg [new file with mode: 0644]
org.simantics.r.ontology/graph/R.pgraph [new file with mode: 0644]
org.simantics.r.ontology/src/org/simantics/r/RResource.java [new file with mode: 0644]
org.simantics.r.scl/.classpath [new file with mode: 0644]
org.simantics.r.scl/.project [new file with mode: 0644]
org.simantics.r.scl/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
org.simantics.r.scl/.settings/org.eclipse.pde.core.prefs [new file with mode: 0644]
org.simantics.r.scl/META-INF/MANIFEST.MF [new file with mode: 0644]
org.simantics.r.scl/build.properties [new file with mode: 0644]
org.simantics.r.scl/scl/R/R.scl [new file with mode: 0644]
org.simantics.r.scl/scl/R/RExp.scl [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/LICENSE [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/MutableREXP.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXP.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPDouble.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPEnvironment.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPExpressionVector.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPFactor.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPGenericVector.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPInteger.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPJavaReference.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPLanguage.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPList.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPLogical.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPMismatchException.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPNull.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPRaw.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPReference.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPS4.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPString.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPSymbol.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPUnknown.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPVector.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REXPWrapper.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REngine.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REngineCallbacks.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REngineConsoleHistoryInterface.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REngineEvalException.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REngineException.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REngineInputInterface.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REngineOutputInterface.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REngineStdOutput.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/REngineUIInterface.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/RFactor.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/RList.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/Rserve/Makefile [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/Rserve/RConnection.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/Rserve/RFileInputStream.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/Rserve/RFileOutputStream.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/Rserve/RSession.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/Rserve/Rserve.jar [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/Rserve/RserveException.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/Rserve/package-info.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/Rserve/protocol/REXPFactory.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/Rserve/protocol/RPacket.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/Rserve/protocol/RTalk.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/Rserve/protocol/jcrypt.java [new file with mode: 0644]
org.simantics.r.scl/src/org/rosuda/REngine/package-info.java [new file with mode: 0644]
org.simantics.r.scl/src/org/simantics/r/scl/RSession.java [new file with mode: 0644]
org.simantics.r.scl/src/org/simantics/r/scl/RSessionConfiguration.java [new file with mode: 0644]
org.simantics.r.scl/src/org/simantics/r/scl/RSessionManager.java [new file with mode: 0644]
org.simantics.r.scl/src/org/simantics/r/scl/TestRServe.java [new file with mode: 0644]
org.simantics.r.scl/src/org/simantics/r/scl/variable/RAttributeNode.java [new file with mode: 0644]
org.simantics.r.scl/src/org/simantics/r/scl/variable/RDataboardConversion.java [new file with mode: 0644]
org.simantics.r.scl/src/org/simantics/r/scl/variable/RGlobalVariableNode.java [new file with mode: 0644]
org.simantics.r.scl/src/org/simantics/r/scl/variable/RListItemNode.java [new file with mode: 0644]
org.simantics.r.scl/src/org/simantics/r/scl/variable/RListLengthNode.java [new file with mode: 0644]
org.simantics.r.scl/src/org/simantics/r/scl/variable/RNamedItemNode.java [new file with mode: 0644]
org.simantics.r.scl/src/org/simantics/r/scl/variable/RNodeManager.java [new file with mode: 0644]
org.simantics.r.scl/src/org/simantics/r/scl/variable/RVariableNode.java [new file with mode: 0644]
org.simantics.r/.classpath [new file with mode: 0644]
org.simantics.r/.project [new file with mode: 0644]
org.simantics.r/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
org.simantics.r/META-INF/MANIFEST.MF [new file with mode: 0644]
org.simantics.r/adapters.xml [new file with mode: 0644]
org.simantics.r/build.properties [new file with mode: 0644]
org.simantics.r/scl/R/RConfiguration.scl [new file with mode: 0644]
org.simantics.r/scl/R/TestRConfiguration.scl [new file with mode: 0644]
org.simantics.r/src/org/simantics/r/RVariableBuilder.java [new file with mode: 0644]

diff --git a/org.simantics.r.build.feature/.credentials.properties b/org.simantics.r.build.feature/.credentials.properties
new file mode 100644 (file)
index 0000000..086b0fb
--- /dev/null
@@ -0,0 +1,16 @@
+###############################################################################\r
+# Copyright (c) 2013, 2014 Association for Decentralized \r
+# Information Management in Industry THTH ry.\r
+# All rights reserved. This program and the accompanying materials\r
+# are made available under the terms of the THTH Simantics \r
+# Division Member Component License which accompanies this \r
+# distribution, and is available at\r
+# http://www.simantics.org/legal/sdmcl-v10.html\r
+#\r
+# Contributors:\r
+#     Semantum Oy - initial API and implementation\r
+###############################################################################\r
+r.user=jenkins.members\r
+r.password=ochoh5Th\r
+simantics.user=anonymous\r
+simantics.password=\r
diff --git a/org.simantics.r.build.feature/.project b/org.simantics.r.build.feature/.project
new file mode 100644 (file)
index 0000000..3a9df7f
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+       <name>org.simantics.r.build.feature</name>\r
+       <comment></comment>\r
+       <projects>\r
+       </projects>\r
+       <buildSpec>\r
+               <buildCommand>\r
+                       <name>org.eclipse.pde.FeatureBuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+       </buildSpec>\r
+       <natures>\r
+               <nature>org.eclipse.pde.FeatureNature</nature>\r
+       </natures>\r
+</projectDescription>\r
diff --git a/org.simantics.r.build.feature/buckminster.properties b/org.simantics.r.build.feature/buckminster.properties
new file mode 100644 (file)
index 0000000..bdc7044
--- /dev/null
@@ -0,0 +1,31 @@
+###############################################################################
+# Copyright (c) 2013, 2014 Association for Decentralized 
+# Information Management in Industry THTH ry.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the THTH Simantics 
+# Division Member Component License which accompanies this 
+# distribution, and is available at
+# http://www.simantics.org/legal/sdmcl-v10.html
+#
+# Contributors:
+#     Semantum Oy - initial API and implementation
+###############################################################################
+# How .qualifier in versions should be replaced
+qualifier.replacement.*=generator:lastRevision
+generator.lastRevision.format=r{0,number,000000}
+
+# Pack200 compression
+#site.pack200=true
+#site.retain.unpack=true
+
+# JAR Signing
+#site.signing=true
+#signing.type=local
+#local.sign=true
+
+# Generate source bundles
+cbi.include.source=false
+
+target.os=win32
+target.ws=win32
+target.arch=*
diff --git a/org.simantics.r.build.feature/build.properties b/org.simantics.r.build.feature/build.properties
new file mode 100644 (file)
index 0000000..e050c88
--- /dev/null
@@ -0,0 +1,17 @@
+###############################################################################\r
+# Copyright (c) 2013, 2014 Association for Decentralized \r
+# Information Management in Industry THTH ry.\r
+# All rights reserved. This program and the accompanying materials\r
+# are made available under the terms of the THTH Simantics \r
+# Division Member Component License which accompanies this \r
+# distribution, and is available at\r
+# http://www.simantics.org/legal/sdmcl-v10.html\r
+#\r
+# Contributors:\r
+#     Semantum Oy - initial API and implementation\r
+###############################################################################\r
+bin.includes = feature.xml\r
+\r
+category.id.org.simantics.epl=Public Simantics Components\r
+category.members.org.simantics.epl=org.simantics.r\r
+category.description.org.simantics.epl=Simantics components freely availably under the EPL license.
\ No newline at end of file
diff --git a/org.simantics.r.build.feature/feature.xml b/org.simantics.r.build.feature/feature.xml
new file mode 100644 (file)
index 0000000..d95e257
--- /dev/null
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!--
+    Copyright (c) 2014, 2016 Association for Decentralized Information\r
+    Management in Industry THTH ry.\r
+    All rights reserved. This program and the accompanying materials\r
+    are made available under the terms of the Eclipse Public License\r
+    v1.0 which accompanies this distribution, and is available at\r
+    http://www.eclipse.org/legal/epl-v10.html\r
+    \r
+    Contributors:\r
+        Semantum Oy - initial API and implementation\r
+ -->\r
+<feature\r
+      id="org.simantics.r.build"\r
+      label="Simantics R Build Site Feature"\r
+      version="1.0.0.qualifier"\r
+      provider-name="Semantum Oy">\r
+\r
+   <description url="http://www.simantics.org">\r
+      R build site feature.\r
+   </description>\r
+\r
+   <copyright>\r
+      Copyright (c) 2014, 2016 Association for Decentralized Information
+Management in Industry THTH ry.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License
+v1.0 which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/epl-v10.html
+Contributors:
+Semantum Oy - initial API and implementation\r
+   </copyright>\r
+\r
+   <license url="https://www.eclipse.org/legal/epl-v10.html">\r
+      Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE (&quot;AGREEMENT&quot;). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT&apos;S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+&quot;Contribution&quot; means:
+
+a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
+
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution &apos;originates&apos; from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor&apos;s behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
+
+&quot;Contributor&quot; means any person or entity that distributes the Program.
+
+&quot;Licensed Patents&quot; mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
+
+&quot;Program&quot; means the Contributions distributed in accordance with this Agreement.
+
+&quot;Recipient&quot; means anyone who receives the Program under this Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient&apos;s responsibility to acquire that license before distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
+
+iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained within the Program.
+
+Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor (&quot;Commercial Contributor&quot;) hereby agrees to defend and indemnify every other Contributor (&quot;Indemnified Contributor&quot;) against any losses, damages and costs (collectively &quot;Losses&quot;) arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor&apos;s responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient&apos;s patent(s), then such Recipient&apos;s rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient&apos;s rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient&apos;s rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient&apos;s obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.\r
+   </license>\r
+\r
+   <includes\r
+         id="org.simantics.r"\r
+         version="0.0.0"/>\r
+\r
+</feature>\r
diff --git a/org.simantics.r.build.feature/notes.txt b/org.simantics.r.build.feature/notes.txt
new file mode 100644 (file)
index 0000000..6c62dca
--- /dev/null
@@ -0,0 +1 @@
+This feature is used for building org.simantics.r.feature, since buckminster command site.p2.zip doesn't include the feature it-self.\r
diff --git a/org.simantics.r.build.feature/r.cquery b/org.simantics.r.build.feature/r.cquery
new file mode 100644 (file)
index 0000000..76df6d3
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<cq:componentQuery xmlns:cq="http://www.eclipse.org/buckminster/CQuery-1.0" resourceMap="r.rmap" properties=".credentials.properties">\r
+    <cq:rootRequest name="org.simantics.r.build" componentType="eclipse.feature"/>\r
+    <cq:property key="target.arch" value="*"/>\r
+    <cq:property key="target.os" value="*"/>\r
+    <cq:property key="target.ws" value="*"/>\r
+    <cq:advisorNode namePattern=".*\.source" componentType="osgi.bundle" skipComponent="true"/>\r
+    <cq:advisorNode namePattern=".*\.source" componentType="eclipse.feature" skipComponent="true"/>\r
+    <cq:advisorNode namePattern=".*" branchTagPath="main"/>\r
+</cq:componentQuery>
\ No newline at end of file
diff --git a/org.simantics.r.build.feature/r.rmap b/org.simantics.r.build.feature/r.rmap
new file mode 100644 (file)
index 0000000..7cb6260
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<rmap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.eclipse.org/buckminster/RMap-1.0"\r
+       xmlns:bc="http://www.eclipse.org/buckminster/Common-1.0" xmlns:pv="http://www.eclipse.org/buckminster/Provider-1.0">\r
+\r
+       <property key="MEMBERS"\r
+               value="https://{1}:{2}@www.simantics.org/svn/simantics-incubator/hannu" />\r
+\r
+       <searchPath name="r">\r
+               <provider readerType="svn" componentTypes="eclipse.feature" mutable="true" source="true">\r
+                       <uri format="${MEMBERS}/{0}.feature">\r
+                               <bc:propertyRef key="buckminster.component" />\r
+                               <bc:propertyRef key="r.user" />\r
+                               <bc:propertyRef key="r.password" />\r
+                       </uri>\r
+               </provider>\r
+               <provider readerType="svn" componentTypes="osgi.bundle" mutable="true" source="true">\r
+                       <uri format="${MEMBERS}/{0}">\r
+                               <bc:propertyRef key="buckminster.component" />\r
+                               <bc:propertyRef key="r.user" />\r
+                               <bc:propertyRef key="r.password" />\r
+                       </uri>\r
+               </provider>\r
+       </searchPath>\r
+\r
+       <locator searchPathRef="r" pattern="^org\.simantics\.r" />\r
+\r
+       <redirect href="http://www.simantics.org/download/head/rmap/Simantics.rmap" /> \r
+\r
+</rmap>
\ No newline at end of file
diff --git a/org.simantics.r.feature/.project b/org.simantics.r.feature/.project
new file mode 100644 (file)
index 0000000..08b2fc9
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+       <name>org.simantics.r.feature</name>\r
+       <comment></comment>\r
+       <projects>\r
+       </projects>\r
+       <buildSpec>\r
+               <buildCommand>\r
+                       <name>org.eclipse.pde.FeatureBuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+       </buildSpec>\r
+       <natures>\r
+               <nature>org.eclipse.pde.FeatureNature</nature>\r
+       </natures>\r
+</projectDescription>\r
diff --git a/org.simantics.r.feature/build.properties b/org.simantics.r.feature/build.properties
new file mode 100644 (file)
index 0000000..82ab19c
--- /dev/null
@@ -0,0 +1 @@
+bin.includes = feature.xml\r
diff --git a/org.simantics.r.feature/feature.xml b/org.simantics.r.feature/feature.xml
new file mode 100644 (file)
index 0000000..73f3997
--- /dev/null
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<feature\r
+      id="org.simantics.r"\r
+      label="R Project support for Simantics"\r
+      version="0.1.0.qualifier"\r
+      provider-name="Association for Decentralized Information Management in Industry THTH ry">\r
+\r
+   <description>\r
+      SCL tooling for Simantics to enable the use of R statistical computing environment with Simantics through RServe.\r
+Allows R code evaluation and reading the R data model through the Simantics Variable interface.\r
+   </description>\r
+\r
+   <copyright url="http://www.example.com/copyright">\r
+      Copyright (c) 2014, 2016 Association for Decentralized Information
+Management in Industry THTH ry.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License
+v1.0 which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/epl-v10.html
+Contributors:
+VTT Technical Research Centre of Finland - initial API and implementation\r
+   </copyright>\r
+\r
+   <license url="https://www.eclipse.org/legal/epl-v10.html">\r
+      Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE (&quot;AGREEMENT&quot;). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT&apos;S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+&quot;Contribution&quot; means:
+
+a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
+
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution &apos;originates&apos; from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor&apos;s behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
+
+&quot;Contributor&quot; means any person or entity that distributes the Program.
+
+&quot;Licensed Patents&quot; mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
+
+&quot;Program&quot; means the Contributions distributed in accordance with this Agreement.
+
+&quot;Recipient&quot; means anyone who receives the Program under this Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient&apos;s responsibility to acquire that license before distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
+
+iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained within the Program.
+
+Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor (&quot;Commercial Contributor&quot;) hereby agrees to defend and indemnify every other Contributor (&quot;Indemnified Contributor&quot;) against any losses, damages and costs (collectively &quot;Losses&quot;) arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor&apos;s responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient&apos;s patent(s), then such Recipient&apos;s rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient&apos;s rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient&apos;s rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient&apos;s obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.\r
+   </license>\r
+\r
+   <plugin\r
+         id="org.simantics.r"\r
+         download-size="0"\r
+         install-size="0"\r
+         version="0.0.0"\r
+         unpack="false"/>\r
+\r
+   <plugin\r
+         id="org.simantics.r.ontology"\r
+         download-size="0"\r
+         install-size="0"\r
+         version="0.0.0"\r
+         unpack="false"/>\r
+\r
+   <plugin\r
+         id="org.simantics.r.scl"\r
+         download-size="0"\r
+         install-size="0"\r
+         version="0.0.0"\r
+         unpack="false"/>\r
+\r
+</feature>\r
diff --git a/org.simantics.r.ontology/.classpath b/org.simantics.r.ontology/.classpath
new file mode 100644 (file)
index 0000000..b1dabee
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>\r
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>\r
+       <classpathentry kind="src" path="src"/>\r
+       <classpathentry kind="output" path="bin"/>\r
+</classpath>\r
diff --git a/org.simantics.r.ontology/.project b/org.simantics.r.ontology/.project
new file mode 100644 (file)
index 0000000..4e80501
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+       <name>org.simantics.r.ontology</name>\r
+       <comment></comment>\r
+       <projects>\r
+       </projects>\r
+       <buildSpec>\r
+               <buildCommand>\r
+                       <name>org.simantics.graph.builder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+               <buildCommand>\r
+                       <name>org.eclipse.jdt.core.javabuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+               <buildCommand>\r
+                       <name>org.eclipse.pde.ManifestBuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+               <buildCommand>\r
+                       <name>org.eclipse.pde.SchemaBuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+       </buildSpec>\r
+       <natures>\r
+               <nature>org.eclipse.pde.PluginNature</nature>\r
+               <nature>org.eclipse.jdt.core.javanature</nature>\r
+               <nature>org.simantics.graph.nature</nature>\r
+       </natures>\r
+</projectDescription>\r
diff --git a/org.simantics.r.ontology/.settings/org.eclipse.jdt.core.prefs b/org.simantics.r.ontology/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..11f6e46
--- /dev/null
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1\r
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\r
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7\r
+org.eclipse.jdt.core.compiler.compliance=1.7\r
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error\r
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error\r
+org.eclipse.jdt.core.compiler.source=1.7\r
diff --git a/org.simantics.r.ontology/META-INF/MANIFEST.MF b/org.simantics.r.ontology/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..2b5576d
--- /dev/null
@@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: http://www.simantics.org/R
+Bundle-SymbolicName: org.simantics.r.ontology
+Bundle-Version: 1.0.0.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Require-Bundle: org.simantics.layer0;bundle-version="1.1.0",
+ org.simantics.simulation.ontology;bundle-version="1.1.0",
+ org.simantics.selectionview.ontology;bundle-version="1.2.0",
+ org.simantics.selectionview.ui.ontology;bundle-version="1.1.0"
+Export-Package: org.simantics.r
+Bundle-Vendor: Association for Decentralized Information Management in Industry THTH ry
diff --git a/org.simantics.r.ontology/build.properties b/org.simantics.r.ontology/build.properties
new file mode 100644 (file)
index 0000000..6c97998
--- /dev/null
@@ -0,0 +1,6 @@
+source.. = src/\r
+output.. = bin/\r
+bin.includes = META-INF/,\\r
+               .,\\r
+               graph/,\\r
+               graph.tg\r
diff --git a/org.simantics.r.ontology/graph.tg b/org.simantics.r.ontology/graph.tg
new file mode 100644 (file)
index 0000000..132a3d2
Binary files /dev/null and b/org.simantics.r.ontology/graph.tg differ
diff --git a/org.simantics.r.ontology/graph/R.pgraph b/org.simantics.r.ontology/graph/R.pgraph
new file mode 100644 (file)
index 0000000..cd441bc
--- /dev/null
@@ -0,0 +1,42 @@
+L0 = <http://www.simantics.org/Layer0-1.1>\r
+SIMU = <http://www.simantics.org/Simulation-1.1>\r
+SEL = <http://www.simantics.org/SelectionView-1.2>\r
+SEL_UI = <http://www.simantics.org/SelectionViewUI-1.1>\r
+\r
+R = <http://www.simantics.org/R-1.0> : L0.Ontology\r
+    @L0.new\r
+    L0.HasResourceClass "org.simantics.r.RResource"\r
+\r
+R.SessionConfiguration <T SIMU.Experiment\r
+    >-- R.SessionConfiguration.host --> L0.String <R L0.HasProperty : L0.TotalFunction\r
+    >-- R.SessionConfiguration.port --> L0.Integer <R L0.HasProperty : L0.TotalFunction\r
+    >-- R.SessionConfiguration.username --> L0.String <R L0.HasProperty : L0.TotalFunction\r
+    >-- R.SessionConfiguration.password --> L0.String <R L0.HasProperty : L0.TotalFunction\r
+    >-- R.SessionConfiguration.hasScript --> R.Script <R L0.IsRelatedTo\r
+    \r
+    @L0.assert R.SessionConfiguration.host "localhost"\r
+    @L0.assert R.SessionConfiguration.port 6311\r
+    @L0.assert R.SessionConfiguration.username ""\r
+    @L0.assert R.SessionConfiguration.password ""\r
+\r
+R.TabContribution : SEL.TypedVariableTabContribution\r
+    SEL.TypedVariableTabContribution.HasType R.Session\r
+    SEL.VariableTabContribution.HasView SEL_UI.StandardProperties\r
+    SEL.VariableTabContribution.HasPriority 1\r
+    L0.HasLabel "Variables"\r
+    \r
+R.Session <T SIMU.Run\r
+    >-- L0.PartOf --> R.SessionConfiguration\r
+    >-- R.Session.hasValue --> L0.Value <R L0.HasProperty : SEL.GenericParameterType\r
+        //SEL.HasDisplayValue R.Session.hasValueDisplayValue : L0.Function\r
+    \r
+    @L0.assert L0.HasLabel "R session"\r
+    \r
+//    @L0.assert L0.domainProperties \r
+//        R.Session.rVariables : L0.ExternalValue\r
+//            L0.HasValueType "VariableMap"\r
+\r
+R.Script <T L0.Entity\r
+    >-- R.Script.text --> L0.String <R L0.HasProperty : L0.TotalFunction\r
+    \r
+    @L0.assert R.Script.text ""
\ No newline at end of file
diff --git a/org.simantics.r.ontology/src/org/simantics/r/RResource.java b/org.simantics.r.ontology/src/org/simantics/r/RResource.java
new file mode 100644 (file)
index 0000000..441cd10
--- /dev/null
@@ -0,0 +1,106 @@
+package org.simantics.r;\r
+\r
+import org.simantics.db.RequestProcessor;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.request.Read;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.service.QueryControl;\r
+\r
+public class RResource {\r
+    \r
+    public final Resource Script;\r
+    public final Resource Script_text;\r
+    public final Resource Script_text_Inverse;\r
+    public final Resource Session;\r
+    public final Resource SessionConfiguration;\r
+    public final Resource SessionConfiguration_hasScript;\r
+    public final Resource SessionConfiguration_host;\r
+    public final Resource SessionConfiguration_host_Inverse;\r
+    public final Resource SessionConfiguration_password;\r
+    public final Resource SessionConfiguration_password_Inverse;\r
+    public final Resource SessionConfiguration_port;\r
+    public final Resource SessionConfiguration_port_Inverse;\r
+    public final Resource SessionConfiguration_username;\r
+    public final Resource SessionConfiguration_username_Inverse;\r
+    public final Resource Session_hasValue;\r
+    public final Resource Session_hasValue_Inverse;\r
+    public final Resource TabContribution;\r
+        \r
+    public static class URIs {\r
+        public static final String Script = "http://www.simantics.org/R-1.0/Script";\r
+        public static final String Script_text = "http://www.simantics.org/R-1.0/Script/text";\r
+        public static final String Script_text_Inverse = "http://www.simantics.org/R-1.0/Script/text/Inverse";\r
+        public static final String Session = "http://www.simantics.org/R-1.0/Session";\r
+        public static final String SessionConfiguration = "http://www.simantics.org/R-1.0/SessionConfiguration";\r
+        public static final String SessionConfiguration_hasScript = "http://www.simantics.org/R-1.0/SessionConfiguration/hasScript";\r
+        public static final String SessionConfiguration_host = "http://www.simantics.org/R-1.0/SessionConfiguration/host";\r
+        public static final String SessionConfiguration_host_Inverse = "http://www.simantics.org/R-1.0/SessionConfiguration/host/Inverse";\r
+        public static final String SessionConfiguration_password = "http://www.simantics.org/R-1.0/SessionConfiguration/password";\r
+        public static final String SessionConfiguration_password_Inverse = "http://www.simantics.org/R-1.0/SessionConfiguration/password/Inverse";\r
+        public static final String SessionConfiguration_port = "http://www.simantics.org/R-1.0/SessionConfiguration/port";\r
+        public static final String SessionConfiguration_port_Inverse = "http://www.simantics.org/R-1.0/SessionConfiguration/port/Inverse";\r
+        public static final String SessionConfiguration_username = "http://www.simantics.org/R-1.0/SessionConfiguration/username";\r
+        public static final String SessionConfiguration_username_Inverse = "http://www.simantics.org/R-1.0/SessionConfiguration/username/Inverse";\r
+        public static final String Session_hasValue = "http://www.simantics.org/R-1.0/Session/hasValue";\r
+        public static final String Session_hasValue_Inverse = "http://www.simantics.org/R-1.0/Session/hasValue/Inverse";\r
+        public static final String TabContribution = "http://www.simantics.org/R-1.0/TabContribution";\r
+    }\r
+    \r
+    public static Resource getResourceOrNull(ReadGraph graph, String uri) {\r
+        try {\r
+            return graph.getResource(uri);\r
+        } catch(DatabaseException e) {\r
+            System.err.println(e.getMessage());\r
+            return null;\r
+        }\r
+    }\r
+    \r
+    public RResource(ReadGraph graph) {\r
+        Script = getResourceOrNull(graph, URIs.Script);\r
+        Script_text = getResourceOrNull(graph, URIs.Script_text);\r
+        Script_text_Inverse = getResourceOrNull(graph, URIs.Script_text_Inverse);\r
+        Session = getResourceOrNull(graph, URIs.Session);\r
+        SessionConfiguration = getResourceOrNull(graph, URIs.SessionConfiguration);\r
+        SessionConfiguration_hasScript = getResourceOrNull(graph, URIs.SessionConfiguration_hasScript);\r
+        SessionConfiguration_host = getResourceOrNull(graph, URIs.SessionConfiguration_host);\r
+        SessionConfiguration_host_Inverse = getResourceOrNull(graph, URIs.SessionConfiguration_host_Inverse);\r
+        SessionConfiguration_password = getResourceOrNull(graph, URIs.SessionConfiguration_password);\r
+        SessionConfiguration_password_Inverse = getResourceOrNull(graph, URIs.SessionConfiguration_password_Inverse);\r
+        SessionConfiguration_port = getResourceOrNull(graph, URIs.SessionConfiguration_port);\r
+        SessionConfiguration_port_Inverse = getResourceOrNull(graph, URIs.SessionConfiguration_port_Inverse);\r
+        SessionConfiguration_username = getResourceOrNull(graph, URIs.SessionConfiguration_username);\r
+        SessionConfiguration_username_Inverse = getResourceOrNull(graph, URIs.SessionConfiguration_username_Inverse);\r
+        Session_hasValue = getResourceOrNull(graph, URIs.Session_hasValue);\r
+        Session_hasValue_Inverse = getResourceOrNull(graph, URIs.Session_hasValue_Inverse);\r
+        TabContribution = getResourceOrNull(graph, URIs.TabContribution);\r
+    }\r
+    \r
+    public static RResource getInstance(ReadGraph graph) {\r
+        Session session = graph.getSession();\r
+        RResource ret = session.peekService(RResource.class);\r
+        if(ret == null) {\r
+            QueryControl qc = graph.getService(QueryControl.class);\r
+            ret = new RResource(qc.getIndependentGraph(graph));\r
+            session.registerService(RResource.class, ret);\r
+        }\r
+        return ret;\r
+    }\r
+    \r
+    public static RResource getInstance(RequestProcessor session) throws DatabaseException {\r
+        RResource ret = session.peekService(RResource.class);\r
+        if(ret == null) {\r
+            ret = session.syncRequest(new Read<RResource>() {\r
+                public RResource perform(ReadGraph graph) throws DatabaseException {\r
+                    QueryControl qc = graph.getService(QueryControl.class);\r
+                    return new RResource(qc.getIndependentGraph(graph));\r
+                }\r
+            });\r
+            session.registerService(RResource.class, ret);\r
+        }\r
+        return ret;\r
+    }\r
+    \r
+}\r
+\r
diff --git a/org.simantics.r.scl/.classpath b/org.simantics.r.scl/.classpath
new file mode 100644 (file)
index 0000000..b1dabee
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>\r
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>\r
+       <classpathentry kind="src" path="src"/>\r
+       <classpathentry kind="output" path="bin"/>\r
+</classpath>\r
diff --git a/org.simantics.r.scl/.project b/org.simantics.r.scl/.project
new file mode 100644 (file)
index 0000000..a235424
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+       <name>org.simantics.r.scl</name>\r
+       <comment></comment>\r
+       <projects>\r
+       </projects>\r
+       <buildSpec>\r
+               <buildCommand>\r
+                       <name>org.eclipse.jdt.core.javabuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+               <buildCommand>\r
+                       <name>org.eclipse.pde.ManifestBuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+               <buildCommand>\r
+                       <name>org.eclipse.pde.SchemaBuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+       </buildSpec>\r
+       <natures>\r
+               <nature>org.eclipse.pde.PluginNature</nature>\r
+               <nature>org.eclipse.jdt.core.javanature</nature>\r
+       </natures>\r
+</projectDescription>\r
diff --git a/org.simantics.r.scl/.settings/org.eclipse.jdt.core.prefs b/org.simantics.r.scl/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..467b8c4
--- /dev/null
@@ -0,0 +1,95 @@
+eclipse.preferences.version=1\r
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled\r
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore\r
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull\r
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault\r
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable\r
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled\r
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\r
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7\r
+org.eclipse.jdt.core.compiler.compliance=1.7\r
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning\r
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error\r
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore\r
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning\r
+org.eclipse.jdt.core.compiler.problem.deadCode=warning\r
+org.eclipse.jdt.core.compiler.problem.deprecation=warning\r
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled\r
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled\r
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning\r
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore\r
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error\r
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore\r
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore\r
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled\r
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore\r
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning\r
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning\r
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error\r
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning\r
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled\r
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning\r
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning\r
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore\r
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore\r
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning\r
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore\r
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore\r
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled\r
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore\r
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore\r
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled\r
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning\r
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore\r
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning\r
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning\r
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore\r
+org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning\r
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error\r
+org.eclipse.jdt.core.compiler.problem.nullReference=warning\r
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error\r
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning\r
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning\r
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore\r
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore\r
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore\r
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore\r
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore\r
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning\r
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore\r
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore\r
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore\r
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore\r
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore\r
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled\r
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning\r
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled\r
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled\r
+org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled\r
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore\r
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning\r
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled\r
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=ignore\r
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning\r
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore\r
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning\r
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore\r
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore\r
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore\r
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore\r
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled\r
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled\r
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled\r
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning\r
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning\r
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning\r
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore\r
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore\r
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled\r
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled\r
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled\r
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning\r
+org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore\r
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning\r
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning\r
+org.eclipse.jdt.core.compiler.source=1.7\r
diff --git a/org.simantics.r.scl/.settings/org.eclipse.pde.core.prefs b/org.simantics.r.scl/.settings/org.eclipse.pde.core.prefs
new file mode 100644 (file)
index 0000000..b1bb1fc
--- /dev/null
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1\r
+pluginProject.extensions=true\r
+resolve.requirebundle=false\r
diff --git a/org.simantics.r.scl/META-INF/MANIFEST.MF b/org.simantics.r.scl/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..e483735
--- /dev/null
@@ -0,0 +1,17 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: RServe connectivity
+Bundle-SymbolicName: org.simantics.r.scl;singleton:=true
+Bundle-Version: 0.1.0.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Require-Bundle: org.simantics.scl.runtime;bundle-version="0.4.0",
+ gnu.trove3;bundle-version="3.0.3",
+ org.simantics.simulator.variable;bundle-version="1.0.0",
+ org.simantics.structural.ontology,
+ org.simantics.utils.datastructures;bundle-version="1.1.0"
+Export-Package: org.rosuda.REngine,
+ org.rosuda.REngine.Rserve,
+ org.rosuda.REngine.Rserve.protocol,
+ org.simantics.r.scl,
+ org.simantics.r.scl.variable
+Bundle-Vendor: Association for Decentralized Information Management in Industry THTH ry
diff --git a/org.simantics.r.scl/build.properties b/org.simantics.r.scl/build.properties
new file mode 100644 (file)
index 0000000..6092d89
--- /dev/null
@@ -0,0 +1,5 @@
+source.. = src/\r
+output.. = bin/\r
+bin.includes = META-INF/,\\r
+               .,\\r
+               scl/\r
diff --git a/org.simantics.r.scl/scl/R/R.scl b/org.simantics.r.scl/scl/R/R.scl
new file mode 100644 (file)
index 0000000..ef82084
--- /dev/null
@@ -0,0 +1,59 @@
+import "Stream"\r
+import "Serialization"\r
+include "R/RExp"\r
+\r
+effect R \r
+    "r" \r
+    "org.rosuda.REngine.Rserve.RConnection"\r
+\r
+importJava "org.simantics.r.scl.RSessionConfiguration" where\r
+    data SessionConfiguration = \r
+        @FieldNames [host, port, username, password]\r
+        SessionConfiguration String Integer String String\r
+\r
+importJava "org.simantics.r.scl.RSession" where\r
+    data Session\r
+    syncExec :: Session -> (<R> a) -> <Proc> a\r
+    asyncExec :: Session -> (<R> a) -> <Proc> ()\r
+    @JavaName getId\r
+    sessionIdOf :: Session -> String\r
+    @JavaName close\r
+    closeSession :: Session -> <Proc> ()  \r
+    @JavaName refreshVariablesSync\r
+    refreshVariables :: Session -> <Proc> ()\r
+\r
+importJava "org.simantics.r.scl.RSessionManager" where\r
+    @JavaName getSession\r
+    sessionById :: String -> <Proc> Maybe Session\r
+    createSession :: SessionConfiguration -> <Proc> Session\r
+    withConfiguration :: SessionConfiguration-> (<R,e> a) -> <Proc,e> a\r
+    getOrCreateSession :: SessionConfiguration -> String -> <Proc> Session\r
+    \r
+importJava "org.rosuda.REngine.Rserve.RConnection" where\r
+    @JavaName eval\r
+    evalRExp:: String -> <R> RExp\r
+    @JavaName voidEval\r
+    evalR_ :: String -> <R> ()\r
+    @JavaName voidEvalS\r
+    evalRS_ :: String -> <R> String\r
+    @JavaName assign\r
+    assignRExp :: String -> RExp -> <R> ()\r
+    @JavaName createFile\r
+    createFileR :: String -> <R> OutputStream\r
+    @JavaName openFile\r
+    openFileR :: String -> <R> InputStream\r
+\r
+evalR :: RCompatible a => String -> <R> a\r
+evalR = fromRExp . evalRExp\r
+\r
+assignR :: RCompatible a => String -> a -> <R> ()\r
+assignR name = assignRExp name . toRExp\r
+\r
+readFileAsStringR :: String -> <R> String\r
+readFileAsStringR name = runProc (readAllString (openFileR name))\r
+\r
+test () = do\r
+    conf = SessionConfiguration "130.188.198.138" 6311 "simupedia" "simupedia"\r
+    session = createSession conf\r
+    print $ syncExec session (evalR "1+1")\r
+    closeSession session
\ No newline at end of file
diff --git a/org.simantics.r.scl/scl/R/RExp.scl b/org.simantics.r.scl/scl/R/RExp.scl
new file mode 100644 (file)
index 0000000..5804b04
--- /dev/null
@@ -0,0 +1,156 @@
+import "Vector"\r
+\r
+@JavaType "org.rosuda.REngine.REXP"\r
+data RExp = \r
+    @JavaType "org.rosuda.REngine.REXPDouble"\r
+    @FieldNames [payload]\r
+    RExpDouble (Vector Double)\r
+  | @JavaType "org.rosuda.REngine.REXPInteger"\r
+    @FieldNames [payload]\r
+    RExpInteger (Vector Integer)\r
+  | @JavaType "org.rosuda.REngine.REXPString"\r
+    @FieldNames [payload]\r
+    RExpString (Vector String)\r
+  | @JavaType "org.rosuda.REngine.REXPGenericVector"\r
+    @FieldNames [payload]\r
+    RExpGenericVector (Vector RExp)\r
+  | @JavaType "org.rosuda.REngine.REXPNull"\r
+    RExpNull\r
+\r
+instance Show RExp where\r
+    sb <+ RExpDouble v = do \r
+        for [0..length v-1] $ \i ->\r
+            ignore $ (if i > 0 then sb << " " else sb) <+ v!i\r
+        sb\r
+    sb <+ RExpInteger v = do \r
+        for [0..length v-1] $ \i ->\r
+            ignore $ (if i > 0 then sb << " " else sb) <+ v!i\r
+        sb   \r
+    sb <+ RExpGenericVector v = do \r
+        for [0..length v-1] $ \i ->\r
+            ignore $ (if i > 0 then sb << " " else sb) <+ v!i\r
+        sb               \r
+    sb <+ RExpNull = sb << "NULL"\r
+    sb <+ _ = sb << "unknown"\r
+    \r
+importJava "org.rosuda.REngine.REXP" where\r
+    @JavaName dim\r
+    dimR :: RExp -> Maybe (Vector Integer)\r
+    \r
+    @JavaName getAttribute\r
+    attributeRExp :: RExp -> String -> Maybe RExp \r
+    \r
+    @private\r
+    @JavaName asDouble\r
+    convertRExpToDouble :: RExp -> Double\r
+    \r
+    @private\r
+    @JavaName asDoubles\r
+    convertRExpToVectorDouble :: RExp -> Vector Double\r
+\r
+    @private\r
+    @JavaName asInteger\r
+    convertRExpToInteger :: RExp -> Integer\r
+    \r
+    @private\r
+    @JavaName asIntegers\r
+    convertRExpToVectorInteger :: RExp -> Vector Integer\r
+\r
+    @private\r
+    @JavaName asString\r
+    convertRExpToString :: RExp -> String\r
+    \r
+    @private\r
+    @JavaName asStrings\r
+    convertRExpToVectorString :: RExp -> Vector String\r
+    \r
+    @private\r
+    @JavaName createDoubleMatrix\r
+    convertDoubleMatrixToRExp :: Vector (Vector Double) -> RExp \r
+\r
+    @private\r
+    @JavaName asDoubleMatrix\r
+    convertRExpToDoubleMatrix :: RExp -> Vector (Vector Double) \r
+        \r
+    @private\r
+    @JavaName asList\r
+    convertRExpToList :: RExp -> [RExp]\r
+\r
+attributeR :: RCompatible a => RExp -> String -> Maybe a\r
+attributeR exp = map fromRExp . attributeRExp exp \r
+    \r
+@private\r
+importJava "org.rosuda.REngine.REXPDouble" where\r
+    @JavaName "<init>"\r
+    convertDoubleToRExp :: Double -> RExp\r
+    \r
+    @JavaName "<init>"\r
+    convertVectorDoubleToRExp :: Vector Double -> RExp\r
+\r
+@private\r
+importJava "org.rosuda.REngine.REXPInteger" where\r
+    @JavaName "<init>"\r
+    convertIntegerToRExp :: Integer -> RExp\r
+    \r
+    @JavaName "<init>"\r
+    convertVectorIntegerToRExp :: Vector Integer -> RExp\r
+\r
+@private\r
+importJava "org.rosuda.REngine.REXPString" where\r
+    @JavaName "<init>"\r
+    convertStringToRExp :: String -> RExp\r
+    \r
+    @JavaName "<init>"\r
+    convertVectorStringToRExp :: Vector String -> RExp\r
+\r
+@private\r
+importJava "org.rosuda.REngine.REXPGenericVector" where\r
+    @JavaName "<init>"\r
+    convertRListToRExp :: RList -> RExp\r
+\r
+@private\r
+importJava "org.rosuda.REngine.RList" where\r
+    data RList\r
+\r
+    @JavaName "<init>"\r
+    createRList :: [RExp] -> RList\r
+\r
+class RCompatible a where\r
+    fromRExp :: RExp -> a\r
+    toRExp :: a -> RExp\r
+\r
+instance RCompatible RExp where\r
+    fromRExp = id\r
+    toRExp = id\r
+\r
+instance RCompatible Double where\r
+    fromRExp = convertRExpToDouble\r
+    toRExp = convertDoubleToRExp\r
+\r
+instance RCompatible (Vector Double) where\r
+    fromRExp = convertRExpToVectorDouble\r
+    toRExp = convertVectorDoubleToRExp\r
+\r
+instance RCompatible Integer where\r
+    fromRExp = convertRExpToInteger\r
+    toRExp = convertIntegerToRExp\r
+\r
+instance RCompatible (Vector Integer) where\r
+    fromRExp = convertRExpToVectorInteger\r
+    toRExp = convertVectorIntegerToRExp\r
+\r
+instance RCompatible String where\r
+    fromRExp = convertRExpToString\r
+    toRExp = convertStringToRExp\r
+\r
+instance RCompatible (Vector String) where\r
+    fromRExp = convertRExpToVectorString\r
+    toRExp = convertVectorStringToRExp\r
+\r
+instance RCompatible (Vector (Vector Double)) where\r
+    fromRExp = convertRExpToDoubleMatrix\r
+    toRExp = convertDoubleMatrixToRExp\r
+\r
+instance (RCompatible a) => RCompatible [a] where\r
+    fromRExp = map fromRExp . convertRExpToList\r
+    toRExp = convertRListToRExp . createRList . map toRExp
\ No newline at end of file
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/LICENSE b/org.simantics.r.scl/src/org/rosuda/REngine/LICENSE
new file mode 100644 (file)
index 0000000..42335b1
--- /dev/null
@@ -0,0 +1,16 @@
+REngine - Java interface to R
+Copyright (C) 2004,5,6,7  Simon Urbanek
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/MutableREXP.java b/org.simantics.r.scl/src/org/rosuda/REngine/MutableREXP.java
new file mode 100644 (file)
index 0000000..8b84a74
--- /dev/null
@@ -0,0 +1,5 @@
+package org.rosuda.REngine;
+
+public interface MutableREXP {
+       public void setAttribute(String name, REXP value);
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXP.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXP.java
new file mode 100644 (file)
index 0000000..78b4c9c
--- /dev/null
@@ -0,0 +1,250 @@
+package org.rosuda.REngine;
+
+/** Basic class representing an object of any type in R. Each type in R in represented by a specific subclass.
+ <p>
+ This class defines basic accessor methods (<tt>as</tt><i>XXX</i>), type check methods (<tt>is</tt><i>XXX</i>), gives access to attributes ({@link #getAttribute}, {@link #hasAttribute}) as well as several convenience methods. If a given method is not applicable to a particular type, it will throw the {@link REXPMismatchException} exception.
+ <p>This root class will throw on any accessor call and returns <code>false</code> for all type methods. This allows subclasses to override accessor and type methods selectively.
+ */
+public class REXP {
+       /** attribute list. This attribute should never be accessed directly. */
+       public REXPList attr;
+
+       /** public root contrsuctor, same as <tt>new REXP(null)</tt> */
+       public REXP() { }
+       /** public root constructor
+        @param attr attribute list object (can be <code>null</code> */
+       public REXP(REXPList attr) { this.attr=attr; }
+
+       // type checks
+       /** check whether the <code>REXP</code> object is a character vector (string)
+        @return <code>true</code> if the receiver is a character vector, <code>false</code> otherwise */
+       public boolean isString() { return false; }
+       /** check whether the <code>REXP</code> object is a numeric vector
+        @return <code>true</code> if the receiver is a numeric vector, <code>false</code> otherwise */
+       public boolean isNumeric() { return false; }
+       /** check whether the <code>REXP</code> object is an integer vector
+        @return <code>true</code> if the receiver is an integer vector, <code>false</code> otherwise */
+       public boolean isInteger() { return false; }
+       /** check whether the <code>REXP</code> object is NULL
+        @return <code>true</code> if the receiver is NULL, <code>false</code> otherwise */
+       public boolean isNull() { return false; }
+       /** check whether the <code>REXP</code> object is a factor
+        @return <code>true</code> if the receiver is a factor, <code>false</code> otherwise */
+       public boolean isFactor() { return false; }
+       /** check whether the <code>REXP</code> object is a list (either generic vector or a pairlist - i.e. {@link #asList()} will succeed)
+        @return <code>true</code> if the receiver is a generic vector or a pair-list, <code>false</code> otherwise */
+       public boolean isList() { return false; }
+       /** check whether the <code>REXP</code> object is a pair-list
+        @return <code>true</code> if the receiver is a pair-list, <code>false</code> otherwise */
+       public boolean isPairList() { return false; }
+       /** check whether the <code>REXP</code> object is a logical vector
+        @return <code>true</code> if the receiver is a logical vector, <code>false</code> otherwise */
+       public boolean isLogical() { return false; }
+       /** check whether the <code>REXP</code> object is an environment
+        @return <code>true</code> if the receiver is an environment, <code>false</code> otherwise */
+       public boolean isEnvironment() { return false; }
+       /** check whether the <code>REXP</code> object is a language object
+        @return <code>true</code> if the receiver is a language object, <code>false</code> otherwise */
+       public boolean isLanguage() { return false; }
+       /** check whether the <code>REXP</code> object is an expression vector
+        @return <code>true</code> if the receiver is an expression vector, <code>false</code> otherwise */
+       public boolean isExpression() { return false; }
+       /** check whether the <code>REXP</code> object is a symbol
+        @return <code>true</code> if the receiver is a symbol, <code>false</code> otherwise */
+       public boolean isSymbol() { return false; }
+       /** check whether the <code>REXP</code> object is a vector
+        @return <code>true</code> if the receiver is a vector, <code>false</code> otherwise */
+       public boolean isVector() { return false; }
+       /** check whether the <code>REXP</code> object is a raw vector
+        @return <code>true</code> if the receiver is a raw vector, <code>false</code> otherwise */
+       public boolean isRaw() { return false; }
+       /** check whether the <code>REXP</code> object is a complex vector
+        @return <code>true</code> if the receiver is a complex vector, <code>false</code> otherwise */
+       public boolean isComplex() { return false; }
+       /** check whether the <code>REXP</code> object is a recursive obejct
+        @return <code>true</code> if the receiver is a recursive object, <code>false</code> otherwise */
+       public boolean isRecursive() { return false; }
+       /** check whether the <code>REXP</code> object is a reference to an R object
+        @return <code>true</code> if the receiver is a reference, <code>false</code> otherwise */
+       public boolean isReference() { return false; }
+
+       // basic accessor methods
+       /** returns the contents as an array of Strings (if supported by the represented object) */
+       public String[] asStrings() throws REXPMismatchException { throw new REXPMismatchException(this, "String"); }
+       /** returns the contents as an array of integers (if supported by the represented object) */
+       public int[] asIntegers() throws REXPMismatchException { throw new REXPMismatchException(this, "int"); }
+       /** returns the contents as an array of doubles (if supported by the represented object) */
+       public double[] asDoubles() throws REXPMismatchException { throw new REXPMismatchException(this, "double"); }
+       /** returns the contents as an array of bytes (if supported by the represented object) */
+       public byte[] asBytes() throws REXPMismatchException { throw new REXPMismatchException(this, "byte"); }
+       /** returns the contents as a (named) list (if supported by the represented object) */
+       public RList asList() throws REXPMismatchException { throw new REXPMismatchException(this, "list"); }
+       /** returns the contents as a factor (if supported by the represented object) */
+       public RFactor asFactor() throws REXPMismatchException { throw new REXPMismatchException(this, "factor"); }
+       /** attempt to represent the REXP by a native Java object and return it. Note that this may lead to loss of information (e.g., factors may be returned as a string array) and attributes are ignored. Not all R types can be converted to native Java objects. Also note that R has no concept of scalars, so vectors of length 1 will always be returned as an arrays (i.e., <code>int[1]</code> and not <code>Integer</code>). */
+       public Object asNativeJavaObject() throws REXPMismatchException { throw new REXPMismatchException(this, "native Java Object"); }
+
+       /** returns the length of a vector object. Note that we use R semantics here, i.e. a matrix will have a length of <i>m * n</i> since it is represented by a single vector (see {@link #dim} for retrieving matrix and multidimentional-array dimensions).
+        * @return length (number of elements) in a vector object
+        * @throws REXPMismatchException if this is not a vector object */
+       public int length() throws REXPMismatchException { throw new REXPMismatchException(this, "vector"); }
+
+       /** returns a boolean vector of the same length as this vector with <code>true</code> for NA values and <code>false</code> for any other values
+        *  @return a boolean vector of the same length as this vector with <code>true</code> for NA values and <code>false</code> for any other values
+        * @throws REXPMismatchException if this is not a vector object */
+       public boolean[] isNA() throws REXPMismatchException { throw new REXPMismatchException(this, "vector"); }
+       
+       // convenience accessor methods
+       /** convenience method corresponding to <code>asIntegers()[0]</code>
+        @return first entry returned by {@link #asInteger} */
+       public int asInteger() throws REXPMismatchException { int[] i = asIntegers(); return i[0]; }
+       /** convenience method corresponding to <code>asDoubles()[0]</code>
+        @return first entry returned by {@link #asDoubles} */
+       public double asDouble() throws REXPMismatchException { double[] d = asDoubles(); return d[0]; }
+       /** convenience method corresponding to <code>asStrings()[0]</code>
+        @return first entry returned by {@link #asStrings} */
+       public String asString() throws REXPMismatchException { String[] s = asStrings(); return s[0]; }
+
+       // methods common to all REXPs
+       
+       /** retrieve an attribute of the given name from this object
+        * @param name attribute name
+        * @return attribute value or <code>null</code> if the attribute does not exist */
+       public REXP getAttribute(String name) {
+               final REXPList a = _attr();
+               if (a==null || !a.isList()) return null;
+               return a.asList().at(name);
+       }
+       
+       /** checks whether this obejct has a given attribute
+        * @param name attribute name
+        * @return <code>true</code> if the attribute exists, <code>false</code> otherwise */
+       public boolean hasAttribute(String name) {
+               final REXPList a = _attr();
+               return (a!=null && a.isList() && a.asList().at(name)!=null);
+       }
+       
+       
+       // helper methods common to all REXPs
+       
+       /** returns dimensions of the object (as determined by the "<code>dim</code>" attribute)
+        * @return an array of integers with corresponding dimensions or <code>null</code> if the object has no dimension attribute */
+       public int[] dim() {
+               try {
+                       return hasAttribute("dim")?_attr().asList().at("dim").asIntegers():null;
+               } catch (REXPMismatchException me) {
+               }
+               return null;
+       }
+       
+       /** determines whether this object inherits from a given class in the same fashion as the <code>inherits()</code> function in R does (i.e. ignoring S4 inheritance)
+        * @param klass class name
+        * @return <code>true</code> if this object is of the class <code>klass</code>, <code>false</code> otherwise */
+       public boolean inherits(String klass) {
+               if (!hasAttribute("class")) return false;
+               try {
+                       String c[] = getAttribute("class").asStrings();
+                       if (c != null) {
+                               int i = 0;
+                               while (i < c.length) {
+                                       if (c[i]!=null && c[i].equals(klass)) return true;
+                                       i++;
+                               }
+                       }
+               } catch (REXPMismatchException me) {
+               }
+               return false;
+       }
+
+       /** this method allows a limited access to object's attributes - <b>{@link #getAttribute} should be used instead to access specific attributes</b>!. Note that the {@link #attr} attribute should never be used directly incase the REXP implements a lazy access (e.g. via a reference)
+           @return list of attributes or <code>null</code> if the object has no attributes
+        */
+       public REXPList _attr() { return attr; }
+       
+       /** returns a string description of the object
+        @return string describing the object - it can be of an arbitrary form and used only for debugging (do not confuse with {@link #asString()} for accessing string REXPs) */
+       public String toString() {
+               return super.toString()+((attr!=null)?"+":"");
+       }
+       
+       /** returns representation that it useful for debugging (e.g. it includes attributes and may include vector values -- see {@link #maxDebugItems})
+        @return extended description of the obejct -- it may include vector values
+        */
+       public String toDebugString() {
+               return (attr!=null)?(("<"+attr.toDebugString()+">")+super.toString()):super.toString();
+       }
+       
+       //======= complex convenience methods
+       /** returns the content of the REXP as a matrix of doubles (2D-array: m[rows][cols]). This is the same form as used by popular math packages for Java, such as JAMA. This means that following leads to desired results:<br>
+        <code>Matrix m=new Matrix(c.eval("matrix(c(1,2,3,4,5,6),2,3)").asDoubleMatrix());</code><br>
+        @return 2D array of doubles in the form double[rows][cols] or <code>null</code> if the contents is no 2-dimensional matrix of doubles */
+       public double[][] asDoubleMatrix() throws REXPMismatchException {
+               double[] ct = asDoubles();
+               REXP dim = getAttribute("dim");
+               if (dim == null) throw new REXPMismatchException(this, "matrix (dim attribute missing)");
+               int[] ds = dim.asIntegers();
+               if (ds.length != 2) throw new REXPMismatchException(this, "matrix (wrong dimensionality)");
+               int m = ds[0], n = ds[1];
+               
+               double[][] r = new double[m][n];
+               // R stores matrices as matrix(c(1,2,3,4),2,2) = col1:(1,2), col2:(3,4)
+               // we need to copy everything, since we create 2d array from 1d array
+               int k = 0;
+               for (int i = 0; i < n; i++)
+                       for (int j = 0; j < m; j++) 
+                               r[j][i] = ct[k++];
+               return r;
+       }
+       
+       /** creates a REXP that represents a double matrix in R based on matrix of doubles (2D-array: m[rows][cols]). This is the same form as used by popular math packages for Java, such as JAMA. The result of this function can be used in {@link REngine.assign} to store a matrix in R.
+        @param matrix array <code>double[rows][colums]</code> containing the matrix to convert into a REXP. If <code>matrix</code> is <code>null</code> or either of the dimensions is 0 then the resulting matrix will have the dimensions <code>0 x 0</code> (Note: Java cannot represent <code>0 x n</code> matrices for <code>n &gt; 0</code>, so special matrices with one dimension of 0 can only be created by setting dimensions directly).
+        @return <code>REXPDouble</code> with "dim" attribute which constitutes a matrix in R */
+       public static REXP createDoubleMatrix(double[][] matrix) {
+               int m = 0, n = 0;
+               double a[];
+               if (matrix != null && matrix.length != 0 && matrix[0].length != 0) {
+                       m = matrix.length;
+                       n = matrix[0].length;
+                       a = new double[m * n];
+                       int k = 0;
+                       for (int j = 0; j < n; j++)
+                               for (int i = 0; i < m; i++)
+                                       a[k++] = matrix[i][j];
+               } else a = new double[0];
+               return new REXPDouble(a,
+                                     new REXPList(
+                                                  new RList(
+                                                            new REXP[] { new REXPInteger(new int[] { m, n }) },
+                                                            new String[] { "dim" })
+                                                  )
+                                     );
+       }
+       
+       //======= tools
+       /** creates a data frame object from a list object using integer row names
+        *  @param l a (named) list of vectors ({@link REXPVector} subclasses), each element corresponds to a column and all elements must have the same length
+        *  @return a data frame object
+        *  @throws REXPMismatchException if the list is empty or any of the elements is not a vector */
+       public static REXP createDataFrame(RList l) throws REXPMismatchException {
+               if (l == null || l.size() < 1) throw new REXPMismatchException(new REXPList(l), "data frame (must have dim>0)");
+               if (!(l.at(0) instanceof REXPVector)) throw new REXPMismatchException(new REXPList(l), "data frame (contents must be vectors)");
+               REXPVector fe = (REXPVector) l.at(0);
+               return
+               new REXPGenericVector(l,
+                                                         new REXPList(
+                                                                       new RList(
+                                                                                  new REXP[] {
+                                                                                          new REXPString("data.frame"),
+                                                                                          new REXPString(l.keys()),
+                                                                                          new REXPInteger(new int[] { REXPInteger.NA, -fe.length() })
+                                                                                  },
+                                                                                  new String[] {
+                                                                                          "class",
+                                                                                          "names",
+                                                                                          "row.names"
+                                                                                  })));
+       }
+       
+       /** specifies how many items of a vector or list will be displayed in {@link #toDebugString} */
+       public static int maxDebugItems = 32;
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPDouble.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPDouble.java
new file mode 100644 (file)
index 0000000..894c1dc
--- /dev/null
@@ -0,0 +1,84 @@
+package org.rosuda.REngine;
+
+/** REXPDouble represents a vector of double precision floating point values. */
+public class REXPDouble extends REXPVector {
+       public double[] payload;
+       
+       /** NA real value as defined in R. Note: it can NOT be used in comparisons, you must use {@link #isNA(double)} instead. */
+       public static final double NA = Double.longBitsToDouble(0x7ff00000000007a2L);
+       
+       /** Java screws up the bits in NA real values, so we cannot compare to the real bits used by R (0x7ff00000000007a2L) but use this value which is obtained by passing the bits through Java's double type */
+       static final long NA_bits = Double.doubleToRawLongBits(Double.longBitsToDouble(0x7ff00000000007a2L));
+       
+       /** checks whether a given double value is a NA representation in R. Note that NA is NaN but not all NaNs are NA. */
+       public static boolean isNA(double value) {
+               /* on OS X i386 the MSB of the fraction is set even though R doesn't set it.
+                  Although this is technically a good idea (to make it a QNaN) it's not what R does and thus makes the comparison tricky */
+               return (Double.doubleToRawLongBits(value) & 0xfff7ffffffffffffL) == (NA_bits & 0xfff7ffffffffffffL);
+       }
+
+       /** create real vector of the length 1 with the given value as its first (and only) element */
+       public REXPDouble(double load) {
+               super();
+               payload=new double[] { load };
+       }
+       
+       public REXPDouble(double[] load) {
+               super();
+               payload=(load==null)?new double[0]:load;
+       }
+
+       public REXPDouble(double[] load, REXPList attr) {
+               super(attr);
+               payload=(load==null)?new double[0]:load;
+       }
+       
+       public int length() { return payload.length; }
+
+       public Object asNativeJavaObject() {
+               return payload;
+       }
+
+       /** return <code>true</code> */
+       public boolean isNumeric() { return true; }
+
+       /** returns the values represented by this vector */
+       public double[] asDoubles() { return payload; }
+
+       /** converts the values of this vector into integers by cast */
+       public int[] asIntegers() {
+               int[] a = new int[payload.length];
+               int i = 0;
+               while (i < payload.length) { a[i] = (int) payload[i]; i++; }
+               return a;
+       }
+
+       /** converts the values of this vector into strings */
+       public String[] asStrings() {
+               String[] s = new String[payload.length];
+               int i = 0;
+               while (i < payload.length) { s[i] = ""+payload[i]; i++; }
+               return s;
+       }
+       
+       /** returns a boolean vector of the same length as this vector with <code>true</code> for NA values and <code>false</code> for any other values (including NaNs) */
+       public boolean[] isNA() {
+               boolean a[] = new boolean[payload.length];
+               int i = 0;
+               while (i < a.length) { a[i] = isNA(payload[i]); i++; }
+               return a;
+       }
+       
+       public String toDebugString() {
+               StringBuffer sb = new StringBuffer(super.toDebugString()+"{");
+               int i = 0;
+               while (i < payload.length && i < maxDebugItems) {
+                       if (i>0) sb.append(",");
+                       sb.append(payload[i]);
+                       i++;
+               }
+               if (i < payload.length) sb.append(",..");
+               return sb.toString()+"}";
+       }
+       
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPEnvironment.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPEnvironment.java
new file mode 100644 (file)
index 0000000..d4d4ab3
--- /dev/null
@@ -0,0 +1,70 @@
+package org.rosuda.REngine;
+// environments are like REXPReferences except that they cannot be resolved
+
+/** REXPEnvironment represents an environment in R. Very much like {@link org.rosuda.REngine.REXPReference} this is a proxy object to the actual object on the R side. It provides methods for accessing the content of the environment. The actual implementation may vary by the back-end used and not all engines support environments. Check {@link org.rosuda.REngine.REngine.supportsEnvironments()} for the given engine. Environments are specific for a given engine, they cannot be passed across engines.
+ */
+public class REXPEnvironment extends REXP {
+       /** engine associated with this environment */
+       REngine eng;
+       /** transparent handle that can be used by the engine to indentify the environment. It is not used by REngine API itself. */
+       Object handle;
+       
+       /** create a new environemnt reference - this constructor should never be used directly, use {@link REngine.newEnvironment()} instead.
+        *  @param eng engine responsible for this environment
+        *  @param handle handle used by the engine to identify this environment
+        */
+       public REXPEnvironment(REngine eng, Object handle) {
+               super();
+               this.eng = eng;
+               this.handle = handle;
+       }
+       
+       public boolean isEnvironment() { return true; }
+       
+       /** returns the handle used to identify this environemnt in the engine - for internal use by engine implementations only
+        *  @return handle of this environment */
+       public Object getHandle() { return handle; }
+       
+       /** get a value from this environment
+        *  @param name name of the value
+        *  @param resolve if <code>false</code> returns a reference to the object, if <code>false</code> the reference is resolved
+        *  @return value corresponding to the symbol name or possibly <code>null</code> if the value is unbound (the latter is currently engine-specific) */
+       public REXP get(String name, boolean resolve) throws REngineException {
+               try {
+                       return eng.get(name, this, resolve);
+               } catch (REXPMismatchException e) { // this should never happen because this is always guaranteed to be REXPEnv
+                       throw(new REngineException(eng, "REXPMismatchException:"+e+" in get()"));
+               }
+       }
+
+       /** get a value from this environment - equavalent to <code>get(name, true)</code>.
+        *  @param name name of the value
+        *  @return value (see {@link #get(String,boolean)}) */
+       public REXP get(String name) throws REngineException {
+               return get(name, true);
+       }
+       
+       /** assigns a value to a given symbol name
+        *  @param name symbol name
+        *  @param value value */
+       public void assign(String name, REXP value) throws REngineException, REXPMismatchException {
+               eng.assign(name, value, this);
+       }
+       
+       /** returns the parent environment or a reference to it
+        *  @param resolve if <code>true</code> returns the environemnt, otherwise a reference. 
+        *  @return parent environemnt (or a reference to it) */
+       public REXP parent(boolean resolve) throws REngineException {
+               try {
+                       return eng.getParentEnvironment(this, resolve);
+               } catch (REXPMismatchException e) { // this should never happen because this is always guaranteed to be REXPEnv
+                       throw(new REngineException(eng, "REXPMismatchException:"+e+" in parent()"));
+               }
+       }
+
+       /** returns the parent environment. This is equivalent to <code>parent(true)</code>.
+        *  @return parent environemnt */
+       public REXPEnvironment parent() throws REngineException {
+               return (REXPEnvironment) parent(true);
+       }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPExpressionVector.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPExpressionVector.java
new file mode 100644 (file)
index 0000000..c16ff53
--- /dev/null
@@ -0,0 +1,15 @@
+package org.rosuda.REngine;
+
+/** REXPExpressionVector represents a vector of expressions in R. It is essentially a special kind of generic vector - its elements are expected to be valid R expressions. */
+public class REXPExpressionVector extends REXPGenericVector {
+       /** create a new vector of expressions from a list of expressions.
+        *  @param list list of expressions to store in this vector */
+       public REXPExpressionVector(RList list) { super(list); }        
+
+       /** create a new vector of expressions from a list of expressions.
+        *  @param list list of expressions to store in this vector
+        *  @param attr attributes for the R object */
+       public REXPExpressionVector(RList list, REXPList attr) { super(list, attr); }
+
+       public boolean isExpression() { return true; }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPFactor.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPFactor.java
new file mode 100644 (file)
index 0000000..11c94a8
--- /dev/null
@@ -0,0 +1,74 @@
+package org.rosuda.REngine;
+
+/** REXPFactor represents a factor in R. It is an integer vector with levels for each contained category. */
+// FIXME: this is currently somehow screwed - the concept of RFactor and REXPFactor is duplicate - we need to remove this historical baggage
+public class REXPFactor extends REXPInteger {
+       private String[] levels;
+       private RFactor factor;
+       
+       /** create a new factor REXP
+        *  @param ids indices (one-based!)
+        *  @param levels levels */
+       public REXPFactor(int[] ids, String[] levels) {
+               super(ids);
+               this.levels = (levels==null)?(new String[0]):levels;
+               factor = new RFactor(this.payload, this.levels, false, 1);
+               attr = new REXPList(
+                                                       new RList(
+                                                                         new REXP[] {
+                                                                                 new REXPString(this.levels), new REXPString("factor")
+                                                                         }, new String[] { "levels", "class" }));
+       }
+
+       /** create a new factor REXP
+        *  @param ids indices (one-based!)
+        *  @param levels levels
+        *  @param attr attributes */
+       public REXPFactor(int[] ids, String[] levels, REXPList attr) {
+               super(ids, attr);
+               this.levels = (levels==null)?(new String[0]):levels;
+               factor = new RFactor(this.payload, this.levels, false, 1);
+       }
+       
+       /** create a new factor REXP from an existing RFactor
+        *  @param factor factor object (can be of any index base, the contents will be pulled with base 1) */
+       public REXPFactor(RFactor factor) {
+               super(factor.asIntegers(1));
+               this.factor = factor;
+               this.levels = factor.levels();
+               attr = new REXPList(
+                                                       new RList(
+                                                                         new REXP[] {
+                                                                                 new REXPString(this.levels), new REXPString("factor")
+                                                                         }, new String[] { "levels", "class" }));
+       }
+       
+       /** create a new factor REXP from an existing RFactor
+        *  @param factor factor object (can be of any index base, the contents will be pulled with base 1)
+        *  @param attr attributes */
+       public REXPFactor(RFactor factor, REXPList attr) {
+               super(factor.asIntegers(1), attr);
+               this.factor = factor;
+               this.levels = factor.levels();
+       }
+
+       public boolean isFactor() { return true; }
+
+       /** return the contents as a factor - the factor is guaranteed to have index base 1
+        *  @return the contents as a factor */
+       public RFactor asFactor() {
+               return factor;
+       }
+
+       public String[] asStrings() {
+               return factor.asStrings();
+       }
+
+       public Object asNativeJavaObject() {
+               return asStrings();
+       }
+       
+       public String toString() {
+               return super.toString()+"["+levels.length+"]";
+       }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPGenericVector.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPGenericVector.java
new file mode 100644 (file)
index 0000000..c739994
--- /dev/null
@@ -0,0 +1,93 @@
+package org.rosuda.REngine;
+import java.util.Vector;
+import java.util.HashMap;
+
+/** REXPGenericVector represents a generic vector in R. Its elements can be typically of any {@link REXP} type. */
+public class REXPGenericVector extends REXPVector {
+       /** payload */
+       public RList payload;
+       
+       /** creates a new generic vector from a list. If the list is named, the <code>"names"</code> attribute is created automatically from it.
+        *  @param list list to create the vector from */
+       public REXPGenericVector(RList list) {
+               super();
+               payload=(list==null)?new RList():list;
+               // automatically generate 'names' attribute
+               if (payload.isNamed())
+                       attr = new REXPList(
+                               new RList(new REXP[] { new REXPString(payload.keys()) },
+                                                 new String[] { "names" }));
+       }
+       
+       /** creates a new generic vector from a list. Note that the names in the list are ignored as they are expected to be defined by the attributes parameter.
+        *  @param list list to create the vector from (names are ignored - use {@link #REXPGenericVector(RList)} or the <code>"names"</code> attribute for named lists
+        *  @param attr attributes */
+       public REXPGenericVector(RList list, REXPList attr) {
+               super(attr);
+               payload=(list==null)?new RList():list;
+       }
+
+       /* generic vectors are converted either to a <code>Map</code> (if it is a named vector and there are no duplicate names) or a <code>Vector</code>. The contained elements are converted using <code>asNativeJavaObject</code> recursively. */
+       public Object asNativeJavaObject() throws REXPMismatchException {
+               // we need to convert the inside as well
+               int n = payload.size();
+
+               // named list -> map but only if
+               //  a) all names are present
+               //  b) there are no duplicates in the names
+               if (payload.isNamed()) {
+                       String[] names = payload.keys();
+                       if (names.length == n) {
+                               HashMap map = new HashMap();
+                               boolean valid = true;
+                               for (int i = 0; i < n; i++) {
+                                       if (map.containsKey(names[i])) {
+                                               valid = false;
+                                               break;
+                                       }
+                                       Object value = payload.elementAt(i);
+                                       if (value != null)
+                                               value = ((REXP) value).asNativeJavaObject();
+                                       map.put(value, names[i]);
+                               }
+                               if (valid)
+                                       return map;
+                       }
+               }
+
+               // otherwise drop names and use just a vector
+               Vector v = new Vector();
+               for (int i = 0; i < n; i++) {
+                       Object value = payload.elementAt(i);
+                       if (value != null)
+                               value = ((REXP) value).asNativeJavaObject();
+                       v.addElement(value);
+               }
+
+               return v;
+       }
+       
+       public int length() { return payload.size(); }
+
+       public boolean isList() { return true; }
+
+       public boolean isRecursive() { return true; }
+
+       public RList asList() { return payload; }
+       
+       public String toString() {
+               return super.toString()+(asList().isNamed()?"named":"");
+       }
+
+       public String toDebugString() {
+               StringBuffer sb = new StringBuffer(super.toDebugString()+"{");
+               int i = 0;
+               while (i < payload.size() && i < maxDebugItems) {
+                       if (i>0) sb.append(",\n");
+                       sb.append(payload.at(i).toDebugString());
+                       i++;
+               }
+               if (i < payload.size()) sb.append(",..");
+               return sb.toString()+"}";
+       }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPInteger.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPInteger.java
new file mode 100644 (file)
index 0000000..55d4d1c
--- /dev/null
@@ -0,0 +1,77 @@
+package org.rosuda.REngine;
+
+/** REXPDouble represents a vector of integer values. */
+public class REXPInteger extends REXPVector {
+       public int[] payload;
+       
+       /** NA integer value as defined in R. Unlike its real equivalent this one can be used in comparisons, although {@link #isNA(int) } is provided for consistency. */
+       public static final int NA = -2147483648;
+
+       public static boolean isNA(int value) {
+               return (value == NA);
+       }
+       
+       /** create integer vector of the length 1 with the given value as its first (and only) element */
+       public REXPInteger(int load) {
+               super();
+               payload=new int[] { load };
+       }
+       
+       /** create integer vector with the payload specified by <code>load</code> */
+       public REXPInteger(int[] load) {
+               super();
+               payload=(load==null)?new int[0]:load;
+       }
+
+       /** create integer vector with the payload specified by <code>load</code> and attributes <code>attr</code> */
+       public REXPInteger(int[] load, REXPList attr) {
+               super(attr);
+               payload=(load==null)?new int[0]:load;
+       }
+
+       public Object asNativeJavaObject() {
+               return payload;
+       }
+       
+       public int length() { return payload.length; }
+
+       public boolean isInteger() { return true; }
+       public boolean isNumeric() { return true; }
+
+       public int[] asIntegers() { return payload; }
+
+       /** returns the contents of this vector as doubles */
+       public double[] asDoubles() {
+               double[] d = new double[payload.length];
+               int i = 0;
+               while (i < payload.length) { d[i] = (double) payload[i]; i++; }
+               return d;
+       }
+
+       /** returns the contents of this vector as strings */
+       public String[] asStrings() {
+               String[] s = new String[payload.length];
+               int i = 0;
+               while (i < payload.length) { s[i] = ""+payload[i]; i++; }
+               return s;
+       }
+       
+       public boolean[] isNA() {
+               boolean a[] = new boolean[payload.length];
+               int i = 0;
+               while (i < a.length) { a[i] = (payload[i]==NA); i++; }
+               return a;
+       }
+       
+       public String toDebugString() {
+               StringBuffer sb = new StringBuffer(super.toDebugString()+"{");
+               int i = 0;
+               while (i < payload.length && i < maxDebugItems) {
+                       if (i>0) sb.append(",");
+                       sb.append(payload[i]);
+                       i++;
+               }
+               if (i < payload.length) sb.append(",..");
+               return sb.toString()+"}";
+       }       
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPJavaReference.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPJavaReference.java
new file mode 100644 (file)
index 0000000..3059697
--- /dev/null
@@ -0,0 +1,26 @@
+package org.rosuda.REngine;
+
+/** REXPJavaReference is a reference to a Java object that has been resolved from is R wrapper. Note that not all engines support references. */
+public class REXPJavaReference extends REXP {
+       /** the referenced Java object */
+       Object object;
+
+       /** creates a new Java reference R object
+        *  @param o Java object referenced by the REXP */
+       public REXPJavaReference(Object o) { super(); this.object = o; }
+
+       /** creates a new Java reference R object
+        *  @param o Java object referenced by the REXP
+        *  @param attr attributes (of the R wrapper) */
+       public REXPJavaReference(Object o, REXPList attr) { super(attr); this.object = o; }
+       
+       /** returns the Java object referenced by this REXP
+        *  @return Java object */
+       public Object getObject() { return object; }
+
+       public Object asNativeJavaObject() { return object; }
+       
+       public String toString() {
+               return super.toString() + "[" + object + "]";
+       }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPLanguage.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPLanguage.java
new file mode 100644 (file)
index 0000000..3ec3385
--- /dev/null
@@ -0,0 +1,9 @@
+package org.rosuda.REngine;
+
+/** represents a language object in R */
+public class REXPLanguage extends REXPList {
+       public REXPLanguage(RList list) { super(list); }
+       public REXPLanguage(RList list, REXPList attr) { super(list, attr); }
+       
+       public boolean isLanguage() { return true; }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPList.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPList.java
new file mode 100644 (file)
index 0000000..004a78b
--- /dev/null
@@ -0,0 +1,64 @@
+package org.rosuda.REngine;
+
+/** Represents a pairlist in R. Unlike the actual internal R implementation this one
+    does not use CAR/CDR/TAG linked representation but a @link{RList} object. */
+public class REXPList extends REXPVector {
+       public RList payload;
+       
+       /* create a new pairlist with the contents of a named R list and no attributes.
+          @param list named list with the contents */
+       public REXPList(RList list) {
+               super();
+               payload=(list==null)?new RList():list;
+       }
+
+       /* create a new pairlist with the contents of a named R list and attributes.
+          @param list named list with the contents
+          @param attr attributes */
+       public REXPList(RList list, REXPList attr) {
+               super(attr);
+               payload=(list==null)?new RList():list;
+       }
+
+       /* create a pairlist containing just one pair comprising of one value and one name.
+          This is a convenience constructor most commonly used to create attribute pairlists.
+          @param value of the element in the pairlist (must not be <code>null</code>)
+          @param name of the element in the pairlist (must not be <code>null</code>) */
+       public REXPList(REXP value, String name) {
+               super();
+               payload = new RList(new REXP[] { value }, new String[] { name });
+       }
+
+       public Object asNativeJavaObject() throws REXPMismatchException {
+               // since REXPGenericVector does the hard work, we just cheat and use it in turn
+               REXPGenericVector v = new REXPGenericVector(payload);
+               return v.asNativeJavaObject();
+       }
+
+       public int length() { return payload.size(); }
+
+       public boolean isList() { return true; }
+       public boolean isPairList() { return true; }
+
+       public boolean isRecursive() { return true; }
+
+       public RList asList() { return payload; }
+       
+       public String toString() {
+               return super.toString()+(asList().isNamed()?"named":"");
+       }
+       
+       public String toDebugString() {
+               StringBuffer sb = new StringBuffer(super.toDebugString()+"{");
+               int i = 0;
+               while (i < payload.size() && i < maxDebugItems) {
+                       if (i>0) sb.append(",\n");
+                       String name = payload.keyAt(i);
+                       if (name!=null) sb.append(name+"=");
+                       sb.append(payload.at(i).toDebugString());
+                       i++;
+               }
+               if (i < payload.size()) sb.append(",..");
+               return sb.toString()+"}";
+       }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPLogical.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPLogical.java
new file mode 100644 (file)
index 0000000..0769c27
--- /dev/null
@@ -0,0 +1,140 @@
+package org.rosuda.REngine;
+
+/** REXPLogical represents a vector of logical values (TRUE, FALSE or NA). Unlike Java's boolean type R's logicals support NA values therefore either of {@link #isTRUE()}, {@link #isFALSE()} or {@link #isNA()} must be used to convert logicals to boolean values. */
+public class REXPLogical extends REXPVector {
+       protected byte[] payload;
+       
+       /** NA integer value as defined in R. Unlike its real equivalent this one can be used in comparisons, although {@link #isNA(int) } is provided for consistency. */
+       static final int NA_internal = -2147483648;
+
+       /** NA boolean value as used in REXPLogical implementation. This differs from the value used in R since R uses int data type and we use byte. Unlike its real equivalent this one can be used in comparisons, although {@link #isNA(byte) } is provided for consistency. */
+       public static final byte NA = -128;
+       public static final byte TRUE = 1;
+       public static final byte FALSE = 0;
+       
+       public static boolean isNA(byte value) {
+               return (value == NA);
+       }
+       
+       /** create logical vector of the length 1 with the given value as its first (and only) element */
+       public REXPLogical(boolean load) {
+               super();
+               payload = new byte[] { load ? TRUE : FALSE };
+       }
+
+       /** create logical vector of the length 1 with the given value as its first (and only) element */
+       public REXPLogical(byte load) {
+               super();
+               payload = new byte[] { load };
+       }
+       
+       /** create logical vector with the payload specified by <code>load</code> */
+       public REXPLogical(byte[] load) {
+               super();
+               payload = (load==null) ? new byte[0]:load;
+       }
+
+       /** create logical vector with the payload specified by <code>load</code> */
+       public REXPLogical(boolean[] load) {
+               super();
+               payload = new byte[(load == null) ? 0 : load.length];
+               if (load != null)
+                       for (int i = 0; i < load.length; i++)
+                               payload[i] = load[i] ? TRUE : FALSE;
+       }
+       
+       /** create integer vector with the payload specified by <code>load</code> and attributes <code>attr</code> */
+       public REXPLogical(byte[] load, REXPList attr) {
+               super(attr);
+               payload = (load==null) ? new byte[0] : load;
+       }
+       
+       /** create integer vector with the payload specified by <code>load</code> and attributes <code>attr</code> */
+       public REXPLogical(boolean[] load, REXPList attr) {
+               super(attr);
+               payload = new byte[(load == null) ? 0 : load.length];
+               if (load != null)
+                       for (int i = 0; i < load.length; i++)
+                               payload[i] = load[i] ? TRUE : FALSE;
+       }
+
+       public int length() { return payload.length; }
+
+       public boolean isLogical() { return true; }
+
+       public Object asNativeJavaObject() {
+               return payload;
+       }
+
+       public int[] asIntegers() {
+               int p[] = new int[payload.length];
+               for (int i = 0; i < payload.length; i++) // map bytes to integers including NA representation
+                       p[i] = (payload[i] == NA) ? REXPInteger.NA : ((payload[i] == FALSE) ? 0 : 1);
+               return p;
+       }
+
+       public byte[] asBytes() { return payload; }
+
+       /** returns the contents of this vector as doubles */
+       public double[] asDoubles() {
+               double[] d = new double[payload.length];
+               for (int i = 0; i < payload.length; i++)
+                       d[i] = (payload[i] == NA) ? REXPDouble.NA : ((payload[i] == FALSE) ? 0.0 : 1.0);
+               return d;
+       }
+
+       /** returns the contents of this vector as strings */
+       public String[] asStrings() {
+               String[] s = new String[payload.length];
+               for (int i = 0; i < payload.length; i++)
+                       s[i] = (payload[i] == NA) ? "NA" : ((payload[i] == FALSE) ? "FALSE" : "TRUE");
+               return s;
+       }
+       
+       public boolean[] isNA() {
+               boolean a[] = new boolean[payload.length];
+               int i = 0;
+               while (i < a.length) { a[i] = (payload[i] == NA); i++; }
+               return a;
+       }
+       
+       /** returns a boolean array of the same langth as the receiver with <code>true</code> for <code>TRUE</code> values and <code>false</code> for <code>FALSE</code> and <code>NA</code> values.
+        @return boolean array */
+       public boolean[] isTRUE() {
+               boolean a[] = new boolean[payload.length];
+               int i = 0;
+               while (i < a.length) { a[i] = (payload[i] != NA && payload[i] != FALSE); i++; }
+               return a;
+       }
+       
+       /** returns a boolean array of the same langth as the receiver with <code>true</code> for <code>FALSE</code> values and <code>false</code> for <code>TRUE</code> and <code>NA</code> values.
+        @return boolean array */
+       public boolean[] isFALSE() {
+               boolean a[] = new boolean[payload.length];
+               int i = 0;
+               while (i < a.length) { a[i] = (payload[i] == FALSE); i++; }
+               return a;
+       }
+       
+       /** returns a boolean array of the same langth as the receiver with <code>true</code> for <code>TRUE</code> values and <code>false</code> for <code>FALSE</code> and <code>NA</code> values.
+        @return boolean array
+        @deprecated replaced by {@link #isTRUE()} for consistency with R nomenclature. */
+       public boolean[] isTrue() { return isTRUE(); }
+
+       /** returns a boolean array of the same langth as the receiver with <code>true</code> for <code>FALSE</code> values and <code>false</code> for <code>TRUE</code> and <code>NA</code> values.
+        @return boolean array
+        @deprecated replaced by {@link #isTRUE()} for consistency with R nomenclature. */
+       public boolean[] isFalse() { return isFALSE(); }
+
+       public String toDebugString() {
+               StringBuffer sb = new StringBuffer(super.toDebugString()+"{");
+               int i = 0;
+               while (i < payload.length && i < maxDebugItems) {
+                       if (i>0) sb.append(",");
+                       sb.append((payload[i] == NA) ? "NA" : ((payload[i] == FALSE) ? "FALSE" : "TRUE"));
+                       i++;
+               }
+               if (i < payload.length) sb.append(",..");
+               return sb.toString()+"}";
+       }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPMismatchException.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPMismatchException.java
new file mode 100644 (file)
index 0000000..5b749a2
--- /dev/null
@@ -0,0 +1,40 @@
+// REngine - generic Java/R API
+//
+// Copyright (C) 2007,2008 Simon Urbanek
+// --- for licensing information see LICENSE file in the distribution ---
+//
+//  REXPMismatch.java
+//
+//  Created by Simon Urbanek on 2007/05/03
+//
+//  $Id: REngineException.java 2555 2006-06-21 20:36:42Z urbaneks $
+//
+
+package org.rosuda.REngine;
+
+/** This exception is thrown whenever the operation requested is not supported by the given R object type, e.g. using <tt>asStrings</tt> on an S4 object. Most {@link REXP} methods throw this exception. Previous R/Java interfaces were silently returning <code>null</code> in those cases, but using exceptions helps to write more robust code. */
+public class REXPMismatchException extends Exception {
+       REXP sender;
+       String access;
+       
+       /** primary constructor. The exception message will be formed as "attempt to access &lt;REXP-class&gt; as &lt;access-string&gt;"
+        * @param sender R object that triggered this exception (cannot be <code>null</code>!)
+        * @param access assumed type of the access that was requested. It should be a simple name of the assumed type (e.g. <tt>"vector"</tt>). The type name can be based on R semantics beyond basic types reflected by REXP classes. In cases where certain assertions were not satisfied, the string should be of the form <tt>"type (assertion)"</tt> (e.g. <tt>"data frame (must have dim>0)"</tt>). */
+    public REXPMismatchException(REXP sender, String access) {
+        super("attempt to access "+sender.getClass().getName()+" as "+access);
+               this.sender = sender;
+               this.access = access;
+    }
+       
+       /** retrieve the exception sender/origin
+        * @return REXP object that triggered the exception */
+       public REXP getSender() {
+               return sender;
+       }
+       
+       /** get the assumed access type that was violated by the sender.
+        * @return string describing the access type. See {@link #REXPMismatchException} for details. */
+       public String getAccess() {
+               return access;
+       }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPNull.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPNull.java
new file mode 100644 (file)
index 0000000..f183d28
--- /dev/null
@@ -0,0 +1,15 @@
+package org.rosuda.REngine;
+
+/** represents a NULL object in R.
+ <p>
+ Note: there is a slight asymmetry - in R NULL is represented by a zero-length pairlist. For this reason <code>REXPNull</code> returns <code>true</code> for {@link #isList()} and {@link #asList()} will return an empty list. Nonetheless <code>REXPList</code> of the length 0 will NOT return <code>true</code> in {@link #isNull()} (currently), becasue it is considered a different object in Java. These nuances are still subject to change, because it's not clear how it should be treated. At any rate use <code>REXPNull</code> instead of empty <code>REXPList</code> if NULL is the intended value.
+ */
+public class REXPNull extends REXP {
+       public REXPNull() { super(); }
+       public REXPNull(REXPList attr) { super(attr); }
+       
+       public boolean isNull() { return true; }
+       public boolean isList() { return true; } // NULL is a list
+       public RList asList() { return new RList(); }
+       public Object asNativeJavaObject() { return null; }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPRaw.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPRaw.java
new file mode 100644 (file)
index 0000000..4df46b5
--- /dev/null
@@ -0,0 +1,31 @@
+package org.rosuda.REngine;
+
+/** REXPRaw represents a raw vector in R - essentially a sequence of bytes. */
+public class REXPRaw extends REXPVector {
+       private byte[] payload;
+       
+       /** create a new raw vector with the specified payload
+        *  @param load payload of the raw vector */
+       public REXPRaw(byte[] load) {
+               super();
+               payload=(load==null)?new byte[0]:load;
+       }
+
+       /** create a new raw vector with the specified payload and attributes
+        *  @param load payload of the raw vector 
+        *  @param attr attributes for the resulting R object */
+       public REXPRaw(byte[] load, REXPList attr) {
+               super(attr);
+               payload=(load==null)?new byte[0]:load;
+       }
+       
+       public int length() { return payload.length; }
+
+       public boolean isRaw() { return true; }
+
+       public byte[] asBytes() { return payload; }
+
+       public Object asNativeJavaObject() {
+               return payload;
+       }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPReference.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPReference.java
new file mode 100644 (file)
index 0000000..dd40268
--- /dev/null
@@ -0,0 +1,89 @@
+package org.rosuda.REngine;
+
+/** this class represents a reference (proxy) to an R object.
+ <p>
+ The reference semantics works by calling {@link #resolve()} (which in turn uses {@link REngine#resolveReference(REXP)} on itself) whenever any methods are accessed. The implementation is not finalized yat and may change as we approach the JRI interface which is more ameanable to reference-style access. Subclasses are free to implement more efficient implementations. */
+public class REXPReference extends REXP {
+       /** engine which will be used to resolve the reference */
+       protected REngine eng;
+       /** an opaque (optional) handle */
+       protected Object handle;
+       /** resolved (cached) object */
+       protected REXP resolvedValue;
+
+       /** create an external REXP reference using given engine and handle. The handle value is just an (optional) identifier not used by the implementation directly. */
+       public REXPReference(REngine eng, Object handle) {
+               super();
+               this.eng = eng;
+               this.handle = handle;
+       }
+
+       /** shortcut for <code>REXPReference(eng, new Long(handle))</code> that is used by native code */
+       REXPReference(REngine eng, long handle) {
+               this(eng, new Long(handle));
+       }
+
+       /** resolve the external REXP reference into an actual REXP object. In addition, the value (if not <code>null</code>) will be cached for subsequent calls to <code>resolve</code> until <code>invalidate</code> is called. */
+       public REXP resolve() {
+               if (resolvedValue != null)
+                       return resolvedValue;
+               try {
+                       resolvedValue = eng.resolveReference(this);
+                       return resolvedValue;
+               } catch (REXPMismatchException me) {
+                       // this should never happen since we are REXPReference
+               } catch(REngineException ee) {
+                       // FIXME: what to we do?
+               }
+               return null;
+       }
+
+       /** invalidates any cached representation of the reference */
+       public void invalidate() {
+               resolvedValue = null;
+       }
+       
+       /** finalization that notifies the engine when a reference gets collected */
+       protected void finalize() throws Throwable {
+               try {
+                       eng.finalizeReference(this);
+               } finally {
+                       super.finalize();
+               }
+       }       
+       // type checks
+       public boolean isString() { return resolve().isString(); }
+       public boolean isNumeric() { return resolve().isNumeric(); }
+       public boolean isInteger() { return resolve().isInteger(); }
+       public boolean isNull() { return resolve().isNull(); }
+       public boolean isFactor() { return resolve().isFactor(); }
+       public boolean isList() { return resolve().isList(); }
+       public boolean isLogical() { return resolve().isLogical(); }
+       public boolean isEnvironment() { return resolve().isEnvironment(); }
+       public boolean isLanguage() { return resolve().isLanguage(); }
+       public boolean isSymbol() { return resolve().isSymbol(); }
+       public boolean isVector() { return resolve().isVector(); }
+       public boolean isRaw() { return resolve().isRaw(); }
+       public boolean isComplex() { return resolve().isComplex(); }
+       public boolean isRecursive() { return resolve().isRecursive(); }
+       public boolean isReference() { return true; }
+
+       // basic accessor methods
+       public String[] asStrings() throws REXPMismatchException { return resolve().asStrings(); }
+       public int[] asIntegers() throws REXPMismatchException { return resolve().asIntegers(); }
+       public double[] asDoubles() throws REXPMismatchException { return resolve().asDoubles(); }
+       public RList asList() throws REXPMismatchException { return resolve().asList(); }
+       public RFactor asFactor() throws REXPMismatchException { return resolve().asFactor(); }
+
+       public int length() throws REXPMismatchException { return resolve().length(); }
+
+       public REXPList _attr() { return resolve()._attr(); }
+       
+       public Object getHandle() { return handle; }
+       
+       public REngine getEngine() { return eng; }
+
+       public String toString() {
+               return super.toString()+"{eng="+eng+",h="+handle+"}";
+       }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPS4.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPS4.java
new file mode 100644 (file)
index 0000000..92ff621
--- /dev/null
@@ -0,0 +1,7 @@
+package org.rosuda.REngine;
+
+/** S4 REXP is a completely vanilla REXP */
+public class REXPS4 extends REXP {
+       public REXPS4() { super(); }
+       public REXPS4(REXPList attr) { super(attr); }   
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPString.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPString.java
new file mode 100644 (file)
index 0000000..302702b
--- /dev/null
@@ -0,0 +1,60 @@
+package org.rosuda.REngine;
+
+/** REXPString represents a character vector in R. */
+public class REXPString extends REXPVector {
+       /** payload */
+       public String[] payload;
+       
+       /** create a new character vector of the length one
+        *  @param load first (and only) element of the vector */
+       public REXPString(String load) {
+               super();
+               payload=new String[] { load };
+       }
+
+       /** create a new character vector
+        *  @param load string elements of the vector */
+       public REXPString(String[] load) {
+               super();
+               payload=(load==null)?new String[0]:load;
+       }
+
+       /** create a new character vector with attributes
+        *  @param load string elements of the vector
+        *  @param attr attributes */
+       public REXPString(String[] load, REXPList attr) {
+               super(attr);
+               payload=(load==null)?new String[0]:load;
+       }
+       
+       public int length() { return payload.length; }
+
+       public boolean isString() { return true; }
+
+       public Object asNativeJavaObject() {
+               return payload;
+       }
+
+       public String[] asStrings() {
+               return payload;
+       }
+       
+       public boolean[] isNA() {
+               boolean a[] = new boolean[payload.length];
+               int i = 0;
+               while (i < a.length) { a[i] = (payload[i]==null); i++; }
+               return a;
+       }
+       
+       public String toDebugString() {
+               StringBuffer sb = new StringBuffer(super.toDebugString()+"{");
+               int i = 0;
+               while (i < payload.length && i < maxDebugItems) {
+                       if (i>0) sb.append(",");
+                       sb.append("\""+payload[i]+"\"");
+                       i++;
+               }
+               if (i < payload.length) sb.append(",..");
+               return sb.toString()+"}";
+       }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPSymbol.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPSymbol.java
new file mode 100644 (file)
index 0000000..fc589f8
--- /dev/null
@@ -0,0 +1,35 @@
+package org.rosuda.REngine;
+
+/** REXPSymbol represents a symbol in R. */
+public class REXPSymbol extends REXP {
+       /** name of the symbol */
+       private String name;
+       
+       /** create a new symbol of the given name */
+       public REXPSymbol(String name) {
+               super();
+               this.name=(name==null)?"":name;
+       }
+       
+       public boolean isSymbol() { return true; }
+       
+       /** returns the name of the symbol
+        *  @return name of the symbol */
+       public String asString() { return name; }
+
+       public String[] asStrings() {
+               return new String[] { name };
+       }
+       
+       public String toString() {
+               return getClass().getName()+"["+name+"]";
+       }
+
+       public String toDebugString() {
+               return super.toDebugString()+"["+name+"]";
+       }
+
+       public Object asNativeJavaObject() {
+               return name;
+       }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPUnknown.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPUnknown.java
new file mode 100644 (file)
index 0000000..f204709
--- /dev/null
@@ -0,0 +1,24 @@
+package org.rosuda.REngine;
+
+/** REXPUnknown is a stand-in for an object that cannot be represented in the REngine hierarchy. Usually this class can be encountered when new types are introduced in R or if a given engine doesn't support all data types. Usually REXPUnknown can only be retrieved from the engine but never assigned. */
+public class REXPUnknown extends REXP {
+       /** type of the unterlying obejct */
+       int type;
+
+       /** creates a new unknown object of the given type
+        *  @param type internal R type code of this object */
+       public REXPUnknown(int type) { super(); this.type=type; }
+
+       /** creates a new unknown object of the given type
+        *  @param type internal R type code of this object
+        *  @param attr attributes */
+       public REXPUnknown(int type, REXPList attr) { super(attr); this.type=type; }
+       
+       /** returns the internal R type of this unknown obejct
+        *  @return type code */
+       public int getType() { return type; }
+       
+       public String toString() {
+               return super.toString()+"["+type+"]";
+       }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPVector.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPVector.java
new file mode 100644 (file)
index 0000000..e8bde03
--- /dev/null
@@ -0,0 +1,31 @@
+package org.rosuda.REngine;
+
+/** abstract class representing all vectors in R */
+public abstract class REXPVector extends REXP {
+       public REXPVector() { super(); }
+       
+       public REXPVector(REXPList attr) {
+               super(attr);
+       }
+
+       /** returns the length of the vector (i.e. the number of elements)
+        *  @return length of the vector */
+       public abstract int length();
+
+       public boolean isVector() { return true; }
+
+       /** returns a boolean vector of the same length as this vector with <code>true</code> for NA values and <code>false</code> for any other values
+        *  @return a boolean vector of the same length as this vector with <code>true</code> for NA values and <code>false</code> for any other values */
+       public boolean[] isNA() {
+               boolean a[] = new boolean[length()];
+               return a;
+       }
+       
+       public String toString() {
+               return super.toString()+"["+length()+"]";
+       }
+       
+       public String toDebugString() {
+               return super.toDebugString()+"["+length()+"]";
+       }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REXPWrapper.java b/org.simantics.r.scl/src/org/rosuda/REngine/REXPWrapper.java
new file mode 100644 (file)
index 0000000..451026b
--- /dev/null
@@ -0,0 +1,282 @@
+package org.rosuda.REngine ;
+
+/**
+ * Utility class to wrap an Object into a REXP object. 
+ *
+ * This facilitates wrapping native java objects and arrays 
+ * into REXP objects that can be pushed to R
+ *
+ * @author Romain Francois <francoisromain@free.fr>
+ */
+public class REXPWrapper {
+
+       /* various classes */
+       private static Class byte_ARRAY ;
+       private static Class short_ARRAY ;
+       private static Class int_ARRAY ;
+       private static Class long_ARRAY ;
+       private static Class float_ARRAY ;
+       private static Class double_ARRAY ;
+       private static Class boolean_ARRAY ;
+       
+       private static Class String_ARRAY ;
+       
+       private static Class Byte_ARRAY ;
+       private static Class Short_ARRAY;
+       private static Class Integer_ARRAY ;
+       private static Class Long_ARRAY ;
+       private static Class Float_ARRAY ;
+       private static Class Double_ARRAY ;
+       private static Class Boolean_ARRAY ;
+               
+       static{
+               try{
+                       byte_ARRAY     = Class.forName("[B") ;
+                       short_ARRAY    = Class.forName("[S" ); 
+                       int_ARRAY      = Class.forName("[I" ); 
+                       long_ARRAY     = (new long[1]).getClass() ; /* FIXME */
+                       float_ARRAY    = Class.forName("[F" ) ;
+                       double_ARRAY   = Class.forName("[D" );
+                       boolean_ARRAY  = Class.forName("[Z" ) ;
+                       
+                       String_ARRAY   = Class.forName( "[Ljava.lang.String;") ;
+                       
+                       Byte_ARRAY     = Class.forName( "[Ljava.lang.Byte;" ) ;
+                       Short_ARRAY    = Class.forName( "[Ljava.lang.Short;" ) ;
+                       Integer_ARRAY  = Class.forName( "[Ljava.lang.Integer;" ) ;
+                       Long_ARRAY     = Class.forName( "[Ljava.lang.Long;" ) ;
+                       Float_ARRAY    = Class.forName( "[Ljava.lang.Float;" ) ;
+                       Double_ARRAY   = Class.forName( "[Ljava.lang.Double;" ) ;
+                       Boolean_ARRAY  = Class.forName( "[Ljava.lang.Boolean;" ) ;
+                       
+                       
+               } catch( Exception e){
+                       // should never happen
+                       e.printStackTrace(); 
+                       System.err.println( "problem while initiating the classes" ) ;
+               }
+       }
+       
+       /**
+        * Wraps an Object into a REXP
+        *
+        * <p>Conversion :</p>
+        *
+        * <ul>
+        * <li>Byte (byte) : REXPRaw </li>
+        * <li>Short (short) : REXPInteger </li>
+        * <li>Integer (int) : REXPInteger </li>
+        * <li>Long (long) : REXPInteger</li>
+        * <li>Float (float) : REXPDouble</li>
+        * <li>Double (double) : REXPDouble </li>
+        * <li>Boolean (boolean) : REXPLogical</li>
+        * <li>--</li>
+        * <li>String : REXPString </li>
+        * <li>String[] : REXPString </li>
+        * <li>--</li>
+        * <li>byte[] or Byte[] : REXPRaw</li>
+        * <li>short[] or Short[] : REXPInteger</li>
+        * <li>int[] or Integer[] : REXPInteger</li>
+        * <li>long[] or Long[] : REXPInteger</li>
+        * <li>float[] or Float[] : REXPDouble</li>
+        * <li>double[] or Double[] : REXPDouble </li>
+        * <li>boolean[] or Boolean[]: REXPLogical</li>
+        * <li>--</li>
+        * <li>null for anything else</li>
+        * </ul>
+        * 
+        * @param o object to wrap
+        * @return REXP object that represents o or null if the conversion is not possible
+        */
+       public static REXP wrap( Object o ) {
+               
+               /* nothing to do in that case */
+               if( o instanceof REXP){
+                       return (REXP)o; 
+               } 
+               
+               Class clazz = o.getClass() ;
+               
+               /* primitives */
+               
+               if( clazz == Byte.class ){
+                       byte[] load = new byte[1]; 
+                       load[0] = ((Byte)o).byteValue() ;
+                       return new REXPRaw( load ); 
+               } 
+               
+               if( clazz == Short.class ){
+                       return new REXPInteger( ((Short)o).intValue() ) ;
+               } 
+               
+               if( clazz == Integer.class ){
+                       return new REXPInteger( ((Integer)o).intValue() ) ;
+               } 
+               
+               if( clazz == Long.class ){
+                       return new REXPInteger( ((Long)o).intValue() ) ;
+               } 
+               
+               if( clazz == Float.class ){
+                       return new REXPDouble( ((Float)o).doubleValue() ) ;
+               }
+               
+               if( clazz == Double.class ){
+                       return new REXPDouble( ((Double)o).doubleValue() ) ;
+               }
+               
+               if( clazz == Boolean.class ){
+                       return new REXPLogical( ((Boolean)o).booleanValue() ) ;
+               }
+               
+               
+               /* Strings -> REXPString */
+               
+               if( clazz == String.class ){
+                       return new REXPString( (String)o ) ;
+               } 
+               
+               if( clazz == String_ARRAY ){ /* String[] */
+                       return new REXPString( (String[])o ); 
+               } 
+               
+               /* array of byte or Bytes -> REXPRaw */
+               
+               if( clazz == byte_ARRAY ){ /* byte[] */
+                       return new REXPRaw( (byte[])o ) ; 
+               } 
+               
+               if( clazz == Byte_ARRAY ){ /* Byte[] */
+                       Byte[] b = (Byte[])o;
+                       int n = b.length ;
+                       byte[] bytes = new byte[b.length];
+                       for( int i=0; i<n; i++){
+                               bytes[i] = b[i].byteValue() ;
+                       }
+                       return new REXPRaw( bytes ); 
+               }
+               
+               /* arrays of short or Short  -> REXPInteger */ 
+               
+               if( clazz == short_ARRAY ){ /* short[] */
+                       short[] shorts = (short[])o ;
+                       int[] ints = new int[ shorts.length ];
+                       int n = ints.length; 
+                       for( int i=0; i<n; i++ ){
+                               ints[i] = shorts[i]; 
+                       }
+                       return new REXPInteger( ints ) ;
+               }
+               
+               if( clazz == Short_ARRAY ){ /* Short[] */
+                       Short[] shorts = (Short[])o;
+                       int n = shorts.length ;
+                       int[] ints = new int[shorts.length];
+                       for( int i=0; i<n; i++){
+                               ints[i] = shorts[i].intValue() ;
+                       }
+                       return new REXPInteger( ints ); 
+               } 
+               
+               
+               /* arrays of int or Integer ->  REXPInteger */
+               
+               if( clazz == int_ARRAY ){ /* int[] */
+                       return new REXPInteger( (int[])o ) ; 
+               } 
+               
+               if( clazz == Integer_ARRAY ){ /* Integer[] */
+                       Integer[] integers = (Integer[])o;
+                       int n = integers.length ;
+                       int[] ints = new int[integers.length];
+                       for( int i=0; i<n; i++){
+                               ints[i] = integers[i].intValue() ;
+                       }
+                       return new REXPInteger( ints ); 
+               } 
+               
+               /* arrays of long or Long -> REXPInteger */
+               
+               if( clazz == long_ARRAY ){ /* long[] */
+                       long[] longs = (long[])o;
+                       int n = longs.length ;
+                       int[] ints = new int[longs.length];
+                       for( int i=0; i<n; i++){
+                               ints[i] = (int)longs[i] ;
+                       }
+                       return new REXPInteger( ints ); 
+               } 
+               
+               if( clazz == Long_ARRAY ){ /* Long[] */
+                       Long[] longs = (Long[])o;
+                       int n = longs.length ;
+                       int[] ints = new int[longs.length];
+                       for( int i=0; i<n; i++){
+                               ints[i] = longs[i].intValue() ;
+                       }
+                       return new REXPInteger( ints ); 
+               } 
+               
+               /* float or Float arrays -> REXPDouble */
+               
+               if( clazz == float_ARRAY ){ /* float[] */
+                       float[] floats = (float[])o;
+                       int n = floats.length ;
+                       double[] doubles = new double[floats.length];
+                       for( int i=0; i<n; i++){
+                               doubles[i] = (double)floats[i] ;
+                       }
+                       return new REXPDouble( doubles ); 
+               } 
+               
+               if( clazz == Float_ARRAY ){ /* Float[] */
+                       Float[] floats = (Float[])o;
+                       int n = floats.length ;
+                       double[] doubles = new double[floats.length];
+                       for( int i=0; i<n; i++){
+                               doubles[i] = floats[i].doubleValue() ;
+                       }
+                       return new REXPDouble( doubles ); 
+               }
+               
+               
+               /* double or Double arrays -> REXPDouble */
+               
+               if(clazz == double_ARRAY ) { /* double[] */
+                       return new REXPDouble( (double[])o ) ;
+               }
+               
+               if( clazz == Double_ARRAY ){ /* Double[] */
+                       Double[] doubles = (Double[])o;
+                       double n = doubles.length ;
+                       double[] d = new double[doubles.length];
+                       for( int i=0; i<n; i++){
+                               d[i] = doubles[i].doubleValue() ;
+                       }
+                       return new REXPDouble( d ); 
+               } 
+               
+               
+               /* boolean arrays -> REXPLogical */
+               
+               if( clazz == boolean_ARRAY ){ /* boolean[] */
+                       return new REXPLogical( (boolean[])o ) ; 
+               } 
+               
+               if( clazz == Boolean_ARRAY ){ /* Boolean[] */
+                       Boolean[] booleans = (Boolean[])o;
+                       int n = booleans.length ;
+                       boolean[] b = new boolean[booleans.length];
+                       for( int i=0; i<n; i++){
+                               b[i] = booleans[i].booleanValue() ;
+                       }
+                       return new REXPLogical( b ); 
+               } 
+               
+               /* give up and return null */
+               
+               return null ; 
+       }
+       
+}
+
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REngine.java b/org.simantics.r.scl/src/org/rosuda/REngine/REngine.java
new file mode 100644 (file)
index 0000000..d9fb32d
--- /dev/null
@@ -0,0 +1,177 @@
+package org.rosuda.REngine;
+
+import java.lang.reflect.Method;
+
+/** REngine is an abstract base class for all implementations of R engines. Subclasses can implement interfaces to R in many different ways.
+ Clients should always use methods this class instead of its direct subclasses in order to maintian compatibility with any R engine implementation.
+ The canonical way of obtaining a new engine is to call {@link #engineForClass}. All subclasses must implement <code>createEngine()</code> method. */
+public abstract class REngine {
+    /** last created engine or <code>null</code> if there is none */
+    protected static REngine lastEngine = null;
+       
+    /** this is the designated constructor for REngine classes. It uses reflection to call createEngine method on the given REngine class.
+       @param klass fully qualified class-name of a REngine implementation
+       @return REngine implementation or <code>null</code> if <code>createEngine</code> invokation failed */
+    public static REngine engineForClass(String klass) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, java.lang.reflect.InvocationTargetException {
+               Class cl=Class.forName(klass);
+               if (cl==null) throw(new ClassNotFoundException("can't find engine class "+klass));
+               Method m=cl.getMethod("createEngine",(Class[])null);
+               Object o=m.invoke(null,(Object[])null);
+               return lastEngine=(REngine)o;
+    }
+
+       /** This is the extended constructor for REngine classes. It uses reflection to call createEngine method on the given REngine class with some additional control over the engine. Note that not all engines may support the extended version.
+        @param klass fully qualified class-name of a REngine implementation
+        @param args arguments to pass to R for initialization
+        @param callbacks delegate for REngine callbacks or <code>null</code> if callbacks won't be serviced (engine may not support callbacks)
+        @param runREPL if <code>true</code> then REPL will be started (if supported by the engine)
+        @return REngine implementation or <code>null</code> if <code>createEngine</code> invokation failed */
+       public static REngine engineForClass(String klass, String[] args, REngineCallbacks callbacks, boolean runREPL) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, java.lang.reflect.InvocationTargetException {
+               Class cl = Class.forName(klass);
+               if (cl == null) throw new ClassNotFoundException("can't find engine class " + klass);
+               Method m = cl.getMethod("createEngine", new Class[] { String[].class, REngineCallbacks.class, Boolean.TYPE });
+               Object o = m.invoke(null, new Object[] { args, callbacks, new Boolean(runREPL) });
+               return lastEngine = (REngine)o;
+       }
+       
+    /** retrieve the last created engine
+               @return last created engine or <code>null</code> if no engine was created yet */
+    public static REngine getLastEngine() {
+               return lastEngine;
+    }
+
+       /** parse a string into an expression vector
+               @param text string to parse
+               @param resolve resolve the resulting REXP (<code>true</code>) or just return a reference (<code>false</code>)
+           @return parsed expression */
+       public abstract REXP parse(String text, boolean resolve) throws REngineException;
+
+       /** evaluate an expression vector
+               @param what an expression (or vector of such) to evaluate
+               @param where environment to evaluate in (use <code>null</code> for the global environemnt and/or if environments are not supported by the engine)
+               @param resolve resolve the resulting REXP or just return a reference
+               @return the result of the evaluation of the last expression */
+    public abstract REXP eval(REXP what, REXP where, boolean resolve) throws REngineException, REXPMismatchException;
+
+       /** assign into an environment
+               @param symbol symbol name
+               @param value value to assign
+               @param env environment to assign to (use <code>null</code> for the global environemnt and/or if environments are not supported by the engine) */
+    public abstract void assign(String symbol, REXP value, REXP env) throws REngineException, REXPMismatchException;
+
+       /** get a value from an environment
+               @param symbol symbol name
+               @param env environment (use <code>null</code> for the global environemnt and/or if environments are not supported by the engine)
+               @param resolve resolve the resulting REXP or just return a reference            
+               @return value */
+    public abstract REXP get(String symbol, REXP env, boolean resolve) throws REngineException, REXPMismatchException;
+
+       /** fetch the contents of the given reference. The resulting REXP may never be REXPReference. The engine should raise a {@link #REngineException} exception if {@link #supportsReferences()} returns <code>false</code>.
+               @param ref reference to resolve
+               @return resolved reference */
+       public abstract REXP resolveReference(REXP ref) throws REngineException, REXPMismatchException;
+
+       /** create a reference by pushing local data to R and returning a reference to the data. If ref is a reference it is returned as-is. The engine should raise a {@link #REngineException} exception if {@link #supportsReferences()} returns <code>false</code>.
+        @param value to create reference to
+        @return reference to the value */
+       public abstract REXP createReference(REXP value) throws REngineException, REXPMismatchException;
+
+       /** removes reference from the R side. This method is called automatically by the finalizer of <code>REXPReference</code> and should never be called directly.
+        @param ref reference to finalize */
+       public abstract void finalizeReference(REXP ref) throws REngineException, REXPMismatchException;
+       
+       /** get the parent environemnt of an environemnt
+        @param env environment to query
+        @param resolve whether to resolve the resulting environment reference
+        @return parent environemnt of env */
+       public abstract REXP getParentEnvironment(REXP env, boolean resolve) throws REngineException, REXPMismatchException;
+       
+       /** create a new environemnt
+        @param parent parent environment
+        @param resolve whether to resolve the reference to the environemnt (usually <code>false</code> since the returned environment will be empty)
+        @return resulting environment */
+       public abstract REXP newEnvironment(REXP parent, boolean resolve) throws REngineException, REXPMismatchException;
+       
+    /** convenince method equivalent to <code>eval(parse(text, false), where, resolve);</code>
+        @param text to parse (see {@link #parse})
+        @param where environment to evaluate in (see {@link #eval})
+        @param resolve whether to resolve the resulting reference or not (see {@link #eval})
+        @return result */
+       public REXP parseAndEval(String text, REXP where, boolean resolve) throws REngineException, REXPMismatchException {
+               REXP p = parse(text, false);
+               return eval(p, where, resolve);
+       }
+
+    /** convenince method equivalent to <code>eval(parse(cmd, false), null, true);</code>
+        @param cmd expression to parse (see {@link #parse})
+        @return result */
+    public REXP parseAndEval(String cmd) throws REngineException, REXPMismatchException { return parseAndEval(cmd, null, true); };
+       
+       /** performs a close operation on engines that support it. The engine may not be used after <code>close()</code> returned <code>true</code>. This operation is optional and will always return <code>false</code> if not implemented.
+        @return <code>true</code> if the close opetaion was successful, <code>false</code> otherwise. */
+       public boolean close() { return false; }
+       
+       //--- capabilities ---
+       /** check whether this engine supports references to R objects
+        @return <code>true</code> if this engine supports references, <code>false/code> otherwise */
+       public boolean supportsReferences() { return false; }
+       /** check whether this engine supports handing of environments (if not, {@link #eval} and {@link #assign} only support the global environment denoted by <code>null</code>).
+        @return <code>true</code> if this engine supports environments, <code>false/code> otherwise */
+       public boolean supportsEnvironments() { return false; }
+       /** check whether this engine supports REPL (Read-Evaluate-Print-Loop) and corresponding callbacks.
+        @return <code>true</code> if this engine supports REPL, <code>false/code> otherwise */
+       public boolean supportsREPL() { return false; }
+       /** check whether this engine supports locking ({@link #lock}, {@link #tryLock} and {@link #unlock}).
+        @return <code>true</code> if this engine supports REPL, <code>false/code> otherwise */
+       public boolean supportsLocking() { return false; }
+
+       //--- convenience methods --- (the REXPMismatchException catches should be no-ops since the value type is guaranteed in the call to assign)
+       /** convenience method equivalent to <code>assign(symbol, new REXPDouble(d), null)</code> (see {@link #assign(String, REXP, REXP)})
+        @param symbol symbol name to assign to
+        @param d values to assign */
+       public void assign(String symbol, double[] d) throws REngineException { try { assign(symbol, new REXPDouble(d), null); } catch (REXPMismatchException e) { throw(new REngineException(this, "REXPMismatchException in assign(,double[]): "+e)); } }
+       /** convenience method equivalent to <code>assign(symbol, new REXPInteger(d), null)</code> (see {@link #assign(String, REXP, REXP)})
+        @param symbol symbol name to assign to
+        @param d values to assign */
+       public void assign(String symbol, int[] d) throws REngineException { try { assign(symbol, new REXPInteger(d), null); } catch (REXPMismatchException e) { throw(new REngineException(this, "REXPMismatchException in assign(,int[]): "+e)); } }
+       /** convenience method equivalent to <code>assign(symbol, new REXPString(d), null)</code> (see {@link #assign(String, REXP, REXP)})
+        @param symbol symbol name to assign to
+        @param d values to assign */
+       public void assign(String symbol, String[] d) throws REngineException { try { assign(symbol, new REXPString(d), null); } catch (REXPMismatchException e) { throw(new REngineException(this, "REXPMismatchException in assign(,String[]): "+e)); } }
+       /** convenience method equivalent to <code>assign(symbol, new REXPRaw(d), null)</code> (see {@link #assign(String, REXP, REXP)})
+        @param symbol symbol name to assign to
+        @param d values to assign */
+       public void assign(String symbol, byte[] d) throws REngineException { try { assign(symbol, new REXPRaw(d), null); } catch (REXPMismatchException e) { throw(new REngineException(this, "REXPMismatchException in assign(,byte[]): "+e)); } }
+       /** convenience method equivalent to <code>assign(symbol, new REXPString(d), null)</code> (see {@link #assign(String, REXP, REXP)})
+        @param symbol symbol name to assign to
+        @param d value to assign */
+       public void assign(String symbol, String d) throws REngineException { try { assign(symbol, new REXPString(d), null); } catch (REXPMismatchException e) { throw(new REngineException(this, "REXPMismatchException in assign(,String[]): "+e)); } }
+       /** convenience method equivalent to <code>assign(symbol, value, null)</code> (see {@link #assign(String, REXP, REXP)})
+        @param symbol symbol name to assign to
+        @param value values to assign */
+       public void assign(String symbol, REXP value) throws REngineException, REXPMismatchException { assign(symbol, value, null); }
+       
+       //--- locking API ---
+       /** attempts to obtain a lock for this R engine synchronously (without waiting for it).
+           <br>Note: check for {@link #supportsLocking()} before relying on this capability. If not implemented, always returns 0.
+        @return 0 if the lock could not be obtained (R engine is busy) and some other value otherwise -- the returned value must be used in a matching call to {@link #unlock(int)}. */
+       public synchronized int tryLock() { return 0; }
+
+       /** obtains a lock for this R engine, waiting until it becomes available.
+        <br>Note: check for {@link #supportsLocking()} before relying on this capability. If not implemented, always returns 0.
+        @return value that must be passed to {@link #unlock} in order to release the lock */
+       public synchronized int lock() { return 0; }
+
+       /** releases a lock previously obtained by {@link #lock()} or {@link #tryLock()}.
+        <br>Note: check for {@link #supportsLocking()} before relying on this capability.  If not implemented, has no effect.
+        @param lockValue value returned by {@link #lock()} or {@link #tryLock()}. */    
+       public synchronized void unlock(int lockValue) {}
+
+       public String toString() {
+               return super.toString()+((lastEngine==this)?"{last}":"");
+       }
+       
+       public REXP wrap(Object o){
+               return REXPWrapper.wrap(o); 
+       }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REngineCallbacks.java b/org.simantics.r.scl/src/org/rosuda/REngine/REngineCallbacks.java
new file mode 100644 (file)
index 0000000..e974253
--- /dev/null
@@ -0,0 +1,5 @@
+package org.rosuda.REngine;
+
+/** REngineCallbacks is a virtual interface that poses as a superclass of all callback delegate classes. */
+public interface REngineCallbacks {
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REngineConsoleHistoryInterface.java b/org.simantics.r.scl/src/org/rosuda/REngine/REngineConsoleHistoryInterface.java
new file mode 100644 (file)
index 0000000..e37765f
--- /dev/null
@@ -0,0 +1,16 @@
+package org.rosuda.REngine;
+
+/** interface defining delegate methods used by {@link REngine} to forward console history callbacks from R. */
+public interface REngineConsoleHistoryInterface {
+       /** called when R wants to save the history content.
+        *  @param eng calling engine
+        *  @param filename name of the file to save command history to
+        */
+       public void   RSaveHistory  (REngine eng, String filename);     
+       
+       /** called when R wants to load the history content.
+        *  @param eng calling engine
+        *  @param filename name of the file to load the command history from
+        */
+       public void   RLoadHistory  (REngine eng, String filename);
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REngineEvalException.java b/org.simantics.r.scl/src/org/rosuda/REngine/REngineEvalException.java
new file mode 100644 (file)
index 0000000..09c4c87
--- /dev/null
@@ -0,0 +1,59 @@
+package org.rosuda.REngine ;
+
+/**
+ * Exception thrown when an error occurs during eval. 
+ * 
+ * <p>
+ * This class is a placeholder and should be extended when more information
+ * can be extracted from R (call stack, etc ... )
+ * </p>
+ */
+public class REngineEvalException extends REngineException {
+       
+       /**
+        * Value returned by the rniEval native method when the input passed to eval
+        * is invalid
+        */ 
+       public static final int INVALID_INPUT = -1 ;
+       
+       /**
+        * Value returned by the rniEval native method when an error occured during 
+        * eval (stop, ...)
+        */
+       public static final int ERROR = -2 ;  
+
+       /**
+        * Type of eval error
+        */
+       protected int type ; 
+       
+       /**
+        * Constructor
+        *
+        * @param eng associated REngine
+        * @param message error message
+        * @param type type of error (ERROR or INVALID_INPUT)
+        */
+       public REngineEvalException( REngine eng, String message, int type ){
+               super( eng, message );
+               this.type = type ;
+       }
+       
+       /**
+        * Constructor using ERROR type
+        *
+        * @param eng associated REngine
+        * @param message error message
+        */
+       public REngineEvalException( REngine eng, String message){
+               this( eng, message, ERROR );
+       }
+       
+       /**
+        * @return the type of error (ERROR or INVALID_INPUT)
+        */
+       public int getType(){
+               return type ;
+       }
+       
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REngineException.java b/org.simantics.r.scl/src/org/rosuda/REngine/REngineException.java
new file mode 100644 (file)
index 0000000..475fbee
--- /dev/null
@@ -0,0 +1,31 @@
+// REngine - generic Java/R API
+//
+// Copyright (C) 2006 Simon Urbanek
+// --- for licensing information see LICENSE file in the original JRclient distribution ---
+//
+//  RSrvException.java
+//
+//  Created by Simon Urbanek on Wed Jun 21 2006.
+//
+//  $Id$
+//
+
+package org.rosuda.REngine;
+
+/** <code>REngineException</code> is a generic exception that can be thrown by methods invoked on an R engine. */
+public class REngineException extends Exception {
+       /** engine associated with this exception */
+    protected REngine engine;
+
+       /** creates an R engine exception
+        @param engine engine associated with this exception
+        @param msg message describing the cause */
+    public REngineException(REngine engine, String msg) {
+        super(msg);
+        this.engine = engine;
+    }
+       /** returns the engine associated with this exception
+        @return engine associated with this exception */
+       public REngine getEngine() { return engine; }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REngineInputInterface.java b/org.simantics.r.scl/src/org/rosuda/REngine/REngineInputInterface.java
new file mode 100644 (file)
index 0000000..4a7894e
--- /dev/null
@@ -0,0 +1,13 @@
+package org.rosuda.REngine;
+
+/** interface defining delegate methods used by {@link REngine} to forward input callbacks from R. */
+public interface REngineInputInterface {
+       /** called when R enters the read stage of the event loop.
+        *  <p> Important: implementations should never use a direct return! That will cause a tigh-spinning event loop. Implementation must wait for input asynchronously (e.g., declare synchonized RReadConsole and use wait()) and return only when a complete line is available for processing.
+        *  @param eng calling engine
+        *  @param prompt prompt to display in the console
+        *  @param addToHistory flag indicating whether the input is transient (<code>false</code>) or to be recorded in the command history (<code>true</code>).
+        *  @return string to be processed as console input
+        */
+       public String RReadConsole(REngine eng, String prompt, int addToHistory);
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REngineOutputInterface.java b/org.simantics.r.scl/src/org/rosuda/REngine/REngineOutputInterface.java
new file mode 100644 (file)
index 0000000..42b85bd
--- /dev/null
@@ -0,0 +1,21 @@
+package org.rosuda.REngine;
+
+/** interface defining delegate methods used by {@link REngine} to forward output callbacks from R. */
+public interface REngineOutputInterface {
+       /** called when R prints output to the console.
+        *  @param eng calling engine
+        *  @param text text to display in the console
+        *  @param oType output type (0=regular, 1=error/warning)
+        */
+       public void RWriteConsole(REngine eng, String text, int oType);
+       
+       /** called when R wants to show a warning/error message box (not console-related).
+        *  @param eng calling engine
+        *  @param text text to display in the message
+        */
+       public void RShowMessage(REngine eng, String text);
+       
+       /** called by R to flush (display) any pending console output.
+        *  @param eng calling engine */
+       public void RFlushConsole(REngine eng);
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REngineStdOutput.java b/org.simantics.r.scl/src/org/rosuda/REngine/REngineStdOutput.java
new file mode 100644 (file)
index 0000000..9b212de
--- /dev/null
@@ -0,0 +1,15 @@
+package org.rosuda.REngine;
+
+/** implementation of the {@link REngineOutputInterface} which uses standard output. */
+public class REngineStdOutput implements REngineCallbacks, REngineOutputInterface {
+       public synchronized void RWriteConsole(REngine eng, String text, int oType) {
+               ((oType == 0) ? System.out : System.err).print(text);
+       }
+       
+       public void RShowMessage(REngine eng, String text) {
+               System.err.println("*** "+text);
+       }
+       
+       public void RFlushConsole(REngine eng) {
+       }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/REngineUIInterface.java b/org.simantics.r.scl/src/org/rosuda/REngine/REngineUIInterface.java
new file mode 100644 (file)
index 0000000..0bc0fdc
--- /dev/null
@@ -0,0 +1,17 @@
+package org.rosuda.REngine;
+
+/** interface defining delegate methods used by {@link REngine} to forward user interface callbacks from R. */
+public interface REngineUIInterface {
+       /** called when the busy state of R changes - usual response is to change the shape of the cursor
+        *  @param eng calling engine
+        *  @param state busy state of R (0 = not busy)
+        */
+       public void   RBusyState  (REngine eng, int state);     
+       
+       /** called when R wants the user to choose a file.
+        *  @param eng calling engine
+        *  @param newFile if <code>true</code> then the user can specify a non-existing file to be created, otherwise an existing file must be selected.
+        *  @return full path and name of the selected file or <code>null</code> if the selection was cancelled.
+        */
+       public String RChooseFile  (REngine eng, boolean newFile);
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/RFactor.java b/org.simantics.r.scl/src/org/rosuda/REngine/RFactor.java
new file mode 100644 (file)
index 0000000..ca95997
--- /dev/null
@@ -0,0 +1,224 @@
+package org.rosuda.REngine;
+
+// REngine
+// Copyright (C) 2007 Simon Urbanek
+// --- for licensing information see LICENSE file in the original distribution ---
+
+import java.util.*;
+
+/** representation of a factor variable. In R there is no actual object
+    type called "factor", instead it is coded as an int vector with a list
+    attribute. The parser code of REXP converts such constructs directly into
+    the RFactor objects and defines an own XT_FACTOR type 
+    
+    @version $Id$
+*/    
+public class RFactor {
+    int ids[];
+    String levels[];
+       int index_base;
+
+    /** create a new, empty factor var */
+    public RFactor() { ids=new int[0]; levels=new String[0]; }
+    
+    /** create a new factor variable, based on the supplied arrays.
+               @param i array of IDs (inde_base..v.length+index_base-1)
+               @param v values - cotegory names
+               @param copy copy above vaules or just retain them
+               @param index_base index of the first level element (1 for R factors, cannot be negtive)
+               */
+    public RFactor(int[] i, String[] v, boolean copy, int index_base) {
+               if (i==null) i = new int[0];
+               if (v==null) v = new String[0];
+               if (copy) {
+                       ids=new int[i.length]; System.arraycopy(i,0,ids,0,i.length);
+                       levels=new String[v.length]; System.arraycopy(v,0,levels,0,v.length);
+               } else {
+                       ids=i; levels=v;
+               }
+               this.index_base = index_base;
+    }
+
+       /** create a new factor variable by factorizing a given string array. The levels will be created in the orer of appearance.
+               @param c contents
+               @param index_base base of the level index */
+       public RFactor(String c[], int index_base) {
+               this.index_base = index_base;
+               if (c == null) c = new String[0];
+               Vector lv = new Vector();
+               ids = new int[c.length];
+               int i = 0;
+               while (i < c.length) {
+                       int ix = (c[i]==null)?-1:lv.indexOf(c[i]);
+                       if (ix<0 && c[i]!=null) {
+                               ix = lv.size();
+                               lv.add(c[i]);
+                       }
+                       ids[i] = (ix<0)?REXPInteger.NA:(ix+index_base);
+                       i++;
+               }
+               levels = new String[lv.size()];
+               i = 0;
+               while (i < levels.length) {
+                       levels[i] = (String) lv.elementAt(i);
+                       i++;
+               }
+       }
+       
+       /** same as <code>RFactor(c, 1)</code> */
+       public RFactor(String c[]) {
+               this(c, 1);
+       }
+       
+       /** same as <code>RFactor(i,v, true, 1)</code> */
+       public RFactor(int[] i, String[] v) {
+               this(i, v, true, 1);
+       }
+       
+    /** returns the level of a given case
+               @param i case number
+               @return name. may throw exception if out of range */
+    public String at(int i) {
+               int li = ids[i] - index_base;
+               return (li<0||li>levels.length)?null:levels[li];
+    }
+
+       /** returns <code>true</code> if the data contain the given level index */
+       public boolean contains(int li) {
+               int i = 0;
+               while (i < ids.length) {
+                       if (ids[i] == li) return true;
+                       i++;
+               }
+               return false;
+       }
+       
+       /** return <code>true</code> if the factor contains the given level (it is NOT the same as levelIndex==-1!) */
+       public boolean contains(String name) {
+               int li = levelIndex(name);
+               if (li<0) return false;
+               int i = 0;
+               while (i < ids.length) {
+                       if (ids[i] == li) return true;
+                       i++;
+               }
+               return false;
+       }
+       
+       /** count the number of occurences of a given level index */
+       public int count(int levelIndex) {
+               int i = 0;
+               int ct = 0;
+               while (i < ids.length) {
+                       if (ids[i] == levelIndex) ct++;
+                       i++;
+               }
+               return ct;
+       }
+       
+       /** count the number of occurences of a given level name */
+       public int count(String name) {
+               return count(levelIndex(name));
+       }
+       
+       /** return an array with level counts. */
+       public int[] counts() {
+               int[] c = new int[levels.length];
+               int i = 0;
+               while (i < ids.length) {
+                       final int li = ids[i] - index_base;
+                       if (li>=0 && li<levels.length)
+                               c[li]++;
+                       i++;
+               }
+               return c;
+       }
+       
+       /** return the index of a given level name or -1 if it doesn't exist */
+       public int levelIndex(String name) {
+               if (name==null) return -1;
+               int i = 0;
+               while (i < levels.length) {
+                       if (levels[i]!=null && levels[i].equals(name)) return i + index_base;
+                       i++;
+               }
+               return -1;
+       }
+       
+       /** return the list of levels (0-based, use {@link #indexBase} correction if you want to access it by level index) */
+       public String[] levels() {
+               return levels;
+       }
+       
+       /** return the contents as integer indices (with the index base of this factor) */
+       public int[] asIntegers() {
+               return ids;
+       }
+       
+       /** return the contents as integer indices with a given index base */
+       public int[] asIntegers(int desired_index_base) {
+               if (desired_index_base == index_base) return ids;
+               int[] ix = new int[ids.length];
+               int j = 0; while (j < ids.length) { ix[j] = ids[j] - index_base + desired_index_base; j++; }
+               return ix;
+       }
+       
+       /** return the level name for a given level index */
+       public String levelAtIndex(int li) {
+               li -= index_base;
+               return (li<0||li>levels.length)?null:levels[li];
+       }
+       
+       /** return the level index for a given case */
+       public int indexAt(int i) {
+               return ids[i];
+       }
+       
+       /** return the factor as an array of strings */
+       public String[] asStrings() {
+               String[] s = new String[ids.length];
+               int i = 0;
+               while (i < ids.length) {
+                       s[i] = at(i);
+                       i++;
+               }
+               return s;       
+       }
+       
+       /** return the base of the levels index */
+       public int indexBase() {
+               return index_base;
+       }
+       
+    /** returns the number of cases */
+    public int size() { return ids.length; }
+
+       public String toString() {
+               return super.toString()+"["+ids.length+","+levels.length+",#"+index_base+"]";
+       }
+       
+    /** displayable representation of the factor variable
+    public String toString() {
+       //return "{"+((val==null)?"<null>;":("levels="+val.size()+";"))+((id==null)?"<null>":("cases="+id.size()))+"}";
+       StringBuffer sb=new StringBuffer("{levels=(");
+       if (val==null)
+           sb.append("null");
+       else
+           for (int i=0;i<val.size();i++) {
+               sb.append((i>0)?",\"":"\"");
+               sb.append((String)val.elementAt(i));
+               sb.append("\"");
+           };
+       sb.append("),ids=(");
+       if (id==null)
+           sb.append("null");
+       else
+           for (int i=0;i<id.size();i++) {
+               if (i>0) sb.append(",");
+               sb.append((Integer)id.elementAt(i));
+           };
+       sb.append(")}");
+       return sb.toString();
+    } */
+}
+
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/RList.java b/org.simantics.r.scl/src/org/rosuda/REngine/RList.java
new file mode 100644 (file)
index 0000000..bedaede
--- /dev/null
@@ -0,0 +1,333 @@
+package org.rosuda.REngine;
+
+// REngine library - Java client interface to R
+// Copyright (C) 2004,2007,2008 Simon Urbanek
+
+import java.util.*;
+
+/** implementation of R-lists<br>
+    All lists (dotted-pair lists, language lists, expressions and vectors) are regarded as named generic vectors. 
+    Note: This implementation has changed radically in Rserve 0.5!
+
+    This class inofficially implements the Map interface. Unfortunately a conflict in the Java iterface classes Map and List doesn't allow us to implement both officially. Most prominently the Map 'remove' method had to be renamed to removeByKey.
+
+    @version $Id$
+*/
+public class RList extends Vector implements List {
+    public Vector names;
+
+    /** constructs an empty list */
+    public RList() { super(); names=null; }
+
+    /** constructs an initialized, unnamed list
+        * @param contents - an array of {@link REXP}s to use as contents of this list */
+    public RList(REXP[] contents) {
+       super(contents.length);
+       int i=0;
+       while (i<contents.length)
+           super.add(contents[i++]);
+       names=null;
+    }
+
+    public RList(int initSize, boolean hasNames) {
+       super(initSize);
+       names=null;
+       if (hasNames) names=new Vector(initSize);
+    }
+    
+    /** constructs an initialized, unnamed list
+        * @param contents - a {@link Collection} of {@link REXP}s to use as contents of this list */
+    public RList(Collection contents) {
+       super(contents);
+       names=null;
+    }
+
+    /** constructs an initialized, named list. The length of the contents vector determines the length of the list.
+        * @param contents - an array of {@link REXP}s to use as contents of this list
+        * @param names - an array of {@link String}s to use as names */
+    public RList(REXP[] contents, String[] names) {
+       this(contents);
+       if (names!=null && names.length>0) {
+           this.names=new Vector(names.length);
+           int i = 0;
+           while (i < names.length) this.names.add(names[i++]);
+           while (this.names.size()<size()) this.names.add(null);
+       }
+    }
+    
+    /** constructs an initialized, named list. The size of the contents collection determines the length of the list.
+        * @param contents - a {@link Collection} of {@link REXP}s to use as contents of this list
+        * @param names - an array of {@link String}s to use as names */
+    public RList(Collection contents, String[] names) {
+       this(contents);
+       if (names!=null && names.length>0) {
+           this.names=new Vector(names.length);
+           int i = 0;
+           while (i < names.length) this.names.add(names[i++]);
+           while (this.names.size()<size()) this.names.add(null);
+       }
+    }
+
+    /** constructs an initialized, named list. The size of the contents collection determines the length of the list.
+        * @param contents - a {@link Collection} of {@link REXP}s to use as contents of this list
+        * @param names - an {@link Collection} of {@link String}s to use as names */
+    public RList(Collection contents, Collection names) {
+       this(contents);
+       if (names!=null && names.size()>0) {
+           this.names=new Vector(names);
+           while (this.names.size()<size()) this.names.add(null);
+       }
+    }
+
+       /** checks whether this list is named or unnamed
+        * @return <code>true</code> if this list is named, <code>false</code> otherwise */
+    public boolean isNamed() {
+       return names!=null;
+    }
+
+    /** get xpression given a key
+       @param v key
+       @return value which corresponds to the given key or
+               <code>null</code> if the list is unnamed or key not found */
+    public REXP at(String v) {
+       if (names==null) return null;
+       int i = names.indexOf(v);
+       if (i < 0) return null;
+       return (REXP)elementAt(i);
+    }
+
+    /** get element at the specified position
+       @param i index
+       @return value at the index or <code>null</code> if the index is out of bounds */
+    public REXP at(int i) {
+       return (i>=0 && i<size())?(REXP)elementAt(i):null;
+    }
+
+       /** return the key (name) at a given index
+        @param i index
+        @return ket at the index - can be <code>null</code> is the list is unnamed or the index is out of range */
+    public String keyAt(int i) {
+       return (names==null || i<0 || i>=names.size())?null:(String)names.get(i);
+    }
+
+       /** set key at the given index. Using this method automatically makes the list a named one even if the key is <code>null</code>. Out of range operations are undefined (currently no-ops)
+        @param i index
+        @param value key name */
+       public void setKeyAt(int i, String value) {
+               if (i < 0) return;
+               if (names==null)
+                       names = new Vector();
+               if (names.size() < size()) names.setSize(size());
+               if (i < size()) names.set(i, value);
+       }
+
+    /** returns all keys of the list
+        * @return array containing all keys or <code>null</code> if list unnamed */
+    public String[] keys() {
+       if (names==null) return null;
+       int i = 0;
+       String k[] = new String[names.size()];
+       while (i < k.length) { k[i] = keyAt(i); i++; };
+       return k;
+    }
+
+    // --- overrides that sync names
+
+    public void add(int index, Object element) {
+       super.add(index, element);
+       if (names==null) return;
+       names.add(index, null);
+    }
+
+       public boolean add(Object element) {
+               super.add(element);
+               if (names != null)
+                       names.add(null);
+               return true;
+       }
+       
+    public boolean addAll(Collection c) {
+       boolean ch = super.addAll(c);
+       if (names==null) return ch;
+       int l = size();
+       while (names.size()<l) names.add(null);
+       return ch;
+    }
+
+    public boolean addAll(int index, Collection c) {
+       boolean ch = super.addAll(index, c);
+       if (names==null) return ch;
+       int l = c.size();
+       while (l-- > 0) names.add(index, null);
+       return ch;
+    }
+
+    public void clear() {
+       super.clear();
+       names=null;
+    }
+
+    public Object clone() {
+       return new RList(this, names);  
+    }
+
+    public Object remove(int index) {
+       Object o = super.remove(index);
+       if (names != null) {
+           names.remove(index);
+           if (size()==0) names=null;
+       }
+       return o;
+    }
+
+    public boolean remove(Object elem) {
+       int i = indexOf(elem);
+       if (i<0) return false;
+       remove(i);
+       if (size()==0) names=null;
+       return true;
+    }
+
+    public boolean removeAll(Collection c) {
+       if (names==null) return super.removeAll(c);
+       boolean changed=false;
+       Iterator it = c.iterator();
+       while (it.hasNext())
+           changed|=remove(it.next());
+       return changed;
+    }
+
+    public boolean retainAll(Collection c) {
+       if (names==null) return super.retainAll(c);
+       boolean rm[] = new boolean[size()];
+       boolean changed=false;
+       int i = 0;
+       while (i<rm.length) {
+           changed|=rm[i]=!c.contains(get(i));
+           i++;
+       }
+       while (i>0) {
+           i--;
+           if (rm[i]) remove(i);
+       }
+       return changed;
+    }
+
+    // --- old API mapping
+    public void removeAllElements() { clear(); }
+    public void insertElementAt(Object obj, int index) { add(index, obj); }
+    public void addElement(Object obj) { add(obj); }
+    public void removeElementAt(int index) { remove(index); }
+    public boolean removeElement(Object obj) { return remove(obj); }
+
+    // --- Map interface
+
+    public boolean containsKey(Object key) {
+       return (names==null)?false:names.contains(key);
+    }
+
+    public boolean containsValue(Object value) {
+       return contains(value);
+    }
+
+    /** NOTE: THIS IS UNIMPLEMENTED and always returns <code>null</code>! Due to the fact that R lists are not proper maps we canot maintain a set-view of the list */
+    public Set entrySet() {
+       return null;
+    }
+
+    public Object get(Object key) {
+       return at((String)key);
+    }
+
+    /** Note: sinde RList is not really a Map, the returned set is only an approximation as it cannot reference duplicate or null names that may exist in the list */
+    public Set keySet() {
+       if (names==null) return null;
+       return new HashSet(names);
+    }
+
+    public Object put(Object key, Object value) {
+       if (key==null) {
+           add(value);
+           return null;
+       }
+       if (names != null) {
+           int p = names.indexOf(key);
+           if (p >= 0)
+               return super.set(p, value);
+       }
+       int i = size();
+       super.add(value);
+       if (names==null)
+           names = new Vector(i+1);
+       while (names.size() < i) names.add(null);
+       names.add(key);
+       return null;
+    }
+
+    public void putAll(Map t) {
+       if (t==null) return;
+       // NOTE: this if branch is dead since RList cannot inherit from Map
+       if (t instanceof RList) { // we need some more sophistication for RLists as they may have null-names which we append
+           RList l = (RList) t;
+           if (names==null) {
+               addAll(l);
+               return;
+           }
+           int n = l.size();
+           int i = 0;
+           while (i < n) {
+               String key = l.keyAt(i);
+               if (key==null)
+                   add(l.at(i));
+               else
+                   put(key, l.at(i));
+               i++;
+           }
+       } else {
+           Set ks = t.keySet();
+           Iterator i = ks.iterator();
+           while (i.hasNext()) {
+               Object key = i.next();
+               put(key, t.get(key));
+           }
+       }
+    }
+
+    public void putAll(RList t) {
+       if (t == null) return;
+       RList l = (RList) t;
+       if (names==null) {
+           addAll(l);
+           return;
+       }
+       int n = l.size();
+       int i = 0;
+       while (i < n) {
+           String key = l.keyAt(i);
+           if (key == null)
+               add(l.at(i));
+           else
+               put(key, l.at(i));
+           i++;
+       }
+    }
+    
+    public Object removeByKey(Object key) {
+       if (names==null) return null;
+       int i = names.indexOf(key);
+       if (i<0) return null;
+       Object o = elementAt(i);
+       removeElementAt(i);
+       names.removeElementAt(i);
+       return o;
+    }
+
+    public Collection values() {
+       return this;
+    }
+       
+       // other
+       public String toString() {
+               return "RList"+super.toString()+"{"+(isNamed()?"named,":"")+size()+"}";
+       }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/Makefile b/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/Makefile
new file mode 100644 (file)
index 0000000..598e48f
--- /dev/null
@@ -0,0 +1,27 @@
+RENG_SRC=$(wildcard ../*.java)
+RSRV_SRC=$(wildcard *.java) $(wildcard protocol/*.java)
+
+TARGETS=Rserve.jar
+
+all: $(TARGETS)
+
+JAVAC=javac
+JFLAGS=-encoding utf8 -source 1.4 -target 1.4
+
+../REngine.jar: $(RENG_SRC)
+       make -C .. REngine.jar
+
+Rserve.jar: $(RSRV_SRC) ../REngine.jar
+       @rm -rf org
+       $(JAVAC) -d . -cp ../REngine.jar $(RSRV_SRC)
+       jar fc $@ org
+       rm -rf org
+
+clean:
+       rm -rf org *~ protocol/*~ $(TARGETS)
+       make -C test clean
+
+test:
+       make -C test test
+
+.PHONY: clean all test
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/RConnection.java b/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/RConnection.java
new file mode 100644 (file)
index 0000000..857e2ee
--- /dev/null
@@ -0,0 +1,517 @@
+package org.rosuda.REngine.Rserve;
+
+// JRclient library - client interface to Rserve, see http://www.rosuda.org/Rserve/
+// Copyright (C) 2004-08 Simon Urbanek
+// --- for licensing information see LICENSE file in the original JRclient distribution ---
+
+import java.util.*;
+import java.io.*;
+import java.net.*;
+import org.rosuda.REngine.*;
+import org.rosuda.REngine.Rserve.protocol.*;
+
+/**  class providing TCP/IP connection to an Rserve
+     @version $Id$
+*/
+public class RConnection extends REngine {
+    /** last error string */
+    String lastError=null;
+    Socket s;
+    boolean connected=false;
+    InputStream is;
+    OutputStream os;
+    boolean authReq=false;
+    int authType=AT_plain;
+    String Key=null;
+    RTalk rt=null;
+
+    String host;
+    int port;
+
+    /** This static variable specifies the character set used to encode string for transfer. Under normal circumstances there should be no reason for changing this variable. The default is UTF-8, which makes sure that 7-bit ASCII characters are sent in a backward-compatible fashion. Currently (Rserve 0.1-7) there is no further conversion on Rserve's side, i.e. the strings are passed to R without re-coding. If necessary the setting should be changed <u>before</u> connecting to the Rserve in case later Rserves will provide a possibility of setting the encoding during the handshake. */
+    public static String transferCharset="UTF-8";
+    
+    /** authorization type: plain text */
+    public static final int AT_plain = 0;
+    /** authorization type: unix crypt */
+    public static final int AT_crypt = 1;
+
+    /** version of the server (as reported in IDstring just after Rsrv) */
+    protected int rsrvVersion;
+    
+    /** make a new local connection on default port (6311) */
+    public RConnection() throws RserveException {
+               this("127.0.0.1",6311);
+    }
+
+    /** make a new connection to specified host on default port (6311)
+       @param host host name/IP
+    */
+    public RConnection(String host) throws RserveException {
+               this(host,6311);
+    }
+
+    /** make a new connection to specified host and given port.
+        * Make sure you check {@link #isConnected} to ensure the connection was successfully created.
+        * @param host host name/IP
+        * @param port TCP port
+        */
+    public RConnection(String host, int port) throws RserveException {
+               this(host, port, null);
+    }
+
+       /** restore a connection based on a previously detached session
+        * @param session detached session object */
+    RConnection(RSession session) throws RserveException {
+               this(null, 0, session);
+    }
+
+    RConnection(String host, int port, RSession session) throws RserveException {
+        try {
+            if (connected) s.close();
+            s=null;
+        } catch (Exception e) {
+            throw new RserveException(this,"Cannot connect: "+e.getMessage());
+        }
+               if (session!=null) {
+                       host=session.host;
+                       port=session.port;
+               }
+        connected=false;
+               this.host=host;
+               this.port=port;
+        try {
+            s=new Socket(host,port);
+                       // disable Nagle's algorithm since we really want immediate replies
+                       s.setTcpNoDelay(true);
+        } catch (Exception sce) {
+            throw new RserveException(this,"Cannot connect: "+sce.getMessage());
+        }
+        try {
+            is=s.getInputStream();
+            os=s.getOutputStream();
+        } catch (Exception gse) {
+            throw new RserveException(this,"Cannot get io stream: "+gse.getMessage());
+        }
+        rt=new RTalk(is,os);
+               if (session==null) {
+                       byte[] IDs=new byte[32];
+                       int n=-1;
+                       try {
+                               n=is.read(IDs);
+                       } catch (Exception sre) {
+                               throw new RserveException(this,"Error while receiving data: "+sre.getMessage());
+                       }
+                       try {
+                               if (n!=32) {
+                                       throw new RserveException(this,"Handshake failed: expected 32 bytes header, got "+n);
+                               }
+                               String ids=new String(IDs);
+                               if (ids.substring(0,4).compareTo("Rsrv")!=0)
+                                       throw new RserveException(this,"Handshake failed: Rsrv signature expected, but received \""+ids+"\" instead.");
+                               try {
+                                       rsrvVersion=Integer.parseInt(ids.substring(4,8));
+                               } catch (Exception px) {}
+                               // we support (knowingly) up to 103
+                               if (rsrvVersion>103)
+                                       throw new RserveException(this,"Handshake failed: The server uses more recent protocol than this client.");
+                               if (ids.substring(8,12).compareTo("QAP1")!=0)
+                                       throw new RserveException(this,"Handshake failed: unupported transfer protocol ("+ids.substring(8,12)+"), I talk only QAP1.");
+                               for (int i=12;i<32;i+=4) {
+                                       String attr=ids.substring(i,i+4);
+                                       if (attr.compareTo("ARpt")==0) {
+                                               if (!authReq) { // this method is only fallback when no other was specified
+                                                       authReq=true;
+                                                       authType=AT_plain;
+                                               }
+                                       }
+                                       if (attr.compareTo("ARuc")==0) {
+                                               authReq=true;
+                                               authType=AT_crypt;
+                                       }
+                                       if (attr.charAt(0)=='K') {
+                                               Key=attr.substring(1,3);
+                                       }
+                               }
+                       } catch (RserveException innerX) {
+                               try { s.close(); } catch (Exception ex01) {}; is=null; os=null; s=null;
+                               throw innerX;
+                       }
+               } else { // we have a session to take care of
+                       try {
+                               os.write(session.key,0,32);
+                       } catch (Exception sre) {
+                               throw new RserveException(this,"Error while sending session key: "+sre.getMessage());
+                       }
+                       rsrvVersion = session.rsrvVersion;
+               }
+               connected=true;
+               lastError="OK";
+    }    
+       
+    public void finalize() {
+        close();
+        is=null;
+       os=null;
+    }
+
+    /** get server version as reported during the handshake.
+        @return server version as integer (Rsrv0100 will return 100) */
+    public int getServerVersion() {
+        return rsrvVersion;
+    }
+    
+    /** closes current connection */
+    public boolean close() {
+        try {
+            if (s != null) s.close();
+            connected = false;
+                       return true;
+        } catch(Exception e) { };
+               return false;
+    }
+    
+    public String voidEvalS(String cmd) {
+       try {
+               voidEval(cmd);
+               return "";
+       } catch (RserveException e) {
+               return e.getMessage();
+       }
+    }
+    
+    /** evaluates the given command, but does not fetch the result (useful for assignment
+       operations)
+       @param cmd command/expression string */
+    public void voidEval(String cmd) throws RserveException {
+               if (!connected || rt==null)
+                       throw new RserveException(this,"Not connected");
+               RPacket rp=rt.request(RTalk.CMD_voidEval,cmd+"\n");
+               if (rp!=null && rp.isOk()) return;
+        throw new RserveException(this,"voidEval failed",rp);
+    }
+
+       /** evaluates the given command, detaches the session (see @link{detach()}) and closes connection while the command is being evaluted (requires Rserve 0.4+).
+               Note that a session cannot be attached again until the commad was successfully processed. Techincally the session is put into listening mode while the command is being evaluated but accept is called only after the command was evaluated. One commonly used techique to monitor detached working sessions is to use second connection to poll the status (e.g. create a temporary file and return the full path before detaching thus allowing new connections to read it).
+               @param cmd command/expression string
+               @return session object that can be use to attach back to the session once the command completed */
+    public RSession voidEvalDetach(String cmd) throws RserveException {
+               if (!connected || rt==null)
+                       throw new RserveException(this,"Not connected");
+               RPacket rp=rt.request(RTalk.CMD_detachedVoidEval,cmd+"\n");
+               if (rp==null || !rp.isOk())
+                       throw new RserveException(this,"detached void eval failed",rp);
+               RSession s = new RSession(this, rp);
+               close();
+               return s;
+    }
+       
+    REXP parseEvalResponse(RPacket rp) throws RserveException {
+               int rxo=0;
+               byte[] pc=rp.getCont();
+               if (rsrvVersion>100) { /* since 0101 eval responds correctly by using DT_SEXP type/len header which is 4 bytes long */
+                       rxo=4;
+                       /* we should check parameter type (should be DT_SEXP) and fail if it's not */
+                       if (pc[0]!=RTalk.DT_SEXP && pc[0]!=(RTalk.DT_SEXP|RTalk.DT_LARGE))
+                               throw new RserveException(this,"Error while processing eval output: SEXP (type "+RTalk.DT_SEXP+") expected but found result type "+pc[0]+".");
+                       if (pc[0]==(RTalk.DT_SEXP|RTalk.DT_LARGE))
+                               rxo=8; // large data need skip of 8 bytes
+                       /* warning: we are not checking or using the length - we assume that only the one SEXP is returned. This is true for the current CMD_eval implementation, but may not be in the future. */
+               }
+               if (pc.length>rxo) {
+                       try {
+                               REXPFactory rx=new REXPFactory();
+                               rx.parseREXP(pc, rxo);
+                               return rx.getREXP();
+                       } catch (REXPMismatchException me) {
+                               me.printStackTrace();
+                               throw new RserveException(this, "Error when parsing response: "+me.getMessage());
+                       }
+               }
+               return null;
+    }
+
+    /** evaluates the given command and retrieves the result
+       @param cmd command/expression string
+       @return R-xpression or <code>null</code> if an error occured */
+    public REXP eval(String cmd) throws RserveException {
+               if (!connected || rt==null)
+            throw new RserveException(this,"Not connected");
+               RPacket rp=rt.request(RTalk.CMD_eval,cmd+"\n");
+               if (rp!=null && rp.isOk())
+                       return parseEvalResponse(rp);
+        throw new RserveException(this,"eval failed",rp);
+    }
+
+    /** assign a string value to a symbol in R. The symbol is created if it doesn't exist already.
+        @param sym symbol name. Currently assign uses CMD_setSEXP command of Rserve, i.e. the symbol value is NOT parsed. It is the responsibility of the user to make sure that the symbol name is valid in R (recall the difference between a symbol and an expression!). In fact R will always create the symbol, but it may not be accessible (examples: "bar\nfoo" or "bar$foo").
+        @param ct contents
+        */
+    public void assign(String sym, String ct) throws RserveException {
+               if (!connected || rt==null)
+            throw new RserveException(this,"Not connected");
+        byte[] symn=sym.getBytes();
+        byte[] ctn=ct.getBytes();
+        int sl=symn.length+1;
+        int cl=ctn.length+1;
+        if ((sl&3)>0) sl=(sl&0xfffffc)+4; // make sure the symbol length is divisible by 4
+        if ((cl&3)>0) cl=(cl&0xfffffc)+4; // make sure the content length is divisible by 4
+        byte[] rq=new byte[sl+4+cl+4];
+        int ic;
+        for(ic=0;ic<symn.length;ic++) rq[ic+4]=symn[ic];
+        while (ic<sl) { rq[ic+4]=0; ic++; }
+        for(ic=0;ic<ctn.length;ic++) rq[ic+sl+8]=ctn[ic];
+        while (ic<cl) { rq[ic+sl+8]=0; ic++; }
+               RTalk.setHdr(RTalk.DT_STRING,sl,rq,0);
+               RTalk.setHdr(RTalk.DT_STRING,cl,rq,sl+4);
+               RPacket rp=rt.request(RTalk.CMD_setSEXP,rq);
+        if (rp!=null && rp.isOk()) return;
+        throw new RserveException(this,"assign failed",rp);
+    }
+
+    /** assign a content of a REXP to a symbol in R. The symbol is created if it doesn't exist already.
+     * @param sym symbol name. Currently assign uses CMD_setSEXP command of Rserve, i.e. the symbol value is NOT parsed. It is the responsibility of the user to make sure that the symbol name is valid in R (recall the difference between a symbol and an expression!). In fact R will always create the symbol, but it may not be accessible (examples: "bar\nfoo" or "bar$foo").
+        * @param rexp contents
+        */
+public void assign(String sym, REXP rexp) throws RserveException {
+       if (!connected || rt==null)
+           throw new RserveException(this,"Not connected");
+       try {
+               REXPFactory r = new REXPFactory(rexp);
+               int rl=r.getBinaryLength();
+               byte[] symn=sym.getBytes();
+               int sl=symn.length+1;
+               if ((sl&3)>0) sl=(sl&0xfffffc)+4; // make sure the symbol length is divisible by 4
+               byte[] rq=new byte[sl+rl+((rl>0xfffff0)?12:8)];
+               int ic;
+               for(ic=0;ic<symn.length;ic++) rq[ic+4]=symn[ic];
+               while(ic<sl) { rq[ic+4]=0; ic++; }; // pad with 0
+               RTalk.setHdr(RTalk.DT_STRING,sl,rq,0);
+               RTalk.setHdr(RTalk.DT_SEXP,rl,rq,sl+4);
+               r.getBinaryRepresentation(rq,sl+((rl>0xfffff0)?12:8));
+               RPacket rp=rt.request(RTalk.CMD_setSEXP,rq);
+               if (rp!=null && rp.isOk()) return;
+               throw new RserveException(this,"assign failed",rp);
+       } catch (REXPMismatchException me) {
+               throw new RserveException(this, "Error creating binary representation: "+me.getMessage());
+       }
+}
+
+    /** open a file on the Rserve for reading
+        @param fn file name. should not contain any path delimiters, since Rserve may restrict the access to local working directory.
+        @return input stream to be used for reading. Note that the stream is read-once only, there is no support for seek or rewind. */
+    public RFileInputStream openFile(String fn) throws IOException {
+               return new RFileInputStream(rt,fn);
+    }
+
+    /** create a file on the Rserve for writing
+        @param fn file name. should not contain any path delimiters, since Rserve may restrict the access to local working directory.
+        @return output stream to be used for writinging. Note that the stream is write-once only, there is no support for seek or rewind. */
+    public RFileOutputStream createFile(String fn) throws IOException {
+        return new RFileOutputStream(rt,fn);
+    }
+
+    /** remove a file on the Rserve
+        @param fn file name. should not contain any path delimiters, since Rserve may restrict the access to local working directory. */
+    public void removeFile(String fn) throws RserveException {
+               if (!connected || rt==null)
+                       throw new RserveException(this,"Not connected");
+               RPacket rp=rt.request(RTalk.CMD_removeFile,fn);
+               if (rp!=null && rp.isOk()) return;
+        throw new RserveException(this,"removeFile failed",rp);
+    }
+
+    /** shutdown remote Rserve. Note that some Rserves cannot be shut down from the client side. */
+    public void shutdown() throws RserveException {
+               if (!connected || rt==null)
+                       throw new RserveException(this,"Not connected");
+
+               RPacket rp=rt.request(RTalk.CMD_shutdown);
+               if (rp!=null && rp.isOk()) return;
+        throw new RserveException(this,"shutdown failed",rp);
+    }
+
+    /** Sets send buffer size of the Rserve (in bytes) for the current connection. All responses send by Rserve are stored in the send buffer before transmitting. This means that any objects you want to get from the Rserve need to fit into that buffer. By default the size of the send buffer is 2MB. If you need to receive larger objects from Rserve, you will need to use this function to enlarge the buffer. In order to save memory, you can also reduce the buffer size once it's not used anymore. Currently the buffer size is only limited by the memory available and/or 1GB (whichever is smaller). Current Rserve implementations won't go below buffer sizes of 32kb though. If the specified buffer size results in 'out of memory' on the server, the corresponding error is sent and the connection is terminated.<br>
+        <i>Note:</i> This command may go away in future versions of Rserve which will use dynamic send buffer allocation.
+        @param sbs send buffer size (in bytes) min=32k, max=1GB
+     */
+    public void setSendBufferSize(long sbs) throws RserveException {
+        if (!connected || rt==null)
+                       throw new RserveException(this,"Not connected");
+
+        RPacket rp=rt.request(RTalk.CMD_setBufferSize,(int)sbs);
+        if (rp!=null && rp.isOk()) return;
+        throw new RserveException(this,"setSendBufferSize failed",rp);        
+    }
+
+    /** set string encoding for this session. It is strongly
+     * recommended to make sure the encoding is always set to UTF-8
+     * because that is the only encoding supported by this Java
+     * client. It can be done either by uisng the
+     * <code>encoding</code> option in the server or by calling
+     * setStringEncoding("utf8") at the beginning of a session (but
+     * after login).
+     @param enc name of the encoding as defined by Rserve - as of
+     Rserve version 0.5-3 valid values are "utf8", "latin1" and
+     "native" (case-sensitive)
+     @since Rserve 0.5-3
+    */
+    public void setStringEncoding(String enc) throws RserveException {
+        if (!connected || rt==null)
+                       throw new RserveException(this,"Not connected");
+       RPacket rp = rt.request(RTalk.CMD_setEncoding, enc);
+       if (rp != null && rp.isOk()) return;
+       throw new RserveException(this,"setStringEncoding failed", rp);
+    }
+
+    /** login using supplied user/pwd. Note that login must be the first
+       command if used
+       @param user username
+       @param pwd password */
+    public void login(String user, String pwd) throws RserveException {
+               if (!authReq) return;
+               if (!connected || rt==null)
+                       throw new RserveException(this,"Not connected");
+               if (authType==AT_crypt) {
+                       if (Key==null) Key="rs";
+                       RPacket rp=rt.request(RTalk.CMD_login,user+"\n"+jcrypt.crypt(Key,pwd));
+                       if (rp!=null && rp.isOk()) return;
+                       try { s.close(); } catch(Exception e) {};
+                       is=null; os=null; s=null; connected=false;
+            throw new RserveException(this,"login failed",rp);
+               }
+               RPacket rp=rt.request(RTalk.CMD_login,user+"\n"+pwd);
+               if (rp!=null && rp.isOk()) return;
+               try {s.close();} catch (Exception e) {};
+               is=null; os=null; s=null; connected=false;
+        throw new RserveException(this,"login failed",rp);
+    }
+
+    
+    /** detaches the session and closes the connection (requires Rserve 0.4+). The session can be only resumed by calling @link{RSession.attach} */
+       public RSession detach() throws RserveException {
+               if (!connected || rt==null)
+            throw new RserveException(this,"Not connected");
+               RPacket rp=rt.request(RTalk.CMD_detachSession);
+               if (rp==null || !rp.isOk())
+                       throw new RserveException(this,"Cannot detach",rp);
+               RSession s = new RSession(this, rp);
+               close();
+               return s;
+    }
+
+    /** check connection state. Note that currently this state is not checked on-the-spot,
+       that is if connection went down by an outside event this is not reflected by
+       the flag
+       @return <code>true</code> if this connection is alive */
+    public boolean isConnected() { return connected; }
+    
+    /** check authentication requirement sent by server
+       @return <code>true</code> is server requires authentication. In such case first
+       command after connecting must be {@link #login}. */
+    public boolean needLogin() { return authReq; }
+    
+    /** get last error string
+       @return last error string */
+    public String getLastError() { return lastError; }
+       
+    /** evaluates the given command in the master server process asynchronously (control command). Note that control commands are always asynchronous, i.e., the expression is enqueued for evaluation in the master process and the method returns before the expression is evaluated (in non-parallel builds the client has to close the connection before the expression can be evaluated). There is no way to check for errors and control commands should be sent with utmost care as they can abort the server process. The evaluation has no immediate effect on the client session.
+     *  @param cmd command/expression string 
+     *  @since Rserve 0.6-0 */
+    public void serverEval(String cmd) throws RserveException {
+       if (!connected || rt == null)
+           throw new RserveException(this, "Not connected");
+       RPacket rp = rt.request(RTalk.CMD_ctrlEval, cmd+"\n");
+       if (rp != null && rp.isOk()) return;
+       throw new RserveException(this,"serverEval failed",rp);
+    }
+    
+    /** sources the given file (the path must be local to the server!) in the master server process asynchronously (control command). See {@link #serverEval()} for details on control commands.
+     *  @param serverFile path to a file on the server (it is recommended to always use full paths, because the server process has a different working directory than the client child process!).
+     *  @since Rserve 0.6-0 */
+    public void serverSource(String serverFile) throws RserveException {
+       if (!connected || rt == null)
+           throw new RserveException(this, "Not connected");
+       RPacket rp = rt.request(RTalk.CMD_ctrlSource, serverFile);
+       if (rp != null && rp.isOk()) return;
+       throw new RserveException(this,"serverSource failed",rp);
+    }
+    
+    /** attempt to shut down the server process cleanly. Note that there is a fundamental difference between the {@link shutdown()} method and this method: <code>serverShutdown()</code> is a proper control command and thus fully authentication controllable, whereas {@link shutdown()} is a client-side command sent to the client child process and thus relying on the ability of the client to signal the server process which may be disabled. Therefore <code>serverShutdown()</code> is preferred and more reliable for Rserve 0.6-0 and higher.
+     *  @since Rserve 0.6-0 */
+    public void serverShutdown() throws RserveException {
+       if (!connected || rt == null)
+           throw new RserveException(this, "Not connected");
+       RPacket rp = rt.request(RTalk.CMD_ctrlShutdown);
+       if (rp != null && rp.isOk()) return;
+       throw new RserveException(this,"serverShutdown failed",rp);
+    }
+    
+//========= REngine interface API
+
+public REXP parse(String text, boolean resolve) throws REngineException {
+       throw new REngineException(this, "Rserve doesn't support separate parsing step.");
+}
+public REXP eval(REXP what, REXP where, boolean resolve) throws REngineException {
+       return new REXPNull();
+}
+public REXP parseAndEval(String text, REXP where, boolean resolve) throws REngineException {
+       if (where!=null) throw new REngineException(this, "Rserve doesn't support environments other than .GlobalEnv");
+       try {
+               return eval(text);
+       } catch (RserveException re) {
+               throw new REngineException(this, re.getMessage());
+       }
+}
+
+/** assign into an environment
+@param symbol symbol name
+@param value value to assign
+@param env environment to assign to */
+public void assign(String symbol, REXP value, REXP env) throws REngineException {
+       if (env!=null) throw new REngineException(this, "Rserve doesn't support environments other than .GlobalEnv");
+       try {
+               assign(symbol, value);
+       } catch (RserveException re) {
+               throw new REngineException(this, re.getMessage());
+       }
+}
+
+/** get a value from an environment
+@param symbol symbol name
+@param env environment
+@param resolve resolve the resulting REXP or just return a reference           
+@return value */
+public REXP get(String symbol, REXP env, boolean resolve) throws REngineException {
+       if (!resolve) throw new REngineException(this, "Rserve doesn't support references");
+       try {
+               return eval("get(\""+symbol+"\")");
+       } catch (RserveException re) {
+               throw new REngineException(this, re.getMessage());
+       }
+}
+
+/** fetch the contents of the given reference. The resulting REXP may never be REXPReference.
+@param ref reference to resolve
+@return resolved reference */
+public REXP resolveReference(REXP ref) throws REngineException {
+       throw new REngineException(this, "Rserve doesn't support references");
+}
+       
+       public REXP createReference(REXP ref) throws REngineException {
+               throw new REngineException(this, "Rserve doesn't support references");
+       }
+       public void finalizeReference(REXP ref) throws REngineException {
+               throw new REngineException(this, "Rserve doesn't support references");
+       }
+       
+public REXP getParentEnvironment(REXP env, boolean resolve) throws REngineException {
+       throw new REngineException(this, "Rserve doesn't support environments other than .GlobalEnv");
+}
+
+public REXP newEnvironment(REXP parent, boolean resolve) throws REngineException {
+       throw new REngineException(this, "Rserve doesn't support environments other than .GlobalEnv");
+}
+
+}
+
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/RFileInputStream.java b/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/RFileInputStream.java
new file mode 100644 (file)
index 0000000..fc971c7
--- /dev/null
@@ -0,0 +1,87 @@
+package org.rosuda.REngine.Rserve;
+
+// JRclient library - client interface to Rserve, see http://www.rosuda.org/Rserve/
+// Copyright (C) 2004 Simon Urbanek
+// --- for licensing information see LICENSE file in the original JRclient distribution ---
+
+import java.io.*;
+import org.rosuda.REngine.*;
+import org.rosuda.REngine.Rserve.protocol.*;
+
+/** <b>RFileInputStream</b> is an {@link InputStream} to transfer files
+    from <b>Rserve</b> server to the client. It is used very much like
+    a {@link FileInputStream}. Currently mark and seek is not supported.
+    The current implementation is also "one-shot" only, that means the file
+    can be read only once.
+    @version $Id$
+*/
+public class RFileInputStream extends InputStream {
+    /** RTalk class to use for communication with the Rserve */
+    RTalk rt;
+    /** set to <code>true</code> when {@link #close} was called.
+       Any subsequent read requests on closed stream  result in an 
+       {@link IOException} or error result */
+    boolean closed;
+    /** set to <code>true</code> once EOF is reached - or more specifically
+       the first time remore fread returns OK and 0 bytes */
+    boolean eof;
+
+    /** tries to open file on the R server, using specified {@link RTalk} object
+       and filename. Be aware that the filename has to be specified in host
+       format (which is usually unix). In general you should not use directories
+       since Rserve provides an own directory for every connection. Future Rserve
+       servers may even strip all directory navigation characters for security
+       purposes. Therefore only filenames without path specification are considered
+       valid, the behavior in respect to absolute paths in filenames is undefined. */
+    RFileInputStream(RTalk rti, String fn) throws IOException {
+       rt=rti;
+       RPacket rp=rt.request(RTalk.CMD_openFile,fn);
+       if (rp==null || !rp.isOk())
+           throw new IOException((rp==null)?"Connection to Rserve failed":("Request return code: "+rp.getStat()));
+       closed=false; eof=false;
+    }
+
+    /** reads one byte from the file. This function should be avoided, since
+       {@link RFileInputStream} provides no buffering. This means that each
+       call to this function leads to a complete packet exchange between
+       the server and the client. Use {@link #read(byte[],int,int)} instead
+       whenever possible. In fact this function calls <code>#read(b,0,1)</code>.
+       @return -1 on any failure, or the acquired byte (0..255) on success */
+    public int read() throws IOException {
+       byte[] b=new byte[1];
+       if (read(b,0,1)<1) return -1;
+       return b[0];
+    }
+
+    /** Reads specified number of bytes (or less) from the remote file.
+       @param b buffer to store the read bytes
+       @param off offset where to strat filling the buffer
+       @param len maximal number of bytes to read
+       @return number of bytes read or -1 if EOF reached
+    */
+    public int read(byte[] b, int off, int len) throws IOException {
+       if (closed) throw new IOException("File is not open");
+       if (eof) return -1;
+       RPacket rp=rt.request(RTalk.CMD_readFile,len);
+       if (rp==null || !rp.isOk())
+           throw new IOException((rp==null)?"Connection to Rserve failed":("Request return code: "+rp.getStat()));
+       byte[] rd=rp.getCont();
+       if (rd==null) {
+           eof=true;
+           return -1;
+       };
+       int i=0;
+       while(i<rd.length) { b[off+i]=rd[i]; i++; };
+       return rd.length;
+    }
+
+    /** close stream - is not related to the actual RConnection, calling
+       close does not close the RConnection
+    */
+    public void close() throws IOException {
+       RPacket rp=rt.request(RTalk.CMD_closeFile,(byte[])null);
+       if (rp==null || !rp.isOk())
+           throw new IOException((rp==null)?"Connection to Rserve failed":("Request return code: "+rp.getStat()));
+       closed=true;
+    }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/RFileOutputStream.java b/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/RFileOutputStream.java
new file mode 100644 (file)
index 0000000..b8cfefd
--- /dev/null
@@ -0,0 +1,97 @@
+// JRclient library - client interface to Rserve, see http://www.rosuda.org/Rserve/
+// Copyright (C) 2003 Simon Urbanek
+// --- for licensing information see LICENSE file in the original JRclient distribution ---
+//
+//  RFileOutputStream.java
+//
+//  Created by Simon Urbanek on Wed Oct 22 2003.
+//
+
+package org.rosuda.REngine.Rserve;
+
+import java.io.*;
+import org.rosuda.REngine.*;
+import org.rosuda.REngine.Rserve.protocol.*;
+
+/** <b>RFileOutputStream</b> is an {@link OutputStream} to transfer files
+from the client to <b>Rserve</b> server. It is used very much like
+a {@link FileOutputStream}. Currently mark and seek is not supported.
+The current implementation is also "one-shot" only, that means the file
+can be written only once.
+@version $Id$
+*/
+
+public class RFileOutputStream extends OutputStream {
+    /** RTalk class to use for communication with the Rserve */
+    RTalk rt;
+    /** set to <code>true</code> when {@link #close} was called.
+    Any subsequent read requests on closed stream  result in an
+{@link IOException} or error result */
+    boolean closed;
+
+    /** tries to create a file on the R server, using specified {@link RTalk} object
+        and filename. Be aware that the filename has to be specified in host
+        format (which is usually unix). In general you should not use directories
+        since Rserve provides an own directory for every connection. Future Rserve
+        servers may even strip all directory navigation characters for security
+        purposes. Therefore only filenames without path specification are considered
+        valid, the behavior in respect to absolute paths in filenames is undefined.
+        @param rti RTalk object for communication with Rserve
+        @param fb filename of the file to create (existing file will be overwritten)
+        */
+    RFileOutputStream(RTalk rti, String fn) throws IOException {
+        rt=rti;
+        RPacket rp=rt.request(RTalk.CMD_createFile,fn);
+        if (rp==null || !rp.isOk())
+            throw new IOException((rp==null)?"Connection to Rserve failed":("Request return code: "+rp.getStat()));
+        closed=false;
+    }
+
+    /** writes one byte to the file. This function should be avoided, since
+    {@link RFileOutputStream} provides no buffering. This means that each
+        call to this function leads to a complete packet exchange between
+        the server and the client. Use {@link #write(byte[])} instead
+        whenever possible. In fact this function calls <code>write(b,0,1)</code>.
+        @param b byte to write
+        */
+    public void write(int b) throws IOException {
+        byte[] ba=new byte[1];
+        write(ba,0,1);
+    }
+
+    /** writes the content of b into the file. This methods is equivalent to calling <code>write(b,0,b.length)</code>.
+        @param b content to write
+        */
+    public void write(byte b[]) throws IOException {
+        write(b,0,b.length);
+    }
+
+    /** Writes specified number of bytes to the remote file.
+        @param b buffer containing the bytes to write
+        @param off offset where to start
+        @param len number of bytes to write
+        */
+    public void write(byte[] b, int off, int len) throws IOException {
+        if (closed) throw new IOException("File is not open");
+        if (len<0) len=0;
+        boolean isLarge=(len>0xfffff0);
+        byte[] hdr=RTalk.newHdr(RTalk.DT_BYTESTREAM,len);
+        RPacket rp=rt.request(RTalk.CMD_writeFile,hdr,b,off,len);
+        if (rp==null || !rp.isOk())
+            throw new IOException((rp==null)?"Connection to Rserve failed":("Request return code: "+rp.getStat()));
+    }
+
+    /** close stream - is not related to the actual RConnection, calling
+        close does not close the RConnection.
+        */
+    public void close() throws IOException {
+        RPacket rp=rt.request(RTalk.CMD_closeFile,(byte[])null);
+        if (rp==null || !rp.isOk())
+            throw new IOException((rp==null)?"Connection to Rserve failed":("Request return code: "+rp.getStat()));
+        closed=true;
+    }
+
+    /** currently (Rserve 0.3) there is no way to force flush on the remote side, hence this function is noop. Future versions of Rserve may support this feature though. At any rate, it is safe to call it. */
+    public void flush() {
+    }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/RSession.java b/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/RSession.java
new file mode 100644 (file)
index 0000000..dd3e86a
--- /dev/null
@@ -0,0 +1,40 @@
+package org.rosuda.REngine.Rserve;
+
+import org.rosuda.REngine.Rserve.protocol.RPacket;
+import org.rosuda.REngine.Rserve.protocol.RTalk;
+
+public class RSession implements java.io.Serializable {
+    // serial version UID should only change if method signatures change
+    // significantly enough that previous versions cannot be used with
+    // current versions
+    private static final long serialVersionUID = -7048099825974875604l;
+
+    String host;
+    int port;
+    byte[] key;
+
+    transient RPacket attachPacket=null; // response on session attach
+    int rsrvVersion;
+
+    protected RSession() {
+        // default no-args constructor for serialization
+    }
+
+    RSession(RConnection c, RPacket p) throws RserveException {
+       this.host=c.host;
+       this.rsrvVersion=c.rsrvVersion;
+       byte[] ct = p.getCont();
+       if (ct==null || ct.length!=32+3*4)
+           throw new RserveException(c, "Invalid response to session detach request.");
+       this.port = RTalk.getInt(ct, 4);
+       this.key=new byte[32];
+       System.arraycopy(ct, 12, this.key, 0, 32);
+    }
+
+    /** attach/resume this session */
+    public RConnection attach() throws RserveException {
+       RConnection c = new RConnection(this);
+       attachPacket = c.rt.request(-1);
+       return c;
+    }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/Rserve.jar b/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/Rserve.jar
new file mode 100644 (file)
index 0000000..9c1cdc2
Binary files /dev/null and b/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/Rserve.jar differ
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/RserveException.java b/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/RserveException.java
new file mode 100644 (file)
index 0000000..33830ba
--- /dev/null
@@ -0,0 +1,71 @@
+// JRclient library - client interface to Rserve, see http://www.rosuda.org/Rserve/
+// Copyright (C) 2004 Simon Urbanek
+// --- for licensing information see LICENSE file in the original JRclient distribution ---
+//
+//  RserveException.java
+//
+//  Created by Simon Urbanek on Mon Aug 18 2003.
+//
+//  $Id$
+//
+
+package org.rosuda.REngine.Rserve;
+
+import org.rosuda.REngine.Rserve.protocol.RPacket;
+import org.rosuda.REngine.Rserve.protocol.RTalk;
+import org.rosuda.REngine.REngineException;
+
+public class RserveException extends REngineException {
+    protected String err;
+    protected int reqReturnCode;
+
+    public String getRequestErrorDescription() {
+               return getRequestErrorDescription(reqReturnCode);
+       }
+       
+    public String getRequestErrorDescription(int code) {
+        switch(code) {
+            case 0: return "no error";
+            case 2: return "R parser: input incomplete";
+            case 3: return "R parser: syntax error";
+            case RTalk.ERR_auth_failed: return "authorization failed";
+            case RTalk.ERR_conn_broken: return "connection broken";
+            case RTalk.ERR_inv_cmd: return "invalid command";
+            case RTalk.ERR_inv_par: return "invalid parameter";
+            case RTalk.ERR_IOerror: return "I/O error on the server";
+            case RTalk.ERR_not_open: return "connection is not open";
+            case RTalk.ERR_access_denied: return "access denied (local to the server)";
+            case RTalk.ERR_unsupported_cmd: return "unsupported command";
+            case RTalk.ERR_unknown_cmd: return "unknown command";
+            case RTalk.ERR_data_overflow: return "data overflow, incoming data too big";
+            case RTalk.ERR_object_too_big: return "evaluation successful, but returned object is too big to transport";
+            case RTalk.ERR_out_of_mem: return "FATAL: Rserve ran out of memory, closing connection";
+                       case RTalk.ERR_session_busy: return "session is busy";
+                       case RTalk.ERR_detach_failed: return "session detach failed";
+               case RTalk.ERR_ctrl_closed: return "control pipe to master process is closed/broken";
+        }
+        return "error code: "+code;
+    }
+
+    public String getMessage() {
+        return super.getMessage()+((reqReturnCode!=-1)?", request status: "+getRequestErrorDescription():"");
+    }
+    
+    public RserveException(RConnection c, String msg) {
+        this(c,msg,-1);
+    }
+
+    public RserveException(RConnection c, String msg, int requestReturnCode) {
+        super(c, msg);
+        reqReturnCode=requestReturnCode;
+               if (c!=null) c.lastError=getMessage();
+    }
+
+       public RserveException(RConnection c, String msg, RPacket p) {
+               this(c, msg, (p==null)?-1:p.getStat());
+       }
+       
+    public int getRequestReturnCode() {
+        return reqReturnCode;
+    }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/package-info.java b/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/package-info.java
new file mode 100644 (file)
index 0000000..4f8b987
--- /dev/null
@@ -0,0 +1,4 @@
+/**
+ * REngine-based interface to <a href="http://www.rforge.net/Rserve/">Rserve</a>
+ */
+package org.rosuda.REngine.Rserve ;
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/protocol/REXPFactory.java b/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/protocol/REXPFactory.java
new file mode 100644 (file)
index 0000000..0011caf
--- /dev/null
@@ -0,0 +1,707 @@
+package org.rosuda.REngine.Rserve.protocol;
+
+// JRclient library - client interface to Rserve, see http://www.rosuda.org/Rserve/
+// Copyright (C) 2004-8 Simon Urbanek
+// --- for licensing information see LICENSE file in the original JRclient distribution ---
+
+import java.util.*;
+
+import org.rosuda.REngine.*;
+import org.rosuda.REngine.Rserve.*;
+
+/** representation of R-eXpressions in Java
+
+    @version $Id$
+*/
+public class REXPFactory {
+    /** xpression type: NULL */
+    public static final int XT_NULL=0;
+    /** xpression type: integer */
+    public static final int XT_INT=1;
+    /** xpression type: double */
+    public static final int XT_DOUBLE=2;
+    /** xpression type: String */
+    public static final int XT_STR=3;
+    /** xpression type: language construct (currently content is same as list) */
+    public static final int XT_LANG=4;
+    /** xpression type: symbol (content is symbol name: String) */
+    public static final int XT_SYM=5;
+    /** xpression type: RBool */    
+    public static final int XT_BOOL=6;
+    /** xpression type: S4 object
+       @since Rserve 0.5 */
+    public static final int XT_S4=7;
+    /** xpression type: generic vector (RList) */
+    public static final int XT_VECTOR=16;
+    /** xpression type: dotted-pair list (RList) */
+    public static final int XT_LIST=17;
+    /** xpression type: closure (there is no java class for that type (yet?). currently the body of the closure is stored in the content part of the REXP. Please note that this may change in the future!) */
+    public static final int XT_CLOS=18;
+    /** xpression type: symbol name
+       @since Rserve 0.5 */
+    public static final int XT_SYMNAME=19;
+    /** xpression type: dotted-pair list (w/o tags)
+       @since Rserve 0.5 */
+    public static final int XT_LIST_NOTAG=20;
+    /** xpression type: dotted-pair list (w tags)
+       @since Rserve 0.5 */
+    public static final int XT_LIST_TAG=21;
+    /** xpression type: language list (w/o tags)
+       @since Rserve 0.5 */
+    public static final int XT_LANG_NOTAG=22;
+    /** xpression type: language list (w tags)
+       @since Rserve 0.5 */
+    public static final int XT_LANG_TAG=23;
+    /** xpression type: expression vector */
+    public static final int XT_VECTOR_EXP=26;
+    /** xpression type: string vector */
+    public static final int XT_VECTOR_STR=27;
+    /** xpression type: int[] */
+    public static final int XT_ARRAY_INT=32;
+    /** xpression type: double[] */
+    public static final int XT_ARRAY_DOUBLE=33;
+    /** xpression type: String[] (currently not used, Vector is used instead) */
+    public static final int XT_ARRAY_STR=34;
+    /** internal use only! this constant should never appear in a REXP */
+    public static final int XT_ARRAY_BOOL_UA=35;
+    /** xpression type: RBool[] */
+    public static final int XT_ARRAY_BOOL=36;
+    /** xpression type: raw (byte[])
+       @since Rserve 0.4-? */
+    public static final int XT_RAW=37;
+    /** xpression type: Complex[]
+       @since Rserve 0.5 */
+    public static final int XT_ARRAY_CPLX=38;
+    /** xpression type: unknown; no assumptions can be made about the content */
+    public static final int XT_UNKNOWN=48;
+
+    /** xpression type: RFactor; this XT is internally generated (ergo is does not come from Rsrv.h) to support RFactor class which is built from XT_ARRAY_INT */
+    public static final int XT_FACTOR=127; 
+
+       /** used for transport only - has attribute */
+       private static final int XT_HAS_ATTR=128;
+          
+       int type;
+       REXPFactory attr;
+       REXP cont;
+       RList rootList;
+       
+    public REXP getREXP() { return cont; }
+    public REXPList getAttr() { return (attr==null)?null:(REXPList)attr.cont; }
+       
+       public REXPFactory() {
+       }
+       
+       public REXPFactory(REXP r) throws REXPMismatchException {
+               if (r == null) r=new REXPNull();
+               REXPList a = r._attr();
+               cont = r;
+               if (a != null) attr = new REXPFactory(a);
+               if (r instanceof REXPNull) {
+                       type=XT_NULL;
+               } else if (r instanceof REXPList) {
+                       RList l = r.asList();
+                       type = l.isNamed()?XT_LIST_TAG:XT_LIST_NOTAG;
+                       if (r instanceof REXPLanguage)
+                               type = (type==XT_LIST_TAG)?XT_LANG_TAG:XT_LANG_NOTAG;
+               } else if (r instanceof REXPGenericVector) {
+                       type = XT_VECTOR; // FIXME: may have to adjust names attr
+               } else if (r instanceof REXPS4) {
+                       type = XT_S4;
+               } else if (r instanceof REXPInteger) { // this includes factor - FIXME: do we need speacial handling?
+                       type = XT_ARRAY_INT;
+               } else if (r instanceof REXPDouble) {
+                       type = XT_ARRAY_DOUBLE;
+               } else if (r instanceof REXPString) {
+                       type = XT_ARRAY_STR;
+               } else if (r instanceof REXPSymbol) {
+                       type = XT_SYMNAME;
+               } else if (r instanceof REXPRaw) {
+                       type = XT_RAW;
+               } else if (r instanceof REXPLogical) {
+                       type = XT_ARRAY_BOOL;
+               } else {
+                       // throw new REXPMismatchException(r, "decode");
+                       System.err.println("*** REXPFactory unable to interpret "+r);
+               }
+       }
+
+    /** parses byte buffer for binary representation of xpressions - read one xpression slot (descends recursively for aggregated xpressions such as lists, vectors etc.)
+               @param buf buffer containing the binary representation
+               @param o offset in the buffer to start at
+        @return position just behind the parsed xpression. Can be use for successive calls to {@link #parseREXP} if more than one expression is stored in the binary array. */
+    public int parseREXP(byte[] buf, int o) throws REXPMismatchException {
+               int xl = RTalk.getLen(buf,o);
+               boolean hasAtt = ((buf[o]&128)!=0);
+        boolean isLong = ((buf[o]&64)!=0);
+               int xt = (int)(buf[o]&63);
+        //System.out.println("parseREXP: type="+xt+", len="+xl+", hasAtt="+hasAtt+", isLong="+isLong);
+        if (isLong) o+=4;
+        o+=4;
+               int eox=o+xl;
+       
+               type=xt; attr=new REXPFactory(); cont=null;
+               if (hasAtt) o = attr.parseREXP(buf, o);
+               if (xt==XT_NULL) {
+                       cont = new REXPNull(getAttr());
+                       return o;
+               }
+               if (xt==XT_DOUBLE) {
+                       long lr = RTalk.getLong(buf,o);
+                       double[] d = new double[] { Double.longBitsToDouble(lr) };
+                       o+=8;
+                       if (o!=eox) {
+                               System.err.println("Warning: double SEXP size mismatch\n");
+                               o=eox;
+                       }
+                       cont = new REXPDouble(d, getAttr());
+                       return o;
+               }
+               if (xt==XT_ARRAY_DOUBLE) {
+                       int as=(eox-o)/8,i=0;
+                       double[] d=new double[as];
+                       while (o<eox) {
+                               d[i]=Double.longBitsToDouble(RTalk.getLong(buf,o));
+                               o+=8;
+                               i++;
+                       }
+                       if (o!=eox) {
+                               System.err.println("Warning: double array SEXP size mismatch\n");
+                               o=eox;
+                       }
+                       cont = new REXPDouble(d, getAttr());
+                       return o;
+               }
+               if (xt==XT_BOOL) {
+                       byte b[] = new byte[] { buf[o] };
+                       if (b[0] != 0 && b[0] != 1) b[0] = REXPLogical.NA;
+                       cont = new REXPLogical(b, getAttr());
+                       o++;
+                       if (o!=eox) {
+                if (eox!=o+3) // o+3 could happen if the result was aligned (1 byte data + 3 bytes padding)
+                    System.err.println("Warning: bool SEXP size mismatch\n");
+                               o=eox;
+                       }
+                       return o;
+               }
+               if (xt==XT_ARRAY_BOOL_UA) {
+                       int as=(eox-o), i=0;
+                       byte[] d=new byte[as];
+                       System.arraycopy(buf,o,d,0,eox-o);
+                       o = eox;
+                       for (int j = 0; j < d.length; j++) if (d[j] != 0 && d[j] != 1) d[j] = REXPLogical.NA;
+                       cont = new REXPLogical(d, getAttr());
+                       return o;
+               }
+        if (xt==XT_ARRAY_BOOL) {
+            int as=RTalk.getInt(buf,o);
+            o+=4;
+            byte[] d=new byte[as];
+                       System.arraycopy(buf,o,d,0,as);
+                       for (int j = 0; j < d.length; j++) if (d[j] != 0 && d[j] != 1) d[j] = REXPLogical.NA;
+                       o = eox;
+                       cont = new REXPLogical(d, getAttr());
+            return o;
+        }
+        if (xt==XT_INT) {
+                       int i[] = new int[] { RTalk.getInt(buf,o) };
+                       cont = new REXPInteger(i, getAttr());
+                       o+=4;
+                       if (o!=eox) {
+                               System.err.println("Warning: int SEXP size mismatch\n");
+                               o=eox;
+                       }
+                       return o;
+               }
+               if (xt==XT_ARRAY_INT) {
+                       int as=(eox-o)/4,i=0;
+                       int[] d=new int[as];
+                       while (o<eox) {
+                               d[i]=RTalk.getInt(buf,o);
+                               o+=4;
+                               i++;
+                       }
+                       if (o!=eox) {
+                               System.err.println("Warning: int array SEXP size mismatch\n");
+                               o=eox;
+                       }
+                       cont = null;
+                       // hack for lists - special lists attached to int are factors
+                       try {
+                           if (getAttr()!=null) {
+                                       REXP ca = getAttr().asList().at("class");
+                                       REXP ls = getAttr().asList().at("levels");
+                                       if (ca != null && ls != null && ca.asString().equals("factor")) {
+                                               // R uses 1-based index, Java uses 0-based one
+                                               cont = new REXPFactor(d, ls.asStrings(), getAttr());
+                                               xt = XT_FACTOR;
+                                       }
+                           }
+                       } catch (Exception e) {
+                       }
+                       if (cont == null) cont = new REXPInteger(d, getAttr());
+                       return o;
+               }
+        if (xt==XT_RAW) {
+            int as=RTalk.getInt(buf,o);
+            o+=4;
+            byte[] d=new byte[as];
+                       System.arraycopy(buf,o,d,0,as);
+                       o = eox;
+                       cont = new REXPRaw(d, getAttr());
+            return o;
+        }
+               if (xt==XT_LIST_NOTAG || xt==XT_LIST_TAG ||
+                       xt==XT_LANG_NOTAG || xt==XT_LANG_TAG) {
+                       REXPFactory lc = new REXPFactory();
+                       REXPFactory nf = new REXPFactory();
+                       RList l = new RList();
+                       while (o<eox) {
+                               String name = null;
+                               o = lc.parseREXP(buf, o);
+                               if (xt==XT_LIST_TAG || xt==XT_LANG_TAG) {
+                                       o = nf.parseREXP(buf, o);
+                                       if (nf.cont.isSymbol() || nf.cont.isString()) name = nf.cont.asString();
+                               }
+                               if (name==null) l.add(lc.cont);
+                               else l.put(name, lc.cont);
+                       }
+                       cont = (xt==XT_LANG_NOTAG || xt==XT_LANG_TAG)?
+                               new REXPLanguage(l, getAttr()):
+                               new REXPList(l, getAttr());
+                       if (o!=eox) {
+                               System.err.println("Warning: int list SEXP size mismatch\n");
+                               o=eox;
+                       }
+                       return o;
+               }
+               if (xt==XT_LIST || xt==XT_LANG) { //old-style lists, for comaptibility with older Rserve versions - rather inefficient since we have to convert the recusively stored structures into a flat structure
+                       boolean isRoot = false;
+                       if (rootList == null) {
+                               rootList = new RList();
+                               isRoot = true;
+                       }
+                       REXPFactory headf = new REXPFactory();
+                       REXPFactory tagf = new REXPFactory();
+                       o = headf.parseREXP(buf, o);
+                       int elIndex = rootList.size();
+                       rootList.add(headf.cont);
+                       //System.out.println("HEAD="+headf.cont);
+                       o = parseREXP(buf, o); // we use ourselves recursively for the body
+                       if (o < eox) {
+                               o = tagf.parseREXP(buf, o);
+                               //System.out.println("TAG="+tagf.cont);
+                               if (tagf.cont != null && (tagf.cont.isString() || tagf.cont.isSymbol()))
+                                       rootList.setKeyAt(elIndex, tagf.cont.asString());
+                       }
+                       if (isRoot) {
+                               cont = (xt==XT_LIST)?
+                               new REXPList(rootList, getAttr()):
+                               new REXPLanguage(rootList, getAttr());
+                               rootList = null;
+                               //System.out.println("result="+cont);
+                       }
+                       return o;
+               }
+               if (xt==XT_VECTOR || xt==XT_VECTOR_EXP) {
+                       Vector v=new Vector(); //FIXME: could we use RList?
+                       while(o<eox) {
+                               REXPFactory xx=new REXPFactory();
+                               o = xx.parseREXP(buf,o);
+                               v.addElement(xx.cont);
+                       }
+                       if (o!=eox) {
+                               System.err.println("Warning: int vector SEXP size mismatch\n");
+                               o=eox;
+                       }
+                       // fixup for lists since they're stored as attributes of vectors
+                       if (getAttr()!=null && getAttr().asList().at("names") != null) {
+                               REXP nam = getAttr().asList().at("names");
+                               String names[] = null;
+                               if (nam.isString()) names = nam.asStrings();
+                               else if (nam.isVector()) { // names could be a vector if supplied by old Rserve
+                                       RList l = nam.asList();
+                                       Object oa[] = l.toArray();
+                                       names = new String[oa.length];
+                                       for(int i = 0; i < oa.length; i++) names[i] = ((REXP)oa[i]).asString();
+                               }
+                               RList l = new RList(v, names);
+                               cont = (xt==XT_VECTOR_EXP)?
+                                       new REXPExpressionVector(l, getAttr()):
+                                       new REXPGenericVector(l, getAttr());
+                       } else
+                               cont = (xt==XT_VECTOR_EXP)?
+                                       new REXPExpressionVector(new RList(v), getAttr()):
+                                       new REXPGenericVector(new RList(v), getAttr());
+                       return o;
+               }
+               if (xt==XT_ARRAY_STR) {
+                       int c = 0, i = o;
+                       /* count the entries */
+                       while (i < eox) if (buf[i++] == 0) c++;
+                       String s[] = new String[c];
+                       if (c > 0) {
+                               c = 0; i = o;
+                               while (o < eox) {
+                                       if (buf[o] == 0) {
+                                               try {
+                                                       if (buf[i] == -1) { /* if the first byte is 0xff (-1 in signed char) then it either needs to be skipped (doubling) or there is an NA value */
+                                                               if (buf[i + 1] == 0)
+                                                                       s[c] = null; /* NA */
+                                                               else
+                                                                       s[c] = new String(buf, i + 1, o - i - 1, RConnection.transferCharset);
+                                                       } else
+                                                               s[c] = new String(buf, i, o - i, RConnection.transferCharset);
+                                               } catch (java.io.UnsupportedEncodingException ex) {
+                                                       s[c]="";
+                                               }
+                                               c++;
+                                               i = o + 1;
+                                       }
+                                       o++;
+                               }
+                       }
+                       cont = new REXPString(s, getAttr());
+                       return o;
+               }
+               if (xt==XT_VECTOR_STR) {
+                       Vector v=new Vector();
+                       while(o<eox) {
+                               REXPFactory xx=new REXPFactory();
+                               o = xx.parseREXP(buf,o);
+                               v.addElement(xx.cont.asString());
+                       }
+                       if (o!=eox) {
+                               System.err.println("Warning: int vector SEXP size mismatch\n");
+                               o=eox;
+                       }
+                       String sa[] = new String[v.size()];
+                       int i = 0; while (i < sa.length) { sa[i]=(String)v.get(i); i++; }
+                       cont = new REXPString(sa, getAttr());
+                       return o;
+               }
+               if (xt==XT_STR||xt==XT_SYMNAME) {
+                       int i = o;
+                       while (buf[i]!=0 && i<eox) i++;
+                       try {
+                               if (xt==XT_STR)
+                                       cont = new REXPString(new String[] { new String(buf, o, i-o, RConnection.transferCharset) }, getAttr());
+                               else
+                                       cont = new REXPSymbol(new String(buf, o, i-o, RConnection.transferCharset));                                    
+                       } catch(Exception e) {
+                               System.err.println("unable to convert string\n");
+                               cont = null;
+                       }
+                       o = eox;
+                       return o;
+               }
+               if (xt==XT_SYM) {
+                       REXPFactory sym = new REXPFactory();
+                       o = sym.parseREXP(buf, o); // PRINTNAME that's all we will use
+                       cont = new REXPSymbol(sym.getREXP().asString()); // content of a symbol is its printname string (so far)
+                       o=eox;
+                       return o;
+               }
+               
+               if (xt==XT_CLOS) {
+                       /*
+                       REXP form=new REXP();
+                       REXP body=new REXP();
+                       o=parseREXP(form,buf,o);
+                       o=parseREXP(body,buf,o);
+                       if (o!=eox) {
+                               System.err.println("Warning: closure SEXP size mismatch\n");
+                               o=eox;
+                       }
+                       x.cont=body;
+                        */
+                       o=eox;
+                       return o;
+               }
+               
+               if (xt==XT_UNKNOWN) {
+                       cont = new REXPUnknown(RTalk.getInt(buf,o), getAttr());
+                       o=eox;
+                       return o;
+               }
+               
+               if (xt==XT_S4) {
+                       cont = new REXPS4(getAttr());
+                       o=eox;
+                       return o;
+               }
+               
+               cont = null;
+               o = eox;
+               System.err.println("unhandled type: "+xt);
+               return o;
+    }
+
+    /** Calculates the length of the binary representation of the REXP including all headers. This is the amount of memory necessary to store the REXP via {@link #getBinaryRepresentation}.
+        <p>Please note that currently only XT_[ARRAY_]INT, XT_[ARRAY_]DOUBLE and XT_[ARRAY_]STR are supported! All other types will return 4 which is the size of the header.
+        @return length of the REXP including headers (4 or 8 bytes)*/
+    public int getBinaryLength() throws REXPMismatchException {
+               int l=0;
+               int rxt = type;
+               if (type==XT_LIST || type==XT_LIST_TAG || type==XT_LIST_NOTAG)
+                       rxt=(cont.asList()!=null && cont.asList().isNamed())?XT_LIST_TAG:XT_LIST_NOTAG;
+               //System.out.print("len["+xtName(type)+"/"+xtName(rxt)+"] ");
+               if (type==XT_VECTOR_STR) rxt=XT_ARRAY_STR; // VECTOR_STR is broken right now
+               
+               /*
+               if (type==XT_VECTOR && cont.asList()!=null && cont.asList().isNamed())
+                       setAttribute("names",new REXPString(cont.asList().keys()));
+                */
+               
+               boolean hasAttr = false;
+               REXPList a = getAttr();
+               RList al = null;
+               if (a!=null) al = a.asList();
+               if (al != null && al.size()>0) hasAttr=true;
+               if (hasAttr)
+                       l+=attr.getBinaryLength();
+               switch (rxt) {
+                       case XT_NULL:
+                       case XT_S4:
+                               break;
+                       case XT_INT: l+=4; break;
+                       case XT_DOUBLE: l+=8; break;
+                       case XT_RAW: l+=4 + cont.asBytes().length; if ((l&3)>0) l=l-(l&3)+4; break;
+                       case XT_STR:
+                       case XT_SYMNAME:
+                               l+=(cont==null)?1:(cont.asString().length()+1);
+                               if ((l&3)>0) l=l-(l&3)+4;
+                                       break;
+                       case XT_ARRAY_INT: l+=cont.asIntegers().length*4; break;
+                       case XT_ARRAY_DOUBLE: l+=cont.asDoubles().length*8; break;
+                       case XT_ARRAY_CPLX: l+=cont.asDoubles().length*8; break;
+                       case XT_ARRAY_BOOL: l += cont.asBytes().length + 4; if ((l & 3) > 0) l = l - (l & 3) + 4; break;
+                       case XT_LIST_TAG:
+                       case XT_LIST_NOTAG:
+                       case XT_LANG_TAG:
+                       case XT_LANG_NOTAG:
+                       case XT_LIST:
+                       case XT_VECTOR:
+                       {
+                               final RList lst = cont.asList();
+                               int i=0;
+                               while (i<lst.size()) {
+                                       REXP x = lst.at(i);
+                                       l += (x==null)?4:(new REXPFactory(x).getBinaryLength());
+                                       if (rxt==XT_LIST_TAG) {
+                                               int pl=l;
+                                               String s = lst.keyAt(i);
+                                               l+=4; // header for a symbol
+                                               l+=(s==null)?1:(s.length()+1);
+                                               if ((l&3)>0) l=l-(l&3)+4;
+                                               // System.out.println("TAG length: "+(l-pl));
+                                       }
+                                       i++;
+                               }
+                               if ((l&3)>0) l=l-(l&3)+4;
+                               break;
+                       }
+                       case XT_ARRAY_STR:
+                       {
+                               String sa[] = cont.asStrings();
+                               int i=0;
+                               while (i < sa.length) {
+                                       if (sa[i] != null) {
+                                               try {
+                                                       byte b[] = sa[i].getBytes(RConnection.transferCharset);
+                                                       if (b.length > 0) {
+                                                               if (b[0] == -1) l++;
+                                                               l += b.length;
+                                                       }
+                                                       b = null;
+                                               } catch (java.io.UnsupportedEncodingException uex) {
+                                                       // FIXME: we should so something ... so far we hope noone's gonna mess with the encoding
+                                               }
+                                       } else l++; // NA = -1
+                                       l++;
+                                       i++;
+                               }
+                               if ((l&3)>0) l=l-(l&3)+4;
+                               break;
+                       }
+               } // switch
+        if (l>0xfffff0) l+=4; // large data need 4 more bytes
+                                                         // System.out.println("len:"+(l+4)+" "+xtName(rxt)+"/"+xtName(type)+" "+cont);
+               return l+4; // add the header
+    }
+
+    /** Stores the REXP in its binary (ready-to-send) representation including header into a buffer and returns the index of the byte behind the REXP.
+        <p>Please note that currently only XT_[ARRAY_]INT, XT_[ARRAY_]DOUBLE and XT_[ARRAY_]STR are supported! All other types will be stored as SEXP of the length 0 without any contents.
+        @param buf buffer to store the REXP binary into
+        @param off offset of the first byte where to store the REXP
+        @return the offset of the first byte behind the stored REXP */
+    public int getBinaryRepresentation(byte[] buf, int off) throws REXPMismatchException {
+               int myl=getBinaryLength();
+        boolean isLarge=(myl>0xfffff0);
+               boolean hasAttr = false;
+               final REXPList a = getAttr();
+               RList al = null;
+               if (a != null) al = a.asList();
+               if (al != null && al.size()>0) hasAttr=true;
+               int rxt=type, ooff=off;
+               if (type==XT_VECTOR_STR) rxt=XT_ARRAY_STR; // VECTOR_STR is broken right now
+               if (type==XT_LIST || type==XT_LIST_TAG || type==XT_LIST_NOTAG)
+                       rxt=(cont.asList()!=null && cont.asList().isNamed())?XT_LIST_TAG:XT_LIST_NOTAG;
+               // System.out.println("@"+off+": "+xtName(rxt)+"/"+xtName(type)+" "+cont+" ("+myl+"/"+buf.length+") att="+hasAttr);
+        RTalk.setHdr(rxt|(hasAttr?XT_HAS_ATTR:0),myl-(isLarge?8:4),buf,off);
+        off+=(isLarge?8:4);
+               if (hasAttr) off=attr.getBinaryRepresentation(buf, off);
+               switch (rxt) {
+                       case XT_S4:
+                       case XT_NULL:
+                               break;
+                       case XT_INT: RTalk.setInt(cont.asInteger(),buf,off); break;
+                       case XT_DOUBLE: RTalk.setLong(Double.doubleToRawLongBits(cont.asDouble()),buf,off); break;
+                       case XT_ARRAY_INT:
+                       {
+                               int ia[]=cont.asIntegers();
+                               int i=0, io=off;
+                               while(i<ia.length) {
+                                       RTalk.setInt(ia[i++],buf,io); io+=4;
+                               }
+                               break;
+                       }
+                       case XT_ARRAY_BOOL:
+                       {
+                               byte ba[] = cont.asBytes();
+                               int io = off;
+                               RTalk.setInt(ba.length, buf, io);
+                               io += 4;
+                               if (ba.length > 0) {
+                                       for(int i =0; i < ba.length; i++)
+                                               buf[io++] = (byte) ( (ba[i] == REXPLogical.NA) ? 2 : ((ba[i] == REXPLogical.FALSE) ? 0 : 1) );
+                                       while ((io & 3) != 0) buf[io++] = 3;
+                               }
+                               break;
+                       }
+                       case XT_ARRAY_DOUBLE:
+                       {
+                               double da[]=cont.asDoubles();
+                               int i=0, io=off;
+                               while(i<da.length) {
+                                       RTalk.setLong(Double.doubleToRawLongBits(da[i++]),buf,io); io+=8;
+                               }
+                               break;
+                       }
+                       case XT_RAW:
+                       {
+                               byte by[] = cont.asBytes();
+                               RTalk.setInt(by.length, buf, off); off+=4;
+                               System.arraycopy(by, 0, buf, off, by.length);
+                               break;
+                       }
+                       case XT_ARRAY_STR:
+                       {
+                               String sa[] = cont.asStrings();
+                               int i = 0, io = off;
+                               while (i < sa.length) {
+                                       if (sa[i] != null) {
+                                               try {
+                                                       byte b[] = sa[i].getBytes(RConnection.transferCharset);
+                                                       if (b.length > 0) {
+                                                               if (b[0] == -1) /* if the first entry happens to be -1 then we need to double it so it doesn't get confused with NAs */
+                                                                       buf[io++] = -1;
+                                                               System.arraycopy(b, 0, buf, io, b.length);
+                                                               io += b.length;
+                                                       }
+                                                       b = null;
+                                               } catch (java.io.UnsupportedEncodingException uex) {
+                                                       // FIXME: we should so something ... so far we hope noone's gonna mess with the encoding
+                                               }
+                                       } else
+                                               buf[io++] = -1; /* NAs are stored as 0xff (-1 in signed bytes) */
+                                       buf[io++] = 0;
+                                       i++;
+                               }
+                               i = io - off;
+                               while ((i & 3) != 0) { buf[io++] = 1; i++; } // padding if necessary..
+                               break;
+                       }
+                       case XT_LIST_TAG:
+                       case XT_LIST_NOTAG:
+                       case XT_LANG_TAG:
+                       case XT_LANG_NOTAG:
+                       case XT_LIST:
+                       case XT_VECTOR:
+                       case XT_VECTOR_EXP:
+                       {
+                               int io = off;
+                               final RList lst = cont.asList();
+                               if (lst != null) {
+                                       int i=0;
+                                       while (i<lst.size()) {
+                                               REXP x = lst.at(i);
+                                               if (x == null) x=new REXPNull();
+                                               io = new REXPFactory(x).getBinaryRepresentation(buf, io);
+                                               if (rxt == XT_LIST_TAG || rxt == XT_LANG_TAG)
+                                                       io = new REXPFactory(new REXPSymbol(lst.keyAt(i))).getBinaryRepresentation(buf, io);
+                                               i++;
+                                       }
+                               }
+                               // System.out.println("io="+io+", expected: "+(ooff+myl));
+                               break;
+                       }
+           
+                       case XT_SYMNAME:
+                       case XT_STR:
+                               getStringBinaryRepresentation(buf, off, cont.asString());
+                               break;
+               }
+               return ooff+myl;
+    }
+
+    public static int getStringBinaryRepresentation(byte[] buf, int off, String s) {
+               if (s==null) s="";
+               int io=off;
+               try {
+                       byte b[]=s.getBytes(RConnection.transferCharset);
+                       // System.out.println("<str> @"+off+", len "+b.length+" (cont "+buf.length+") \""+s+"\"");
+                       System.arraycopy(b,0,buf,io,b.length);
+                       io+=b.length;
+                       b=null;
+               } catch (java.io.UnsupportedEncodingException uex) {
+                       // FIXME: we should so something ... so far we hope noone's gonna mess with the encoding
+               }
+               buf[io++]=0;
+               while ((io&3)!=0) buf[io++]=0; // padding if necessary..
+               return io;
+    }
+
+    /** returns human-readable name of the xpression type as string. Arrays are denoted by a trailing asterisk (*).
+       @param xt xpression type
+       @return name of the xpression type */
+    public static String xtName(int xt) {
+               if (xt==XT_NULL) return "NULL";
+               if (xt==XT_INT) return "INT";
+               if (xt==XT_STR) return "STRING";
+               if (xt==XT_DOUBLE) return "REAL";
+               if (xt==XT_BOOL) return "BOOL";
+               if (xt==XT_ARRAY_INT) return "INT*";
+               if (xt==XT_ARRAY_STR) return "STRING*";
+               if (xt==XT_ARRAY_DOUBLE) return "REAL*";
+               if (xt==XT_ARRAY_BOOL) return "BOOL*";
+               if (xt==XT_ARRAY_CPLX) return "COMPLEX*";
+               if (xt==XT_SYM) return "SYMBOL";
+               if (xt==XT_SYMNAME) return "SYMNAME";
+               if (xt==XT_LANG) return "LANG";
+               if (xt==XT_LIST) return "LIST";
+               if (xt==XT_LIST_TAG) return "LIST+T";
+               if (xt==XT_LIST_NOTAG) return "LIST/T";
+               if (xt==XT_LANG_TAG) return "LANG+T";
+               if (xt==XT_LANG_NOTAG) return "LANG/T";
+               if (xt==XT_CLOS) return "CLOS";
+               if (xt==XT_RAW) return "RAW";
+               if (xt==XT_S4) return "S4";
+               if (xt==XT_VECTOR) return "VECTOR";
+               if (xt==XT_VECTOR_STR) return "STRING[]";
+               if (xt==XT_VECTOR_EXP) return "EXPR[]";
+               if (xt==XT_FACTOR) return "FACTOR";
+               if (xt==XT_UNKNOWN) return "UNKNOWN";
+               return "<unknown "+xt+">";
+    }  
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/protocol/RPacket.java b/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/protocol/RPacket.java
new file mode 100644 (file)
index 0000000..db19bff
--- /dev/null
@@ -0,0 +1,42 @@
+package org.rosuda.REngine.Rserve.protocol;
+
+// JRclient library - client interface to Rserve, see http://www.rosuda.org/Rserve/
+// Copyright (C) 2004 Simon Urbanek
+// --- for licensing information see LICENSE file in the original JRclient distribution ---
+
+/** small class encapsulating packets from/to Rserv
+    @version $Id$
+*/
+public class RPacket {
+    int cmd;
+    byte[] cont;
+
+    /** construct new packet
+       @param Rcmd command
+       @param Rcont content */
+    public RPacket(int Rcmd, byte[] Rcont) {
+       cmd=Rcmd; cont=Rcont;
+    }
+    
+    /** get command
+        @return command */
+    public int getCmd() { return cmd; }
+    
+    /** check last response for RESP_OK
+       @return <code>true</code> if last response was OK */
+    public boolean isOk() { return ((cmd&15)==1); }
+    
+    /** check last response for RESP_ERR
+       @return <code>true</code> if last response was ERROR */
+    public boolean isError() { return ((cmd&15)==2); }
+    
+    /** get status code of last response
+       @return status code returned on last response */
+    public int getStat() { return ((cmd>>24)&127); }
+
+    /** get content
+       @return inner package content */
+    public byte[] getCont() { return cont; }
+
+    public String toString() { return "RPacket[cmd="+cmd+",len="+((cont==null)?"<null>":(""+cont.length))+"]"; }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/protocol/RTalk.java b/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/protocol/RTalk.java
new file mode 100644 (file)
index 0000000..b723c2c
--- /dev/null
@@ -0,0 +1,271 @@
+package org.rosuda.REngine.Rserve.protocol;
+
+// JRclient library - client interface to Rserve, see http://www.rosuda.org/Rserve/
+// Copyright (C) 2004 Simon Urbanek
+// --- for licensing information see LICENSE file in the original JRclient distribution ---
+
+import java.util.*;
+import java.io.*;
+import java.net.*;
+
+import org.rosuda.REngine.Rserve.RConnection;
+
+/** This class encapsulates the QAP1 protocol used by Rserv.
+    it is independent of the underying protocol(s), therefore RTalk
+    can be used over any transport layer
+    <p>
+    The current implementation supports long (0.3+/0102) data format only
+    up to 32-bit and only for incoming packets.
+    <p>
+    @version $Id$
+*/
+public class RTalk {
+    public static final int DT_INT=1;
+    public static final int DT_CHAR=2;
+    public static final int DT_DOUBLE=3;
+    public static final int DT_STRING=4;
+    public static final int DT_BYTESTREAM=5;
+    public static final int DT_SEXP=10;
+    public static final int DT_ARRAY=11;
+
+    /** this is a flag saying that the contents is large (>0xfffff0) and hence uses 56-bit length field */
+    public static final int DT_LARGE=64;
+
+    public static final int CMD_login=0x001;
+    public static final int CMD_voidEval=0x002;
+    public static final int CMD_eval=0x003;
+    public static final int CMD_shutdown=0x004;
+    public static final int CMD_openFile=0x010;
+    public static final int CMD_createFile=0x011;
+    public static final int CMD_closeFile=0x012;
+    public static final int CMD_readFile=0x013;
+    public static final int CMD_writeFile=0x014;
+    public static final int CMD_removeFile=0x015;
+    public static final int CMD_setSEXP=0x020;
+    public static final int CMD_assignSEXP=0x021;
+    
+    public static final int CMD_setBufferSize=0x081;
+    public static final int CMD_setEncoding=0x082;
+
+    public static final int CMD_detachSession=0x030;
+    public static final int CMD_detachedVoidEval=0x031;
+    public static final int CMD_attachSession=0x032;
+
+    // control commands since 0.6-0
+    public static final int CMD_ctrlEval=0x42;
+    public static final int CMD_ctrlSource=0x45;
+    public static final int CMD_ctrlShutdown=0x44; 
+    
+    // errors as returned by Rserve
+    public static final int ERR_auth_failed=0x41;
+    public static final int ERR_conn_broken=0x42;
+    public static final int ERR_inv_cmd=0x43;
+    public static final int ERR_inv_par=0x44;
+    public static final int ERR_Rerror=0x45;
+    public static final int ERR_IOerror=0x46;
+    public static final int ERR_not_open=0x47;
+    public static final int ERR_access_denied=0x48;
+    public static final int ERR_unsupported_cmd=0x49;
+    public static final int ERR_unknown_cmd=0x4a;
+    public static final int ERR_data_overflow=0x4b;
+    public static final int ERR_object_too_big=0x4c;
+    public static final int ERR_out_of_mem=0x4d;
+    public static final int ERR_ctrl_closed=0x4e;
+    public static final int ERR_session_busy=0x50;
+    public static final int ERR_detach_failed=0x51;
+   
+    InputStream is;
+    OutputStream os;
+    
+    /** constructor; parameters specify the streams
+       @param sis socket input stream
+       @param sos socket output stream */
+
+    public RTalk(InputStream sis, OutputStream sos) {
+       is=sis; os=sos;
+    }
+
+    /** writes bit-wise int to a byte buffer at specified position in Intel-endian form
+       @param v value to be written
+       @param buf buffer
+       @param o offset in the buffer to start at. An int takes always 4 bytes */
+    public static void setInt(int v, byte[] buf, int o) {
+       buf[o]=(byte)(v&255); o++;
+       buf[o]=(byte)((v&0xff00)>>8); o++;
+       buf[o]=(byte)((v&0xff0000)>>16); o++;
+       buf[o]=(byte)((v&0xff000000)>>24);
+    }
+
+    /** writes cmd/resp/type byte + 3/7 bytes len into a byte buffer at specified offset.
+       @param ty type/cmd/resp byte
+       @param len length
+       @param buf buffer
+       @param o offset
+        @return offset in buf just after the header. Please note that since Rserve 0.3 the header can be either 4 or 8 bytes long, depending on the len parameter.
+        */
+    public static int setHdr(int ty, int len, byte[] buf, int o) {
+        buf[o]=(byte)((ty&255)|((len>0xfffff0)?DT_LARGE:0)); o++;
+       buf[o]=(byte)(len&255); o++;
+       buf[o]=(byte)((len&0xff00)>>8); o++;
+       buf[o]=(byte)((len&0xff0000)>>16); o++;
+        if (len>0xfffff0) { // for large data we need to set the next 4 bytes as well
+            buf[o]=(byte)((len&0xff000000)>>24); o++;
+            buf[o]=0; o++; // since len is int, we get 32-bits only
+            buf[o]=0; o++;
+            buf[o]=0; o++;
+        }
+        return o;
+    }
+
+    /** creates a new header according to the type and length of the parameter
+        @param ty type/cmd/resp byte
+        @param len length */        
+    public static byte[] newHdr(int ty, int len) {
+        byte[] hdr=new byte[(len>0xfffff0)?8:4];
+        setHdr(ty,len,hdr,0);
+        return hdr;
+    }
+    
+    /** converts bit-wise stored int in Intel-endian form into Java int
+       @param buf buffer containg the representation
+       @param o offset where to start (4 bytes will be used)
+       @return the int value. no bounds checking is done so you need to
+               make sure that the buffer is big enough */
+    public static int getInt(byte[] buf, int o) {
+       return ((buf[o]&255)|((buf[o+1]&255)<<8)|((buf[o+2]&255)<<16)|((buf[o+3]&255)<<24));
+    }
+
+    /** converts bit-wise stored length from a header. "long" format is supported up to 32-bit
+       @param buf buffer
+       @param o offset of the header (length is at o+1)
+       @return length */
+    public static int getLen(byte[] buf, int o) {
+
+        return
+        ((buf[o]&64)>0)? // "long" format; still - we support 32-bit only
+        ((buf[o+1]&255)|((buf[o+2]&255)<<8)|((buf[o+3]&255)<<16)|((buf[o+4]&255)<<24))
+        :
+        ((buf[o+1]&255)|((buf[o+2]&255)<<8)|((buf[o+3]&255)<<16));
+    }
+
+    /** converts bit-wise Intel-endian format into long
+       @param buf buffer
+       @param o offset (8 bytes will be used)
+       @return long value */
+    public static long getLong(byte[] buf, int o) {
+       long low=((long)getInt(buf,o))&0xffffffffL;
+       long hi=((long)getInt(buf,o+4))&0xffffffffL;
+       hi<<=32; hi|=low;
+       return hi;
+    }
+
+    public static void setLong(long l, byte[] buf, int o) {
+       setInt((int)(l&0xffffffffL),buf,o);
+       setInt((int)(l>>32),buf,o+4);
+    }
+
+    /** sends a request with no attached parameters
+       @param cmd command
+       @return returned packet or <code>null</code> if something went wrong */
+    public RPacket request(int cmd) {
+        byte[] d = new byte[0];
+        return request(cmd,d);
+    }
+
+    /** sends a request with attached parameters
+        @param cmd command
+        @param cont contents - parameters
+        @return returned packet or <code>null</code> if something went wrong */
+    public RPacket request(int cmd, byte[] cont) {
+        return request(cmd,null,cont,0,(cont==null)?0:cont.length);
+    }
+
+    /** sends a request with attached prefix and  parameters. Both prefix and cont can be <code>null</code>. Effectively <code>request(a,b,null)</code> and <code>request(a,null,b)</code> are equivalent.
+       @param cmd command - a special command of -1 prevents request from sending anything
+        @param prefix - this content is sent *before* cont. It is provided to save memory copy operations where a small header precedes a large data chunk (usually prefix conatins the parameter header and cont contains the actual data).
+        @param cont contents
+        @param offset offset in cont where to start sending (if <0 then 0 is assumed, if >cont.length then no cont is sent)
+        @param len number of bytes in cont to send (it is clipped to the length of cont if necessary)
+       @return returned packet or <code>null</code> if something went wrong */
+    public RPacket request(int cmd, byte[] prefix, byte[] cont, int offset, int len) {
+        if (cont!=null) {
+            if (offset>=cont.length) { cont=null; len=0; }
+            else if (len>cont.length-offset) len=cont.length-offset;
+        }
+        if (offset<0) offset=0;
+        if (len<0) len=0;
+        int contlen=(cont==null)?0:len;
+        if (prefix!=null && prefix.length>0) contlen+=prefix.length;
+       byte[] hdr=new byte[16];
+       setInt(cmd,hdr,0);
+       setInt(contlen,hdr,4);
+       for(int i=8;i<16;i++) hdr[i]=0;
+       try {
+           if (cmd!=-1) {
+               os.write(hdr);
+               if (prefix!=null && prefix.length>0)
+                   os.write(prefix);
+               if (cont!=null && cont.length>0)
+                   os.write(cont,offset,len);
+           }
+
+           byte[] ih=new byte[16];
+           if (is.read(ih)!=16)
+               return null;
+           int rep=getInt(ih,0);
+           int rl =getInt(ih,4);
+           if (rl>0) {
+               byte[] ct=new byte[rl];
+                int n=0;
+                while (n<rl) {
+                    int rd=is.read(ct,n,rl-n);
+                    n+=rd;
+                }
+               return new RPacket(rep,ct);
+           }
+           return new RPacket(rep,null);
+       } catch(Exception e) {
+           e.printStackTrace();
+           return null;
+       }
+    }
+
+    /** sends a request with one string parameter attached
+       @param cmd command
+       @param par parameter - length and DT_STRING will be prepended
+       @return returned packet or <code>null</code> if something went wrong */
+    public RPacket request(int cmd, String par) {
+       try {
+            byte[] b=par.getBytes(RConnection.transferCharset);
+            int sl=b.length+1;
+            if ((sl&3)>0) sl=(sl&0xfffffc)+4; // make sure the length is divisible by 4
+           byte[] rq=new byte[sl+5];
+            int i;
+           for(i=0;i<b.length;i++)
+                rq[i+4]=b[i];
+            while (i<sl) { // pad with 0
+                rq[i+4]=0; i++;
+            };
+           setHdr(DT_STRING,sl,rq,0);
+           return request(cmd,rq);
+       } catch (Exception e) {
+           e.printStackTrace();
+       }
+       return null;
+    }
+
+    /** sends a request with one string parameter attached
+        @param cmd command
+        @param par parameter of the type DT_INT
+        @return returned packet or <code>null</code> if something went wrong */
+    public RPacket request(int cmd, int par) {
+       try {
+           byte[] rq=new byte[8];
+           setInt(par,rq,4);
+           setHdr(DT_INT,4,rq,0);
+           return request(cmd,rq);
+       } catch (Exception e) {
+       };
+       return null;
+    }
+}
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/protocol/jcrypt.java b/org.simantics.r.scl/src/org/rosuda/REngine/Rserve/protocol/jcrypt.java
new file mode 100644 (file)
index 0000000..e815318
--- /dev/null
@@ -0,0 +1,617 @@
+package org.rosuda.REngine.Rserve.protocol;
+
+/****************************************************************************
+ * jcrypt.java
+ *
+ * Java-based implementation of the unix crypt command
+ *
+ * Based upon C source code written by Eric Young, eay@psych.uq.oz.au
+ *
+ ****************************************************************************/
+
+public class jcrypt
+{
+   private static final int ITERATIONS = 16;
+
+   private static final int con_salt[] =
+   {
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 
+      0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 
+      0x0A, 0x0B, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 
+      0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 
+      0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 
+      0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 
+      0x23, 0x24, 0x25, 0x20, 0x21, 0x22, 0x23, 0x24, 
+      0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 
+      0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 
+      0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 
+      0x3D, 0x3E, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 
+   };
+
+   private static final boolean shifts2[] =
+   {
+      false, false, true, true, true, true, true, true,
+      false, true,  true, true, true, true, true, false
+   };
+
+   private static final int skb[][] =
+   {
+      {
+         /* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */
+         0x00000000, 0x00000010, 0x20000000, 0x20000010, 
+         0x00010000, 0x00010010, 0x20010000, 0x20010010, 
+         0x00000800, 0x00000810, 0x20000800, 0x20000810, 
+         0x00010800, 0x00010810, 0x20010800, 0x20010810, 
+         0x00000020, 0x00000030, 0x20000020, 0x20000030, 
+         0x00010020, 0x00010030, 0x20010020, 0x20010030, 
+         0x00000820, 0x00000830, 0x20000820, 0x20000830, 
+         0x00010820, 0x00010830, 0x20010820, 0x20010830, 
+         0x00080000, 0x00080010, 0x20080000, 0x20080010, 
+         0x00090000, 0x00090010, 0x20090000, 0x20090010, 
+         0x00080800, 0x00080810, 0x20080800, 0x20080810, 
+         0x00090800, 0x00090810, 0x20090800, 0x20090810, 
+         0x00080020, 0x00080030, 0x20080020, 0x20080030, 
+         0x00090020, 0x00090030, 0x20090020, 0x20090030, 
+         0x00080820, 0x00080830, 0x20080820, 0x20080830, 
+         0x00090820, 0x00090830, 0x20090820, 0x20090830, 
+      },
+      {
+         /* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */
+         0x00000000, 0x02000000, 0x00002000, 0x02002000, 
+         0x00200000, 0x02200000, 0x00202000, 0x02202000, 
+         0x00000004, 0x02000004, 0x00002004, 0x02002004, 
+         0x00200004, 0x02200004, 0x00202004, 0x02202004, 
+         0x00000400, 0x02000400, 0x00002400, 0x02002400, 
+         0x00200400, 0x02200400, 0x00202400, 0x02202400, 
+         0x00000404, 0x02000404, 0x00002404, 0x02002404, 
+         0x00200404, 0x02200404, 0x00202404, 0x02202404, 
+         0x10000000, 0x12000000, 0x10002000, 0x12002000, 
+         0x10200000, 0x12200000, 0x10202000, 0x12202000, 
+         0x10000004, 0x12000004, 0x10002004, 0x12002004, 
+         0x10200004, 0x12200004, 0x10202004, 0x12202004, 
+         0x10000400, 0x12000400, 0x10002400, 0x12002400, 
+         0x10200400, 0x12200400, 0x10202400, 0x12202400, 
+         0x10000404, 0x12000404, 0x10002404, 0x12002404, 
+         0x10200404, 0x12200404, 0x10202404, 0x12202404, 
+      },
+      {
+         /* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */
+         0x00000000, 0x00000001, 0x00040000, 0x00040001, 
+         0x01000000, 0x01000001, 0x01040000, 0x01040001, 
+         0x00000002, 0x00000003, 0x00040002, 0x00040003, 
+         0x01000002, 0x01000003, 0x01040002, 0x01040003, 
+         0x00000200, 0x00000201, 0x00040200, 0x00040201, 
+         0x01000200, 0x01000201, 0x01040200, 0x01040201, 
+         0x00000202, 0x00000203, 0x00040202, 0x00040203, 
+         0x01000202, 0x01000203, 0x01040202, 0x01040203, 
+         0x08000000, 0x08000001, 0x08040000, 0x08040001, 
+         0x09000000, 0x09000001, 0x09040000, 0x09040001, 
+         0x08000002, 0x08000003, 0x08040002, 0x08040003, 
+         0x09000002, 0x09000003, 0x09040002, 0x09040003, 
+         0x08000200, 0x08000201, 0x08040200, 0x08040201, 
+         0x09000200, 0x09000201, 0x09040200, 0x09040201, 
+         0x08000202, 0x08000203, 0x08040202, 0x08040203, 
+         0x09000202, 0x09000203, 0x09040202, 0x09040203, 
+      },
+      {
+         /* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */
+         0x00000000, 0x00100000, 0x00000100, 0x00100100, 
+         0x00000008, 0x00100008, 0x00000108, 0x00100108, 
+         0x00001000, 0x00101000, 0x00001100, 0x00101100, 
+         0x00001008, 0x00101008, 0x00001108, 0x00101108, 
+         0x04000000, 0x04100000, 0x04000100, 0x04100100, 
+         0x04000008, 0x04100008, 0x04000108, 0x04100108, 
+         0x04001000, 0x04101000, 0x04001100, 0x04101100, 
+         0x04001008, 0x04101008, 0x04001108, 0x04101108, 
+         0x00020000, 0x00120000, 0x00020100, 0x00120100, 
+         0x00020008, 0x00120008, 0x00020108, 0x00120108, 
+         0x00021000, 0x00121000, 0x00021100, 0x00121100, 
+         0x00021008, 0x00121008, 0x00021108, 0x00121108, 
+         0x04020000, 0x04120000, 0x04020100, 0x04120100, 
+         0x04020008, 0x04120008, 0x04020108, 0x04120108, 
+         0x04021000, 0x04121000, 0x04021100, 0x04121100, 
+         0x04021008, 0x04121008, 0x04021108, 0x04121108, 
+      },
+      {
+         /* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */
+         0x00000000, 0x10000000, 0x00010000, 0x10010000, 
+         0x00000004, 0x10000004, 0x00010004, 0x10010004, 
+         0x20000000, 0x30000000, 0x20010000, 0x30010000, 
+         0x20000004, 0x30000004, 0x20010004, 0x30010004, 
+         0x00100000, 0x10100000, 0x00110000, 0x10110000, 
+         0x00100004, 0x10100004, 0x00110004, 0x10110004, 
+         0x20100000, 0x30100000, 0x20110000, 0x30110000, 
+         0x20100004, 0x30100004, 0x20110004, 0x30110004, 
+         0x00001000, 0x10001000, 0x00011000, 0x10011000, 
+         0x00001004, 0x10001004, 0x00011004, 0x10011004, 
+         0x20001000, 0x30001000, 0x20011000, 0x30011000, 
+         0x20001004, 0x30001004, 0x20011004, 0x30011004, 
+         0x00101000, 0x10101000, 0x00111000, 0x10111000, 
+         0x00101004, 0x10101004, 0x00111004, 0x10111004, 
+         0x20101000, 0x30101000, 0x20111000, 0x30111000, 
+         0x20101004, 0x30101004, 0x20111004, 0x30111004, 
+      },
+      {
+         /* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */
+         0x00000000, 0x08000000, 0x00000008, 0x08000008, 
+         0x00000400, 0x08000400, 0x00000408, 0x08000408, 
+         0x00020000, 0x08020000, 0x00020008, 0x08020008, 
+         0x00020400, 0x08020400, 0x00020408, 0x08020408, 
+         0x00000001, 0x08000001, 0x00000009, 0x08000009, 
+         0x00000401, 0x08000401, 0x00000409, 0x08000409, 
+         0x00020001, 0x08020001, 0x00020009, 0x08020009, 
+         0x00020401, 0x08020401, 0x00020409, 0x08020409, 
+         0x02000000, 0x0A000000, 0x02000008, 0x0A000008, 
+         0x02000400, 0x0A000400, 0x02000408, 0x0A000408, 
+         0x02020000, 0x0A020000, 0x02020008, 0x0A020008, 
+         0x02020400, 0x0A020400, 0x02020408, 0x0A020408, 
+         0x02000001, 0x0A000001, 0x02000009, 0x0A000009, 
+         0x02000401, 0x0A000401, 0x02000409, 0x0A000409, 
+         0x02020001, 0x0A020001, 0x02020009, 0x0A020009, 
+         0x02020401, 0x0A020401, 0x02020409, 0x0A020409, 
+      },
+      {
+         /* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */
+         0x00000000, 0x00000100, 0x00080000, 0x00080100, 
+         0x01000000, 0x01000100, 0x01080000, 0x01080100, 
+         0x00000010, 0x00000110, 0x00080010, 0x00080110, 
+         0x01000010, 0x01000110, 0x01080010, 0x01080110, 
+         0x00200000, 0x00200100, 0x00280000, 0x00280100, 
+         0x01200000, 0x01200100, 0x01280000, 0x01280100, 
+         0x00200010, 0x00200110, 0x00280010, 0x00280110, 
+         0x01200010, 0x01200110, 0x01280010, 0x01280110, 
+         0x00000200, 0x00000300, 0x00080200, 0x00080300, 
+         0x01000200, 0x01000300, 0x01080200, 0x01080300, 
+         0x00000210, 0x00000310, 0x00080210, 0x00080310, 
+         0x01000210, 0x01000310, 0x01080210, 0x01080310, 
+         0x00200200, 0x00200300, 0x00280200, 0x00280300, 
+         0x01200200, 0x01200300, 0x01280200, 0x01280300, 
+         0x00200210, 0x00200310, 0x00280210, 0x00280310, 
+         0x01200210, 0x01200310, 0x01280210, 0x01280310, 
+      },
+      {
+         /* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */
+         0x00000000, 0x04000000, 0x00040000, 0x04040000, 
+         0x00000002, 0x04000002, 0x00040002, 0x04040002, 
+         0x00002000, 0x04002000, 0x00042000, 0x04042000, 
+         0x00002002, 0x04002002, 0x00042002, 0x04042002, 
+         0x00000020, 0x04000020, 0x00040020, 0x04040020, 
+         0x00000022, 0x04000022, 0x00040022, 0x04040022, 
+         0x00002020, 0x04002020, 0x00042020, 0x04042020, 
+         0x00002022, 0x04002022, 0x00042022, 0x04042022, 
+         0x00000800, 0x04000800, 0x00040800, 0x04040800, 
+         0x00000802, 0x04000802, 0x00040802, 0x04040802, 
+         0x00002800, 0x04002800, 0x00042800, 0x04042800, 
+         0x00002802, 0x04002802, 0x00042802, 0x04042802, 
+         0x00000820, 0x04000820, 0x00040820, 0x04040820, 
+         0x00000822, 0x04000822, 0x00040822, 0x04040822, 
+         0x00002820, 0x04002820, 0x00042820, 0x04042820, 
+         0x00002822, 0x04002822, 0x00042822, 0x04042822, 
+      },
+   };
+
+   private static final int SPtrans[][] =
+   {
+      {
+         /* nibble 0 */
+         0x00820200, 0x00020000, 0x80800000, 0x80820200,
+         0x00800000, 0x80020200, 0x80020000, 0x80800000,
+         0x80020200, 0x00820200, 0x00820000, 0x80000200,
+         0x80800200, 0x00800000, 0x00000000, 0x80020000,
+         0x00020000, 0x80000000, 0x00800200, 0x00020200,
+         0x80820200, 0x00820000, 0x80000200, 0x00800200,
+         0x80000000, 0x00000200, 0x00020200, 0x80820000,
+         0x00000200, 0x80800200, 0x80820000, 0x00000000,
+         0x00000000, 0x80820200, 0x00800200, 0x80020000,
+         0x00820200, 0x00020000, 0x80000200, 0x00800200,
+         0x80820000, 0x00000200, 0x00020200, 0x80800000,
+         0x80020200, 0x80000000, 0x80800000, 0x00820000,
+         0x80820200, 0x00020200, 0x00820000, 0x80800200,
+         0x00800000, 0x80000200, 0x80020000, 0x00000000,
+         0x00020000, 0x00800000, 0x80800200, 0x00820200,
+         0x80000000, 0x80820000, 0x00000200, 0x80020200,
+      },
+      {
+         /* nibble 1 */
+         0x10042004, 0x00000000, 0x00042000, 0x10040000,
+         0x10000004, 0x00002004, 0x10002000, 0x00042000,
+         0x00002000, 0x10040004, 0x00000004, 0x10002000,
+         0x00040004, 0x10042000, 0x10040000, 0x00000004,
+         0x00040000, 0x10002004, 0x10040004, 0x00002000,
+         0x00042004, 0x10000000, 0x00000000, 0x00040004,
+         0x10002004, 0x00042004, 0x10042000, 0x10000004,
+         0x10000000, 0x00040000, 0x00002004, 0x10042004,
+         0x00040004, 0x10042000, 0x10002000, 0x00042004,
+         0x10042004, 0x00040004, 0x10000004, 0x00000000,
+         0x10000000, 0x00002004, 0x00040000, 0x10040004,
+         0x00002000, 0x10000000, 0x00042004, 0x10002004,
+         0x10042000, 0x00002000, 0x00000000, 0x10000004,
+         0x00000004, 0x10042004, 0x00042000, 0x10040000,
+         0x10040004, 0x00040000, 0x00002004, 0x10002000,
+         0x10002004, 0x00000004, 0x10040000, 0x00042000,
+      },
+      {
+         /* nibble 2 */
+         0x41000000, 0x01010040, 0x00000040, 0x41000040,
+         0x40010000, 0x01000000, 0x41000040, 0x00010040,
+         0x01000040, 0x00010000, 0x01010000, 0x40000000,
+         0x41010040, 0x40000040, 0x40000000, 0x41010000,
+         0x00000000, 0x40010000, 0x01010040, 0x00000040,
+         0x40000040, 0x41010040, 0x00010000, 0x41000000,
+         0x41010000, 0x01000040, 0x40010040, 0x01010000,
+         0x00010040, 0x00000000, 0x01000000, 0x40010040,
+         0x01010040, 0x00000040, 0x40000000, 0x00010000,
+         0x40000040, 0x40010000, 0x01010000, 0x41000040,
+         0x00000000, 0x01010040, 0x00010040, 0x41010000,
+         0x40010000, 0x01000000, 0x41010040, 0x40000000,
+         0x40010040, 0x41000000, 0x01000000, 0x41010040,
+         0x00010000, 0x01000040, 0x41000040, 0x00010040,
+         0x01000040, 0x00000000, 0x41010000, 0x40000040,
+         0x41000000, 0x40010040, 0x00000040, 0x01010000,
+      },
+      {
+         /* nibble 3 */
+         0x00100402, 0x04000400, 0x00000002, 0x04100402,
+         0x00000000, 0x04100000, 0x04000402, 0x00100002,
+         0x04100400, 0x04000002, 0x04000000, 0x00000402,
+         0x04000002, 0x00100402, 0x00100000, 0x04000000,
+         0x04100002, 0x00100400, 0x00000400, 0x00000002,
+         0x00100400, 0x04000402, 0x04100000, 0x00000400,
+         0x00000402, 0x00000000, 0x00100002, 0x04100400,
+         0x04000400, 0x04100002, 0x04100402, 0x00100000,
+         0x04100002, 0x00000402, 0x00100000, 0x04000002,
+         0x00100400, 0x04000400, 0x00000002, 0x04100000,
+         0x04000402, 0x00000000, 0x00000400, 0x00100002,
+         0x00000000, 0x04100002, 0x04100400, 0x00000400,
+         0x04000000, 0x04100402, 0x00100402, 0x00100000,
+         0x04100402, 0x00000002, 0x04000400, 0x00100402,
+         0x00100002, 0x00100400, 0x04100000, 0x04000402,
+         0x00000402, 0x04000000, 0x04000002, 0x04100400,
+      },
+      {
+         /* nibble 4 */
+         0x02000000, 0x00004000, 0x00000100, 0x02004108,
+         0x02004008, 0x02000100, 0x00004108, 0x02004000,
+         0x00004000, 0x00000008, 0x02000008, 0x00004100,
+         0x02000108, 0x02004008, 0x02004100, 0x00000000,
+         0x00004100, 0x02000000, 0x00004008, 0x00000108,
+         0x02000100, 0x00004108, 0x00000000, 0x02000008,
+         0x00000008, 0x02000108, 0x02004108, 0x00004008,
+         0x02004000, 0x00000100, 0x00000108, 0x02004100,
+         0x02004100, 0x02000108, 0x00004008, 0x02004000,
+         0x00004000, 0x00000008, 0x02000008, 0x02000100,
+         0x02000000, 0x00004100, 0x02004108, 0x00000000,
+         0x00004108, 0x02000000, 0x00000100, 0x00004008,
+         0x02000108, 0x00000100, 0x00000000, 0x02004108,
+         0x02004008, 0x02004100, 0x00000108, 0x00004000,
+         0x00004100, 0x02004008, 0x02000100, 0x00000108,
+         0x00000008, 0x00004108, 0x02004000, 0x02000008,
+      },
+      {
+         /* nibble 5 */
+         0x20000010, 0x00080010, 0x00000000, 0x20080800,
+         0x00080010, 0x00000800, 0x20000810, 0x00080000,
+         0x00000810, 0x20080810, 0x00080800, 0x20000000,
+         0x20000800, 0x20000010, 0x20080000, 0x00080810,
+         0x00080000, 0x20000810, 0x20080010, 0x00000000,
+         0x00000800, 0x00000010, 0x20080800, 0x20080010,
+         0x20080810, 0x20080000, 0x20000000, 0x00000810,
+         0x00000010, 0x00080800, 0x00080810, 0x20000800,
+         0x00000810, 0x20000000, 0x20000800, 0x00080810,
+         0x20080800, 0x00080010, 0x00000000, 0x20000800,
+         0x20000000, 0x00000800, 0x20080010, 0x00080000,
+         0x00080010, 0x20080810, 0x00080800, 0x00000010,
+         0x20080810, 0x00080800, 0x00080000, 0x20000810,
+         0x20000010, 0x20080000, 0x00080810, 0x00000000,
+         0x00000800, 0x20000010, 0x20000810, 0x20080800,
+         0x20080000, 0x00000810, 0x00000010, 0x20080010,
+      },
+      {
+         /* nibble 6 */
+         0x00001000, 0x00000080, 0x00400080, 0x00400001,
+         0x00401081, 0x00001001, 0x00001080, 0x00000000,
+         0x00400000, 0x00400081, 0x00000081, 0x00401000,
+         0x00000001, 0x00401080, 0x00401000, 0x00000081,
+         0x00400081, 0x00001000, 0x00001001, 0x00401081,
+         0x00000000, 0x00400080, 0x00400001, 0x00001080,
+         0x00401001, 0x00001081, 0x00401080, 0x00000001,
+         0x00001081, 0x00401001, 0x00000080, 0x00400000,
+         0x00001081, 0x00401000, 0x00401001, 0x00000081,
+         0x00001000, 0x00000080, 0x00400000, 0x00401001,
+         0x00400081, 0x00001081, 0x00001080, 0x00000000,
+         0x00000080, 0x00400001, 0x00000001, 0x00400080,
+         0x00000000, 0x00400081, 0x00400080, 0x00001080,
+         0x00000081, 0x00001000, 0x00401081, 0x00400000,
+         0x00401080, 0x00000001, 0x00001001, 0x00401081,
+         0x00400001, 0x00401080, 0x00401000, 0x00001001,
+      },
+      {
+         /* nibble 7 */
+         0x08200020, 0x08208000, 0x00008020, 0x00000000,
+         0x08008000, 0x00200020, 0x08200000, 0x08208020,
+         0x00000020, 0x08000000, 0x00208000, 0x00008020,
+         0x00208020, 0x08008020, 0x08000020, 0x08200000,
+         0x00008000, 0x00208020, 0x00200020, 0x08008000,
+         0x08208020, 0x08000020, 0x00000000, 0x00208000,
+         0x08000000, 0x00200000, 0x08008020, 0x08200020,
+         0x00200000, 0x00008000, 0x08208000, 0x00000020,
+         0x00200000, 0x00008000, 0x08000020, 0x08208020,
+         0x00008020, 0x08000000, 0x00000000, 0x00208000,
+         0x08200020, 0x08008020, 0x08008000, 0x00200020,
+         0x08208000, 0x00000020, 0x00200020, 0x08008000,
+         0x08208020, 0x00200000, 0x08200000, 0x08000020,
+         0x00208000, 0x00008020, 0x08008020, 0x08200000,
+         0x00000020, 0x08208000, 0x00208020, 0x00000000,
+         0x08000000, 0x08200020, 0x00008000, 0x00208020
+      }
+   };
+
+   private static final int cov_2char[] =
+   {
+      0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 
+      0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 
+      0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 
+      0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 
+      0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 
+      0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 
+      0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 
+      0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A
+   };
+
+   private static final int byteToUnsigned(byte b)
+   {
+      int value = (int)b;
+
+      return(value >= 0 ? value : value + 256);
+   }
+
+   private static int fourBytesToInt(byte b[], int offset)
+   {
+      int value;
+
+      value  =  byteToUnsigned(b[offset++]);
+      value |= (byteToUnsigned(b[offset++]) <<  8);
+      value |= (byteToUnsigned(b[offset++]) << 16);
+      value |= (byteToUnsigned(b[offset++]) << 24);
+
+      return(value);
+   }
+
+   private static final void intToFourBytes(int iValue, byte b[], int offset)
+   {
+      b[offset++] = (byte)((iValue)        & 0xff);
+      b[offset++] = (byte)((iValue >>> 8 ) & 0xff);
+      b[offset++] = (byte)((iValue >>> 16) & 0xff);
+      b[offset++] = (byte)((iValue >>> 24) & 0xff);
+   }
+
+   private static final void PERM_OP(int a, int b, int n, int m, int results[])
+   {
+      int t;
+
+      t = ((a >>> n) ^ b) & m;
+      a ^= t << n;
+      b ^= t;
+
+      results[0] = a;
+      results[1] = b;
+   }
+
+   private static final int HPERM_OP(int a, int n, int m)
+   {
+      int t;
+
+      t = ((a << (16 - n)) ^ a) & m;
+      a = a ^ t ^ (t >>> (16 - n));
+
+      return(a);
+   }
+
+   private static int [] des_set_key(byte key[])
+   {
+      int schedule[] = new int[ITERATIONS * 2];
+
+      int c = fourBytesToInt(key, 0);
+      int d = fourBytesToInt(key, 4);
+
+      int results[] = new int[2];
+
+      PERM_OP(d, c, 4, 0x0f0f0f0f, results);
+      d = results[0]; c = results[1];
+
+      c = HPERM_OP(c, -2, 0xcccc0000);
+      d = HPERM_OP(d, -2, 0xcccc0000);
+
+      PERM_OP(d, c, 1, 0x55555555, results);
+      d = results[0]; c = results[1];
+
+      PERM_OP(c, d, 8, 0x00ff00ff, results);
+      c = results[0]; d = results[1];
+
+      PERM_OP(d, c, 1, 0x55555555, results);
+      d = results[0]; c = results[1];
+
+      d = (((d & 0x000000ff) <<  16) |  (d & 0x0000ff00)     |
+           ((d & 0x00ff0000) >>> 16) | ((c & 0xf0000000) >>> 4));
+      c &= 0x0fffffff;
+
+      int s, t;
+      int j = 0;
+
+      for(int i = 0; i < ITERATIONS; i ++)
+      {
+         if(shifts2[i])
+         {
+            c = (c >>> 2) | (c << 26);
+            d = (d >>> 2) | (d << 26);
+         }
+         else
+         {
+            c = (c >>> 1) | (c << 27);
+            d = (d >>> 1) | (d << 27);
+         }
+
+         c &= 0x0fffffff;
+         d &= 0x0fffffff;
+
+         s = skb[0][ (c       ) & 0x3f                       ]|
+             skb[1][((c >>>  6) & 0x03) | ((c >>>  7) & 0x3c)]|
+             skb[2][((c >>> 13) & 0x0f) | ((c >>> 14) & 0x30)]|
+             skb[3][((c >>> 20) & 0x01) | ((c >>> 21) & 0x06) |
+                                          ((c >>> 22) & 0x38)];
+
+         t = skb[4][ (d     )  & 0x3f                       ]|
+             skb[5][((d >>> 7) & 0x03) | ((d >>>  8) & 0x3c)]|
+             skb[6][ (d >>>15) & 0x3f                       ]|
+             skb[7][((d >>>21) & 0x0f) | ((d >>> 22) & 0x30)];
+
+         schedule[j++] = ((t <<  16) | (s & 0x0000ffff)) & 0xffffffff;
+         s             = ((s >>> 16) | (t & 0xffff0000));
+
+         s             = (s << 4) | (s >>> 28);
+         schedule[j++] = s & 0xffffffff;
+      }
+      return(schedule);
+   }
+
+   private static final int D_ENCRYPT
+   (
+      int L, int R, int S, int E0, int E1, int s[]
+   )
+   {
+      int t, u, v;
+
+      v = R ^ (R >>> 16);
+      u = v & E0;
+      v = v & E1;
+      u = (u ^ (u << 16)) ^ R ^ s[S];
+      t = (v ^ (v << 16)) ^ R ^ s[S + 1];
+      t = (t >>> 4) | (t << 28);
+
+      L ^= SPtrans[1][(t       ) & 0x3f] |
+           SPtrans[3][(t >>>  8) & 0x3f] |
+           SPtrans[5][(t >>> 16) & 0x3f] |
+           SPtrans[7][(t >>> 24) & 0x3f] |
+           SPtrans[0][(u       ) & 0x3f] |
+           SPtrans[2][(u >>>  8) & 0x3f] |
+           SPtrans[4][(u >>> 16) & 0x3f] |
+           SPtrans[6][(u >>> 24) & 0x3f];
+
+      return(L);
+   }
+
+   private static final int [] body(int schedule[], int Eswap0, int Eswap1)
+   {
+      int left = 0;
+      int right = 0;
+      int t     = 0;
+
+      for(int j = 0; j < 25; j ++)
+      {
+         for(int i = 0; i < ITERATIONS * 2; i += 4)
+         {
+            left  = D_ENCRYPT(left,  right, i,     Eswap0, Eswap1, schedule);
+            right = D_ENCRYPT(right, left,  i + 2, Eswap0, Eswap1, schedule);
+         }
+         t     = left; 
+         left  = right; 
+         right = t;
+      }
+
+      t = right;
+
+      right = (left >>> 1) | (left << 31);
+      left  = (t    >>> 1) | (t    << 31);
+
+      left  &= 0xffffffff;
+      right &= 0xffffffff;
+
+      int results[] = new int[2];
+
+      PERM_OP(right, left, 1, 0x55555555, results); 
+      right = results[0]; left = results[1];
+
+      PERM_OP(left, right, 8, 0x00ff00ff, results); 
+      left = results[0]; right = results[1];
+
+      PERM_OP(right, left, 2, 0x33333333, results); 
+      right = results[0]; left = results[1];
+
+      PERM_OP(left, right, 16, 0x0000ffff, results);
+      left = results[0]; right = results[1];
+
+      PERM_OP(right, left, 4, 0x0f0f0f0f, results);
+      right = results[0]; left = results[1];
+
+      int out[] = new int[2];
+
+      out[0] = left; out[1] = right;
+
+      return(out);
+   }
+
+   public static final String crypt(String salt, String original)
+   {
+      while(salt.length() < 2)
+         salt += "A";
+
+      StringBuffer buffer = new StringBuffer("             ");
+
+      char charZero = salt.charAt(0);
+      char charOne  = salt.charAt(1);
+
+      buffer.setCharAt(0, charZero);
+      buffer.setCharAt(1, charOne);
+
+      int Eswap0 = con_salt[(int)charZero];
+      int Eswap1 = con_salt[(int)charOne] << 4;
+      byte key[] = new byte[8];
+
+      for(int i = 0; i < key.length; i ++)
+         key[i] = (byte)0;
+
+      for(int i = 0; i < key.length && i < original.length(); i ++)
+      {
+         int iChar = (int)original.charAt(i);
+
+         key[i] = (byte)(iChar << 1);
+      }
+
+      int schedule[] = des_set_key(key);
+      int out[]      = body(schedule, Eswap0, Eswap1);
+
+      byte b[] = new byte[9];
+
+      intToFourBytes(out[0], b, 0);
+      intToFourBytes(out[1], b, 4);
+      b[8] = 0;
+
+      for(int i = 2, y = 0, u = 0x80; i < 13; i ++)
+      {
+         for(int j = 0, c = 0; j < 6; j ++)
+         {
+            c <<= 1;
+
+            if(((int)b[y] & u) != 0)
+               c |= 1;
+
+            u >>>= 1;
+
+            if(u == 0)
+            {
+               y++;
+               u = 0x80;
+            }
+            buffer.setCharAt(i, (char)cov_2char[c]);
+         }
+      }
+      return(buffer.toString());
+   }
+}
+   
diff --git a/org.simantics.r.scl/src/org/rosuda/REngine/package-info.java b/org.simantics.r.scl/src/org/rosuda/REngine/package-info.java
new file mode 100644 (file)
index 0000000..66c145c
--- /dev/null
@@ -0,0 +1,5 @@
+/**
+ * High level java interface to R
+ */
+package org.rosuda.REngine ;
+
diff --git a/org.simantics.r.scl/src/org/simantics/r/scl/RSession.java b/org.simantics.r.scl/src/org/simantics/r/scl/RSession.java
new file mode 100644 (file)
index 0000000..b9fa5ee
--- /dev/null
@@ -0,0 +1,143 @@
+package org.simantics.r.scl;\r
+\r
+import java.util.concurrent.ExecutorService;\r
+import java.util.concurrent.Executors;\r
+import java.util.concurrent.Semaphore;\r
+import java.util.concurrent.ThreadFactory;\r
+import java.util.concurrent.TimeUnit;\r
+\r
+import org.rosuda.REngine.Rserve.RConnection;\r
+import org.rosuda.REngine.Rserve.RserveException;\r
+import org.simantics.r.scl.variable.RNodeManager;\r
+import org.simantics.r.scl.variable.RVariableNode;\r
+import org.simantics.scl.runtime.SCLContext;\r
+import org.simantics.scl.runtime.function.Function;\r
+import org.simantics.scl.runtime.tuple.Tuple0;\r
+import org.simantics.simulator.variable.NodeManager;\r
+import org.simantics.simulator.variable.Realm;\r
+\r
+public class RSession implements Realm {\r
+    public static final String R = "r";\r
+    \r
+    RConnection connection;\r
+    String id;\r
+    Thread executorThread;\r
+    ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() {\r
+        @Override\r
+        public Thread newThread(Runnable r) {\r
+            executorThread = new Thread(r);\r
+            return executorThread;\r
+        }\r
+    });\r
+    \r
+    Semaphore beginSyncExec = new Semaphore(0);\r
+    Semaphore endSyncExec = new Semaphore(0);\r
+    \r
+    RNodeManager nodeManager;\r
+    \r
+    Runnable scheduleSyncExec = new Runnable() {\r
+        @Override\r
+        public void run() {\r
+            beginSyncExec.release();\r
+            try {\r
+                endSyncExec.acquire();\r
+            } catch (InterruptedException e) {\r
+            }\r
+        }\r
+    };\r
+    \r
+    RSession(RConnection connection, String id) {\r
+        this.connection = connection;\r
+        this.id = id;\r
+        this.nodeManager = new RNodeManager(this);\r
+    }\r
+\r
+    public String getId() {\r
+        return id;\r
+    }\r
+    \r
+    public RConnection getConnection() {\r
+        return connection;\r
+    }\r
+    \r
+    public Thread getThread() {\r
+        return executorThread;\r
+    }\r
+    \r
+    @SuppressWarnings({ "rawtypes", "unchecked" })\r
+    public Object syncExec(Function fun) throws RserveException, InterruptedException {\r
+        executor.execute(scheduleSyncExec);\r
+        \r
+        SCLContext context = SCLContext.getCurrent();\r
+        RConnection oldConnection = (RConnection)context.put(R, connection);\r
+        \r
+        try {\r
+            beginSyncExec.acquire();\r
+            Thread oldThread = executorThread;\r
+            executorThread = Thread.currentThread();\r
+            try {\r
+                return fun.apply(Tuple0.INSTANCE);\r
+            } finally {\r
+                executorThread = oldThread;\r
+                endSyncExec.release();\r
+            }\r
+        } finally {\r
+            context.put(R, oldConnection);\r
+        }\r
+    }\r
+    \r
+    @SuppressWarnings("rawtypes")\r
+    public void asyncExec(final Function fun) {\r
+        executor.execute(new Runnable() {\r
+            @SuppressWarnings("unchecked")\r
+            @Override\r
+            public void run() {\r
+                SCLContext context = SCLContext.getCurrent();\r
+                context.put(R, connection);\r
+                fun.apply(Tuple0.INSTANCE);\r
+            }\r
+        });\r
+    }\r
+\r
+    @Override\r
+    public void syncExec(Runnable runnable) throws InterruptedException {\r
+        executor.execute(scheduleSyncExec);\r
+        \r
+        beginSyncExec.acquire();\r
+        Thread oldThread = executorThread;\r
+        executorThread = Thread.currentThread();\r
+        try {\r
+            runnable.run();\r
+        } finally {\r
+            executorThread = oldThread;\r
+            endSyncExec.release();\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public void asyncExec(Runnable runnable) {\r
+        executor.execute(runnable);\r
+    }\r
+    \r
+    public void refreshVariables() {\r
+        nodeManager.refreshVariables();\r
+    }\r
+    \r
+    public void refreshVariablesSync() {\r
+        nodeManager.refreshVariablesSync();\r
+    }\r
+\r
+    public void close() {\r
+        RSessionManager.CONNECTIONS.remove(id);\r
+        executor.shutdown();\r
+        try {\r
+            executor.awaitTermination(500L, TimeUnit.MILLISECONDS);\r
+        } catch (InterruptedException e) {\r
+        }\r
+        connection.close();\r
+    }\r
+\r
+    public NodeManager<RVariableNode> getNodeManager() {\r
+        return nodeManager;\r
+    }\r
+}\r
diff --git a/org.simantics.r.scl/src/org/simantics/r/scl/RSessionConfiguration.java b/org.simantics.r.scl/src/org/simantics/r/scl/RSessionConfiguration.java
new file mode 100644 (file)
index 0000000..ae46330
--- /dev/null
@@ -0,0 +1,21 @@
+package org.simantics.r.scl;\r
+\r
+public class RSessionConfiguration {\r
+    public final String host;\r
+    public final int port;\r
+    public final String username;\r
+    public final String password;\r
+    \r
+    public RSessionConfiguration(\r
+            String host, int port,\r
+            String username, String password) {\r
+        this.host = host;\r
+        this.port = port;\r
+        this.username = username;\r
+        this.password = password;\r
+    }\r
+    \r
+    public RSessionConfiguration(String host, int port) {\r
+        this(host, port, null, null);\r
+    }\r
+}\r
diff --git a/org.simantics.r.scl/src/org/simantics/r/scl/RSessionManager.java b/org.simantics.r.scl/src/org/simantics/r/scl/RSessionManager.java
new file mode 100644 (file)
index 0000000..f75df4c
--- /dev/null
@@ -0,0 +1,57 @@
+package org.simantics.r.scl;\r
+\r
+import java.util.UUID;\r
+import java.util.concurrent.ConcurrentHashMap;\r
+\r
+import org.rosuda.REngine.Rserve.RConnection;\r
+import org.rosuda.REngine.Rserve.RserveException;\r
+import org.simantics.scl.runtime.function.Function;\r
+\r
+public class RSessionManager {\r
+    static ConcurrentHashMap<String, RSession> CONNECTIONS =\r
+            new ConcurrentHashMap<String, RSession>(); \r
+    \r
+    public static RSession getSession(String id) {\r
+        // CONNECTIONS is ConcurrentHashMap so no synchronization is needed here\r
+        return CONNECTIONS.get(id);\r
+    }\r
+    \r
+    public static RSession createSession(RSessionConfiguration configuration) throws RserveException {\r
+        synchronized(CONNECTIONS) {\r
+            String id = UUID.randomUUID().toString();\r
+            return createSession(configuration, id);\r
+        }\r
+    }\r
+    \r
+    public static Object withConfiguration(RSessionConfiguration configuration, @SuppressWarnings("rawtypes") Function f) throws RserveException {\r
+        RSession session = createSession(configuration);\r
+        try {\r
+            return session.syncExec(f);\r
+        } catch (InterruptedException e) {\r
+            throw new RuntimeException(e);\r
+        } finally {\r
+            session.close();\r
+        }\r
+    }\r
+    \r
+    public static RSession getOrCreateSession(RSessionConfiguration configuration, String id) throws RserveException {\r
+        synchronized(CONNECTIONS) {\r
+            RSession session = getSession(id);\r
+            if(session == null)\r
+                return createSession(configuration, id);\r
+            else\r
+                return session;\r
+        }\r
+    }\r
+    \r
+    private static RSession createSession(RSessionConfiguration configuration, String id) throws RserveException {\r
+        synchronized(CONNECTIONS) {\r
+            RConnection connection = new RConnection(configuration.host, configuration.port);\r
+            if(configuration.username != null && !configuration.username.isEmpty())\r
+                connection.login(configuration.username, configuration.password);\r
+            RSession managedConnection = new RSession(connection, id);\r
+            CONNECTIONS.put(id, managedConnection);\r
+            return managedConnection;\r
+        }\r
+    }\r
+}\r
diff --git a/org.simantics.r.scl/src/org/simantics/r/scl/TestRServe.java b/org.simantics.r.scl/src/org/simantics/r/scl/TestRServe.java
new file mode 100644 (file)
index 0000000..e3ac2fe
--- /dev/null
@@ -0,0 +1,36 @@
+package org.simantics.r.scl;\r
+\r
+import java.util.Arrays;\r
+\r
+import org.rosuda.REngine.REXP;\r
+import org.rosuda.REngine.REXPMismatchException;\r
+import org.rosuda.REngine.REngineException;\r
+import org.rosuda.REngine.Rserve.RConnection;\r
+import org.rosuda.REngine.Rserve.RSession;\r
+\r
+public class TestRServe {\r
+\r
+    public static void main(String[] args) throws REXPMismatchException, REngineException {\r
+        RSession session;\r
+        {\r
+            RConnection c = new RConnection("130.188.198.138");\r
+            c.login("simupedia", "simupedia");\r
+            c.assign("a", new double[] {13});\r
+            c.assign("b", new double[] {12});\r
+            REXP x =  c.eval("ls()");\r
+            System.out.println(Arrays.toString(x.asStrings()));\r
+            session = c.detach();\r
+        }\r
+        \r
+        {\r
+            RConnection c = session.attach();\r
+            c.login("simupedia", "simupedia");\r
+            c.assign("c", new double[] {13});\r
+            c.assign("d", new double[] {12});\r
+            REXP x =  c.eval("ls()");\r
+            System.out.println(Arrays.toString(x.asStrings()));\r
+            c.close();\r
+        }\r
+    }\r
+    \r
+}\r
diff --git a/org.simantics.r.scl/src/org/simantics/r/scl/variable/RAttributeNode.java b/org.simantics.r.scl/src/org/simantics/r/scl/variable/RAttributeNode.java
new file mode 100644 (file)
index 0000000..57773c9
--- /dev/null
@@ -0,0 +1,32 @@
+package org.simantics.r.scl.variable;\r
+\r
+import org.rosuda.REngine.REXP;\r
+\r
+public class RAttributeNode implements RVariableNode {\r
+       private String name;\r
+       private RVariableNode parent;\r
+\r
+       public RAttributeNode(RVariableNode parent, String name) {\r
+               this.name = name;\r
+               this.parent = parent;\r
+       }\r
+\r
+       @Override\r
+       public REXP getValue() {\r
+               REXP parentValue = parent.getValue();\r
+               if (parentValue == null)\r
+                       return null;\r
+               \r
+               return parentValue.getAttribute(name);\r
+       }\r
+\r
+       @Override\r
+       public String getName() {\r
+               return RNodeManager.ATTRIBUTE_NAME_PREFIX + name;\r
+       }\r
+\r
+       @Override\r
+       public RVariableNode getParent() {\r
+               return parent;\r
+       }\r
+}\r
diff --git a/org.simantics.r.scl/src/org/simantics/r/scl/variable/RDataboardConversion.java b/org.simantics.r.scl/src/org/simantics/r/scl/variable/RDataboardConversion.java
new file mode 100644 (file)
index 0000000..6e81d2c
--- /dev/null
@@ -0,0 +1,231 @@
+package org.simantics.r.scl.variable;\r
+\r
+import org.rosuda.REngine.REXP;\r
+import org.rosuda.REngine.REXPDouble;\r
+import org.rosuda.REngine.REXPFactor;\r
+import org.rosuda.REngine.REXPGenericVector;\r
+import org.rosuda.REngine.REXPInteger;\r
+import org.rosuda.REngine.REXPList;\r
+import org.rosuda.REngine.REXPLogical;\r
+import org.rosuda.REngine.REXPMismatchException;\r
+import org.rosuda.REngine.REXPNull;\r
+import org.rosuda.REngine.REXPRaw;\r
+import org.rosuda.REngine.REXPReference;\r
+import org.rosuda.REngine.REXPS4;\r
+import org.rosuda.REngine.REXPString;\r
+import org.rosuda.REngine.REXPSymbol;\r
+import org.rosuda.REngine.RList;\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.Datatypes;\r
+import org.simantics.databoard.binding.ArrayBinding;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.BooleanBinding;\r
+import org.simantics.databoard.binding.ByteBinding;\r
+import org.simantics.databoard.binding.DoubleBinding;\r
+import org.simantics.databoard.binding.IntegerBinding;\r
+import org.simantics.databoard.binding.NumberBinding;\r
+import org.simantics.databoard.binding.StringBinding;\r
+import org.simantics.databoard.binding.VariantBinding;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.impl.BooleanArrayBinding;\r
+import org.simantics.databoard.binding.impl.ByteArrayBinding;\r
+import org.simantics.databoard.binding.impl.DoubleArrayBinding;\r
+import org.simantics.databoard.binding.impl.IntArrayBinding;\r
+import org.simantics.databoard.binding.impl.StringArrayBinding;\r
+import org.simantics.databoard.type.Datatype;\r
+\r
+public class RDataboardConversion {\r
+    public static Datatype getDatatype(REXP rexp) throws BindingException {\r
+       if (rexp == null) return Datatypes.VOID;\r
+       \r
+        if(rexp instanceof REXPDouble)\r
+            return Datatypes.DOUBLE_ARRAY;\r
+        else if(rexp instanceof REXPInteger)\r
+            return Datatypes.INTEGER_ARRAY;\r
+        else if(rexp instanceof REXPString)\r
+            return Datatypes.STRING_ARRAY;\r
+        else if(rexp instanceof REXPLogical)\r
+               return Datatypes.BOOLEAN_ARRAY;\r
+        else if(rexp instanceof REXPSymbol)\r
+               return Datatypes.STRING;\r
+        else if(rexp instanceof REXPFactor)\r
+               return Datatypes.STRING_ARRAY;\r
+        else if(rexp instanceof REXPRaw)\r
+               return Datatypes.BYTE_ARRAY;\r
+        else if(rexp instanceof REXPReference)\r
+               return getDatatype(((REXPReference)rexp).resolve());\r
+        else if(rexp instanceof REXPList || rexp instanceof REXPNull || rexp instanceof REXPGenericVector)\r
+               return Datatypes.VARIANT_ARRAY;\r
+        else if(rexp instanceof REXPS4)\r
+               return Datatypes.VARIANT;\r
+               else\r
+            throw new BindingException("Cannot handle REXP of type " + rexp != null ? (rexp.getClass().getSimpleName() + ".") : "null");\r
+    }\r
+\r
+    public static Object fromREXP(REXP value, Binding binding) throws BindingException {\r
+       if (value == null)\r
+               return binding.createDefault();\r
+       \r
+        try {\r
+            if(binding instanceof DoubleBinding)\r
+                return ((DoubleBinding)binding).create(value.asDouble());\r
+            else if(binding instanceof IntegerBinding)\r
+                return ((IntegerBinding)binding).create(value.asInteger());\r
+            else if(binding instanceof ByteBinding)\r
+                return ((ByteBinding)binding).create(value.asInteger());\r
+            else if(binding instanceof StringBinding)\r
+                return ((StringBinding)binding).create(value.asString());\r
+            else if(binding instanceof NumberBinding)\r
+                return ((NumberBinding)binding).create(value.asDouble());\r
+            else if(binding instanceof BooleanBinding)\r
+               return ((BooleanBinding)binding).create(value.asInteger() == 1);\r
+            else if(binding instanceof VariantBinding) {\r
+               Datatype dt = getDatatype(value);\r
+               if (dt != null) {\r
+                       if (dt == Datatypes.VARIANT)\r
+                               return ((VariantBinding) binding).create(Bindings.OBJECT, value);\r
+                       else {\r
+                               Binding bd = Bindings.getBinding(dt);\r
+                               if (bd != null) {\r
+                                       return ((VariantBinding)binding).create(bd, fromREXP(value, bd));\r
+                               }\r
+                       }\r
+               }\r
+            }\r
+            else if(binding instanceof ArrayBinding) {\r
+                if(binding instanceof DoubleArrayBinding)\r
+                    return value.asDoubles();\r
+                else if(binding instanceof ByteArrayBinding)\r
+                    return value.asBytes();\r
+                else if(binding instanceof IntArrayBinding)\r
+                    return value.asIntegers();\r
+                else if(binding instanceof StringArrayBinding)\r
+                    return value.asStrings();\r
+                else if(binding instanceof BooleanArrayBinding) {\r
+                       int[] values = value.asIntegers();\r
+                       boolean[] result = new boolean[values.length];\r
+                       for (int i = 0; i < values.length; i++)\r
+                               result[i] = values[i] == 1;\r
+                       return result;\r
+                }\r
+                \r
+                ArrayBinding arrayBinding = (ArrayBinding)binding;\r
+                if(arrayBinding.componentBinding instanceof DoubleBinding) {\r
+                    DoubleBinding componentBinding = (DoubleBinding)arrayBinding.componentBinding;\r
+                    double[] vs = value.asDoubles();\r
+                    Object[] components = new Object[vs.length];\r
+                    for(int i=0;i<vs.length;++i)\r
+                        components[i] = componentBinding.create(vs[i]);\r
+                    return arrayBinding.create(components);\r
+                }\r
+                else if(arrayBinding.componentBinding instanceof IntegerBinding) {\r
+                    IntegerBinding componentBinding = (IntegerBinding)arrayBinding.componentBinding;\r
+                    int[] vs = value.asIntegers();\r
+                    Object[] components = new Object[vs.length];\r
+                    for(int i=0;i<vs.length;++i)\r
+                        components[i] = componentBinding.create(vs[i]);\r
+                    return arrayBinding.create(components);\r
+                }\r
+                else if(arrayBinding.componentBinding instanceof StringBinding) {\r
+                    StringBinding componentBinding = (StringBinding)arrayBinding.componentBinding;\r
+                    String[] vs = value.asStrings();\r
+                    Object[] components = new Object[vs.length];\r
+                    for(int i=0;i<vs.length;++i)\r
+                        components[i] = componentBinding.create(vs[i]);\r
+                    return arrayBinding.create(components);\r
+                }\r
+                else if(arrayBinding.componentBinding instanceof NumberBinding) {\r
+                    NumberBinding componentBinding = (NumberBinding)arrayBinding.componentBinding;\r
+                    double[] vs = value.asDoubles();\r
+                    Object[] components = new Object[vs.length];\r
+                    for(int i=0;i<vs.length;++i)\r
+                        components[i] = componentBinding.create(vs[i]);\r
+                    return arrayBinding.create(components);\r
+                }\r
+                else if(arrayBinding.componentBinding instanceof VariantBinding) {\r
+                       if (value instanceof REXPNull || value.length() == 0)\r
+                               return arrayBinding.create();\r
+                       \r
+                       VariantBinding componentBinding = (VariantBinding)arrayBinding.componentBinding;\r
+                       Object[] components = new Object[value.length()];\r
+                       if (value.isInteger()) {\r
+                               int[] vs = value.asIntegers();\r
+                               for (int i=0; i<vs.length; i++)\r
+                                       components[i] = componentBinding.create(Bindings.INTEGER, vs[i]);\r
+                       }\r
+                       else if (value.isNumeric()) {\r
+                               double[] vs = value.asDoubles();\r
+                               for (int i=0; i<vs.length; i++)\r
+                                       components[i] = componentBinding.create(Bindings.DOUBLE, vs[i]);\r
+                       }\r
+                       else if (value.isString()) {\r
+                               String[] vs = value.asStrings();\r
+                               for (int i=0; i<vs.length; i++)\r
+                                       components[i] = componentBinding.create(Bindings.STRING, vs[i]);\r
+                       }\r
+                       else if (value.isList()) {\r
+                               RList vs = value.asList();\r
+                               for (int i=0; i<vs.size(); i++)\r
+                                       components[i] = fromREXP(vs.at(i), componentBinding);\r
+                       }\r
+                       return arrayBinding.create(components);\r
+                }\r
+                //TODO: Other bindings\r
+            }\r
+            \r
+            throw new BindingException("Couldn't handle binding " + binding + ".");\r
+        } catch(REXPMismatchException e) {\r
+            throw new BindingException(e);\r
+        }\r
+    }\r
+\r
+    public static REXP toREXP(Object value, Binding binding) throws BindingException {\r
+        if(binding instanceof DoubleBinding)\r
+            return new REXPDouble(((DoubleBinding)binding).getValue_(value));\r
+        else if(binding instanceof IntegerBinding)\r
+            return new REXPInteger(((IntegerBinding)binding).getValue_(value));\r
+        else if(binding instanceof StringBinding)\r
+            return new REXPString(((StringBinding)binding).getValue(value));\r
+        else if(binding instanceof NumberBinding)\r
+            return new REXPDouble(((NumberBinding)binding).getValue(value).doubleValue());\r
+        else if(binding instanceof ArrayBinding) {\r
+            if(binding instanceof DoubleArrayBinding)\r
+                return new REXPDouble((double[])value);\r
+            else if(binding instanceof IntArrayBinding)\r
+                return new REXPInteger((int[])value);\r
+            else if(binding instanceof StringArrayBinding)\r
+                return new REXPString((String[])value);\r
+            ArrayBinding arrayBinding = (ArrayBinding)binding;\r
+            if(arrayBinding.componentBinding instanceof DoubleBinding) {\r
+                DoubleBinding componentBinding = (DoubleBinding)arrayBinding.componentBinding;\r
+                double[] vs = new double[arrayBinding.size(value)];\r
+                for(int i=0;i<vs.length;++i)\r
+                    vs[i] = componentBinding.getValue_(arrayBinding.get(value, i));\r
+                return new REXPDouble(vs);\r
+            }\r
+            if(arrayBinding.componentBinding instanceof IntegerBinding) {\r
+                IntegerBinding componentBinding = (IntegerBinding)arrayBinding.componentBinding;\r
+                int[] vs = new int[arrayBinding.size(value)];\r
+                for(int i=0;i<vs.length;++i)\r
+                    vs[i] = componentBinding.getValue_(arrayBinding.get(value, i));\r
+                return new REXPInteger(vs);\r
+            }\r
+            if(arrayBinding.componentBinding instanceof StringBinding) {\r
+                StringBinding componentBinding = (StringBinding)arrayBinding.componentBinding;\r
+                String[] vs = new String[arrayBinding.size(value)];\r
+                for(int i=0;i<vs.length;++i)\r
+                    vs[i] = componentBinding.getValue(arrayBinding.get(value, i));\r
+                return new REXPString(vs);\r
+            }\r
+            if(arrayBinding.componentBinding instanceof NumberBinding) {\r
+                NumberBinding componentBinding = (NumberBinding)arrayBinding.componentBinding;\r
+                double[] vs = new double[arrayBinding.size(value)];\r
+                for(int i=0;i<vs.length;++i)\r
+                    vs[i] = componentBinding.getValue(arrayBinding.get(value, i)).doubleValue();\r
+                return new REXPDouble(vs);\r
+            }\r
+        }\r
+        \r
+        throw new BindingException("Couldn't handle binding " + binding + ".");\r
+    }\r
+}\r
diff --git a/org.simantics.r.scl/src/org/simantics/r/scl/variable/RGlobalVariableNode.java b/org.simantics.r.scl/src/org/simantics/r/scl/variable/RGlobalVariableNode.java
new file mode 100644 (file)
index 0000000..d2239cf
--- /dev/null
@@ -0,0 +1,42 @@
+package org.simantics.r.scl.variable;\r
+\r
+import org.rosuda.REngine.REXP;\r
+\r
+public class RGlobalVariableNode implements RVariableNode {\r
+       private RNodeManager manager;\r
+       private REXP expression;\r
+       private String name;\r
+       \r
+       public RGlobalVariableNode(RNodeManager manager, String name) {\r
+               super();\r
+               \r
+               this.manager = manager;\r
+               this.name = name;\r
+               this.expression = null;\r
+       }\r
+\r
+       public RGlobalVariableNode(RNodeManager manager, String name, REXP expression) {\r
+               super();\r
+               \r
+               this.manager = manager;\r
+               this.name = name;\r
+               this.expression = expression;\r
+       }\r
+\r
+       @Override\r
+       public REXP getValue() {\r
+               if (expression == null)\r
+                       expression = manager.getGlobalValue(name); \r
+               return expression;\r
+       }\r
+\r
+       @Override\r
+       public String getName() {\r
+               return name;\r
+       }\r
+\r
+       @Override\r
+       public RVariableNode getParent() {\r
+               return manager;\r
+       }\r
+}\r
diff --git a/org.simantics.r.scl/src/org/simantics/r/scl/variable/RListItemNode.java b/org.simantics.r.scl/src/org/simantics/r/scl/variable/RListItemNode.java
new file mode 100644 (file)
index 0000000..68405ad
--- /dev/null
@@ -0,0 +1,43 @@
+package org.simantics.r.scl.variable;\r
+\r
+import org.rosuda.REngine.REXP;\r
+import org.rosuda.REngine.REXPMismatchException;\r
+import org.rosuda.REngine.RList;\r
+\r
+public class RListItemNode implements RVariableNode {\r
+\r
+       private RVariableNode parent;\r
+       private int index;\r
+       \r
+       public RListItemNode(RVariableNode parent, int index) {\r
+               this.index = index;\r
+               this.parent = parent;\r
+       }\r
+\r
+       @Override\r
+       public REXP getValue() {\r
+               REXP parentValue = parent.getValue();\r
+               if (parentValue == null || ! parentValue.isList())\r
+                       return null;\r
+               \r
+               RList list;\r
+               try {\r
+                       list = parentValue.asList();\r
+               } catch (REXPMismatchException e) {\r
+                       // Should never happen\r
+                       return null;\r
+               }\r
+               \r
+               return list.at(index);\r
+       }\r
+\r
+       @Override\r
+       public String getName() {\r
+               return RNodeManager.INDEXED_ITEM_NAME_PREFIX + Integer.toString(index);\r
+       }\r
+\r
+       @Override\r
+       public RVariableNode getParent() {\r
+               return parent;\r
+       }\r
+}\r
diff --git a/org.simantics.r.scl/src/org/simantics/r/scl/variable/RListLengthNode.java b/org.simantics.r.scl/src/org/simantics/r/scl/variable/RListLengthNode.java
new file mode 100644 (file)
index 0000000..cd6438c
--- /dev/null
@@ -0,0 +1,37 @@
+package org.simantics.r.scl.variable;\r
+\r
+import org.rosuda.REngine.REXP;\r
+import org.rosuda.REngine.REXPInteger;\r
+import org.rosuda.REngine.REXPMismatchException;\r
+\r
+public class RListLengthNode implements RVariableNode {\r
+\r
+       RVariableNode parent;\r
+       \r
+       public RListLengthNode(RVariableNode parent) {\r
+               this.parent = parent;\r
+       }\r
+\r
+       @Override\r
+       public REXP getValue() {\r
+               REXP parentValue = parent.getValue();\r
+               if (parentValue == null)\r
+                       return null;\r
+               \r
+               try {\r
+                       return new REXPInteger(parentValue.length());\r
+               } catch (REXPMismatchException e) {\r
+                       return null;\r
+               }\r
+       }\r
+\r
+       @Override\r
+       public String getName() {\r
+               return "length";\r
+       }\r
+\r
+       @Override\r
+       public RVariableNode getParent() {\r
+               return parent;\r
+       }\r
+}\r
diff --git a/org.simantics.r.scl/src/org/simantics/r/scl/variable/RNamedItemNode.java b/org.simantics.r.scl/src/org/simantics/r/scl/variable/RNamedItemNode.java
new file mode 100644 (file)
index 0000000..dbf2397
--- /dev/null
@@ -0,0 +1,43 @@
+package org.simantics.r.scl.variable;\r
+\r
+import org.rosuda.REngine.REXP;\r
+import org.rosuda.REngine.REXPMismatchException;\r
+import org.rosuda.REngine.RList;\r
+\r
+public class RNamedItemNode implements RVariableNode {\r
+\r
+       public RNamedItemNode(RVariableNode parent, String name) {\r
+               this.parent = parent;\r
+               this.name = name;\r
+       }\r
+\r
+       private RVariableNode parent;\r
+       private String name;\r
+       \r
+       @Override\r
+       public REXP getValue() {\r
+               REXP parentValue = parent.getValue();\r
+               if (parentValue == null || ! parentValue.isList())\r
+                       return null;\r
+               \r
+               RList list;\r
+               try {\r
+                       list = parentValue.asList();\r
+               } catch (REXPMismatchException e) {\r
+                       // Should never happen\r
+                       return null;\r
+               }\r
+               \r
+               return list.at(name);\r
+       }\r
+\r
+       @Override\r
+       public String getName() {\r
+               return RNodeManager.NAMED_ITEM_NAME_PREFIX + name;\r
+       }\r
+\r
+       @Override\r
+       public RVariableNode getParent() {\r
+               return parent;\r
+       }\r
+}\r
diff --git a/org.simantics.r.scl/src/org/simantics/r/scl/variable/RNodeManager.java b/org.simantics.r.scl/src/org/simantics/r/scl/variable/RNodeManager.java
new file mode 100644 (file)
index 0000000..8c2d85a
--- /dev/null
@@ -0,0 +1,389 @@
+package org.simantics.r.scl.variable;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+import gnu.trove.procedure.TObjectProcedure;\r
+import gnu.trove.set.hash.THashSet;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.List;\r
+import java.util.Set;\r
+import java.util.concurrent.atomic.AtomicBoolean;\r
+\r
+import org.rosuda.REngine.REXP;\r
+import org.rosuda.REngine.REXPList;\r
+import org.rosuda.REngine.REXPMismatchException;\r
+import org.rosuda.REngine.REngineException;\r
+import org.rosuda.REngine.RList;\r
+import org.rosuda.REngine.Rserve.RserveException;\r
+import org.simantics.databoard.Datatypes;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.r.scl.RSession;\r
+import org.simantics.simulator.variable.Realm;\r
+import org.simantics.simulator.variable.exceptions.NodeManagerException;\r
+import org.simantics.simulator.variable.exceptions.NotInRealmException;\r
+import org.simantics.simulator.variable.impl.AbstractNodeManager;\r
+import org.simantics.structural.stubs.StructuralResource2;\r
+import org.simantics.utils.datastructures.Pair;\r
+\r
+public class RNodeManager extends AbstractNodeManager<RVariableNode> implements RVariableNode {\r
+\r
+       public final static String LENGTH_PROPERTY_NAME = "length";\r
+       public final static String ATTRIBUTE_NAME_PREFIX = "a-";\r
+       public final static String INDEXED_ITEM_NAME_PREFIX = "i-";\r
+       public final static String NAMED_ITEM_NAME_PREFIX = "n-";       \r
+       \r
+    RSession realm;\r
+    THashMap<RVariableNode, List<RVariableNode>> propertyCache = new THashMap<RVariableNode, List<RVariableNode>>(); \r
+    THashMap<Pair<RVariableNode, String>, RVariableNode> nodeCache = new THashMap<Pair<RVariableNode, String>, RVariableNode>(); \r
+    THashMap<RVariableNode, THashSet<Runnable>> listeners = new THashMap<RVariableNode, THashSet<Runnable>>();\r
+    List<RVariableNode> globals;\r
+    \r
+    AtomicBoolean fireNodeListenersScheduled = new AtomicBoolean(false);\r
+    Runnable fireNodeListeners = new Runnable() {\r
+        @Override\r
+        public void run() {\r
+            fireNodeListenersScheduled.set(false);\r
+            final TObjectProcedure<Runnable> procedure = new TObjectProcedure<Runnable>() {\r
+                @Override\r
+                public boolean execute(Runnable object) {\r
+                    object.run();\r
+                    return true;\r
+                }\r
+            };\r
+            synchronized(listeners) {\r
+                listeners.forEachValue(new TObjectProcedure<THashSet<Runnable>>() {\r
+                    @Override\r
+                    public boolean execute(THashSet<Runnable> object) {\r
+                        object.forEach(procedure);\r
+                        return true;\r
+                    }\r
+                });\r
+            }\r
+        }\r
+    };\r
+    \r
+    Runnable clearValueCache = new Runnable() {\r
+        @Override\r
+        public void run() {\r
+            nodeCache.clear();\r
+            propertyCache.clear();\r
+            globals = null;\r
+        }\r
+    };\r
+    \r
+    public RNodeManager(RSession realm) {\r
+        super();\r
+        this.realm = realm;\r
+    }\r
+\r
+    @Override\r
+    public Realm getRealm() {\r
+        return realm;\r
+    }\r
+\r
+    @Override\r
+    public String getName(RVariableNode node) {\r
+       return node.getName();\r
+    }\r
+\r
+    @Override\r
+    public void addNodeListener(RVariableNode node, Runnable listener) {\r
+        synchronized(listeners) {\r
+            THashSet<Runnable> l = listeners.get(node);\r
+            if(l == null) {\r
+                l = new THashSet<Runnable>();\r
+                listeners.put(node, l);\r
+            }\r
+            l.add(listener);\r
+        }\r
+        \r
+        if (realm.getThread() == Thread.currentThread())\r
+               listener.run();\r
+        else\r
+               realm.asyncExec(listener);\r
+    }\r
+\r
+    @Override\r
+    public void removeNodeListener(RVariableNode node, Runnable listener) {\r
+        synchronized(listeners) {\r
+            THashSet<Runnable> l = listeners.get(node);\r
+            if(l != null) {\r
+                l.remove(listener);\r
+                if(l.isEmpty())\r
+                    listeners.remove(node);\r
+            }\r
+        }\r
+    }\r
+    \r
+    public void fireNodeListeners() {\r
+        if(!fireNodeListenersScheduled.getAndSet(true))\r
+            realm.asyncExec(fireNodeListeners);\r
+    }\r
+    \r
+    public void fireNodeListenersSync() {\r
+       try {\r
+                       realm.syncExec(fireNodeListeners);\r
+               } catch (InterruptedException e) {\r
+                       e.printStackTrace();\r
+               }\r
+    }\r
+\r
+    public void refreshVariables() {\r
+        realm.asyncExec(clearValueCache);\r
+        fireNodeListeners();\r
+    }\r
+\r
+    public void refreshVariablesSync() {\r
+        try {\r
+                       realm.syncExec(clearValueCache);\r
+               } catch (InterruptedException e) {\r
+                       e.printStackTrace();\r
+               }\r
+        fireNodeListenersSync();\r
+    }\r
+\r
+    @Override\r
+    public RVariableNode getNode(String path) throws NodeManagerException {\r
+        checkThreadAccess();\r
+        throw new UnsupportedOperationException();\r
+    }\r
+\r
+    @Override\r
+    public RVariableNode getChild(RVariableNode node, String name)\r
+            throws NodeManagerException {\r
+        checkThreadAccess();\r
+        return null;\r
+    }\r
+\r
+    @Override\r
+    public RVariableNode getProperty(RVariableNode node, String name)\r
+            throws NodeManagerException {\r
+        checkThreadAccess();\r
+        RVariableNode prop = nodeCache.get(new Pair<RVariableNode, String>(node, name));\r
+        if (prop != null)\r
+               return prop;\r
+\r
+        if (node == this) {\r
+               prop = new RGlobalVariableNode(this, name);\r
+        }\r
+        else if (node instanceof RListLengthNode) {\r
+               return null;\r
+        }\r
+        else {\r
+               if (name.equals(LENGTH_PROPERTY_NAME)) {\r
+                       prop = new RListLengthNode(node);\r
+               }\r
+               else if (name.startsWith(NAMED_ITEM_NAME_PREFIX)) {\r
+                       prop = new RNamedItemNode(node, name.substring(NAMED_ITEM_NAME_PREFIX.length()));\r
+               }\r
+               else if (name.startsWith(ATTRIBUTE_NAME_PREFIX)) {\r
+                       prop = new RAttributeNode(node, name.substring(ATTRIBUTE_NAME_PREFIX.length()));\r
+               }\r
+               else if (name.startsWith(INDEXED_ITEM_NAME_PREFIX)) {\r
+                       try {\r
+                               int index = Integer.parseInt(name.substring(INDEXED_ITEM_NAME_PREFIX.length()));\r
+                               if (index >= 0) {\r
+                                       prop = new RListItemNode(node, index);\r
+                               }\r
+                       }\r
+                       catch (NumberFormatException e) {\r
+                               return null;\r
+                       }\r
+               }\r
+               else {\r
+                       return null;\r
+               }\r
+        }\r
+        \r
+        nodeCache.put(new Pair<RVariableNode, String>(node, name), prop);\r
+        \r
+        return prop;\r
+    }\r
+\r
+    @Override\r
+    public List<RVariableNode> getChildren(RVariableNode node) throws NodeManagerException {\r
+        checkThreadAccess();\r
+        return Collections.emptyList();\r
+    }\r
+\r
+    @Override\r
+    public List<RVariableNode> getProperties(RVariableNode node) throws NodeManagerException {\r
+        checkThreadAccess();\r
+        if (node == this) {\r
+               if (globals != null)\r
+                       return globals;\r
+               \r
+            try {              \r
+                REXP result = realm.getConnection().eval("ls()");\r
+                if(result.isString()) {\r
+                       String[] names = result.asStrings();                    \r
+                       \r
+                       globals = new ArrayList<RVariableNode>(names.length);\r
+                       for (String n : names) {\r
+                               RGlobalVariableNode child = new RGlobalVariableNode(this, n);\r
+                               globals.add(child);\r
+                               nodeCache.put(new Pair<RVariableNode, String>(this, n), child);\r
+                       }\r
+                       return globals;\r
+                }\r
+                else\r
+                    throw new NodeManagerException("ls() returned invalid result!");\r
+            } catch (RserveException e) {\r
+                throw new NodeManagerException(e);\r
+            } catch (REXPMismatchException e) {\r
+                throw new NodeManagerException(e);\r
+            }\r
+        }\r
+        else if (node instanceof RListLengthNode) {\r
+               return Collections.emptyList();\r
+        }\r
+        else {\r
+               List<RVariableNode> props = propertyCache.get(node);\r
+               if (props != null)\r
+                       return props;\r
+               \r
+               props = new ArrayList<RVariableNode>();\r
+               \r
+               REXP value = node.getValue();\r
+               if (value == null)\r
+                       return Collections.emptyList();\r
+               \r
+               // Create attribute nodes\r
+               REXPList attrs = value._attr();\r
+               if (attrs != null) {\r
+                       RList list = attrs.asList();\r
+                       for (int i = 0; i < list.size(); i++)\r
+                               props.add(new RAttributeNode(node, list.keyAt(i)));\r
+               }\r
+               \r
+               // Create list item nodes\r
+               if (value.isList()) {\r
+                       RList list;\r
+                               try {\r
+                                       list = value.asList();\r
+                               String[] keys = list.keys();\r
+                               for (int i = 0; i < list.size(); i++) {\r
+                                       props.add(new RListItemNode(node, i));\r
+                                       if (keys != null && keys[i] != null)\r
+                                               props.add(new RNamedItemNode(node, keys[i]));\r
+                               }\r
+                               } catch (REXPMismatchException e) {\r
+                                       // Shouldn't happen                                     \r
+                               }\r
+               }\r
+               \r
+               try {\r
+                       int length = value.length();            \r
+                       if (length >= 0)\r
+                               props.add(new RListLengthNode(node));\r
+               }\r
+               catch (REXPMismatchException e) {\r
+                       // Does not have a length\r
+               }\r
+               \r
+               propertyCache.put(node, props);\r
+               \r
+               for (RVariableNode n : props) {\r
+                       nodeCache.put(new Pair<RVariableNode, String>(node, n.getName()), n);\r
+               }\r
+               \r
+               return props;\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public Datatype getDatatype(RVariableNode node) throws NodeManagerException {\r
+        checkThreadAccess();\r
+        if (node == this) return null;\r
+        \r
+        if (node instanceof RListLengthNode)\r
+               return Datatypes.INTEGER;\r
+        \r
+        REXP value = node.getValue();\r
+        try {\r
+                       return RDataboardConversion.getDatatype(value);\r
+               } catch (BindingException e) {\r
+                       throw new NodeManagerException(e);\r
+               }\r
+    }\r
+\r
+    @Override\r
+    public Object getValue(RVariableNode node, Binding binding)\r
+            throws NodeManagerException, BindingException {\r
+        checkThreadAccess();\r
+        if (node == this) return null;\r
+        \r
+        REXP value = node.getValue();\r
+        if (binding == null || binding.isInstance(value))\r
+               return value;\r
+        else\r
+               return RDataboardConversion.fromREXP(value, binding);\r
+    }\r
+\r
+    @Override\r
+    public void setValue(RVariableNode node, Object value, Binding binding)\r
+            throws NodeManagerException, BindingException {\r
+        checkThreadAccess();\r
+        \r
+        if (node.getParent() != this)\r
+               throw new NodeManagerException("Assignment only allowed on top-level nodes");\r
+        \r
+        String name = node.getName();\r
+        REXP rexp = RDataboardConversion.toREXP(value, binding);\r
+        try {\r
+            realm.getConnection().assign(name, rexp);\r
+            refreshVariables();\r
+        } catch (RserveException e) {\r
+            throw new NodeManagerException(e);\r
+        }\r
+    }\r
+\r
+    static final Set<String> COMPONENT_CLASS = Collections.singleton(StructuralResource2.URIs.Component);\r
+            \r
+    @Override\r
+    public Set<String> getClassifications(RVariableNode node) throws NodeManagerException {\r
+        checkThreadAccess();\r
+        if(node.equals(this))\r
+            return COMPONENT_CLASS;\r
+        else\r
+            return Collections.emptySet();\r
+    }\r
\r
+    private void checkThreadAccess() throws NodeManagerException {\r
+        if(Thread.currentThread() != realm.getThread())\r
+            throw new NotInRealmException();\r
+    }\r
+    \r
+    @Override\r
+    public String getPropertyURI(RVariableNode parent, RVariableNode property) {\r
+        return "http://www.simantics.org/R-1.0/Session/hasValue";\r
+    }\r
+\r
+    // Methods for the RVariableNode interface\r
+\r
+       @Override\r
+       public String getName() {\r
+               return realm.getId();\r
+       }\r
+\r
+       @Override\r
+       public RVariableNode getParent() {\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       public REXP getValue() {\r
+               return null;\r
+       }\r
+       \r
+       public REXP getGlobalValue(String name) {\r
+               try {\r
+                       return realm.getConnection().get(name, null, true);\r
+               } catch (REngineException e) {\r
+                       return null;\r
+               }\r
+       }\r
+}\r
diff --git a/org.simantics.r.scl/src/org/simantics/r/scl/variable/RVariableNode.java b/org.simantics.r.scl/src/org/simantics/r/scl/variable/RVariableNode.java
new file mode 100644 (file)
index 0000000..8dbf0d4
--- /dev/null
@@ -0,0 +1,11 @@
+package org.simantics.r.scl.variable;\r
+\r
+import org.rosuda.REngine.REXP;\r
+\r
+public interface RVariableNode {\r
+       public REXP getValue();\r
+\r
+       public String getName();\r
+\r
+       public RVariableNode getParent();       \r
+}\r
diff --git a/org.simantics.r/.classpath b/org.simantics.r/.classpath
new file mode 100644 (file)
index 0000000..b1dabee
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>\r
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>\r
+       <classpathentry kind="src" path="src"/>\r
+       <classpathentry kind="output" path="bin"/>\r
+</classpath>\r
diff --git a/org.simantics.r/.project b/org.simantics.r/.project
new file mode 100644 (file)
index 0000000..f2c998b
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+       <name>org.simantics.r</name>\r
+       <comment></comment>\r
+       <projects>\r
+       </projects>\r
+       <buildSpec>\r
+               <buildCommand>\r
+                       <name>org.eclipse.jdt.core.javabuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+               <buildCommand>\r
+                       <name>org.eclipse.pde.ManifestBuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+               <buildCommand>\r
+                       <name>org.eclipse.pde.SchemaBuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+       </buildSpec>\r
+       <natures>\r
+               <nature>org.eclipse.pde.PluginNature</nature>\r
+               <nature>org.eclipse.jdt.core.javanature</nature>\r
+       </natures>\r
+</projectDescription>\r
diff --git a/org.simantics.r/.settings/org.eclipse.jdt.core.prefs b/org.simantics.r/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..11f6e46
--- /dev/null
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1\r
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\r
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7\r
+org.eclipse.jdt.core.compiler.compliance=1.7\r
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error\r
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error\r
+org.eclipse.jdt.core.compiler.source=1.7\r
diff --git a/org.simantics.r/META-INF/MANIFEST.MF b/org.simantics.r/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..7b9ea88
--- /dev/null
@@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: R Variable for Simantics
+Bundle-SymbolicName: org.simantics.r;singleton:=true
+Bundle-Version: 0.1.0.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Require-Bundle: org.simantics.r.ontology;bundle-version="1.0.0",
+ org.simantics.r.scl;bundle-version="0.0.1",
+ org.simantics.scl.runtime;bundle-version="0.4.0",
+ org.simantics.db.layer0;bundle-version="1.1.0",
+ org.simantics.simulator.variable;bundle-version="1.0.0"
+Bundle-Vendor: Association for Decentralized Information Management in Industry THTH ry
diff --git a/org.simantics.r/adapters.xml b/org.simantics.r/adapters.xml
new file mode 100644 (file)
index 0000000..896e3c3
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<adapters>\r
+\r
+    <target interface="org.simantics.db.layer0.variable.VariableBuilder">\r
+        <type uri="http://www.simantics.org/R-1.0/Session" class="org.simantics.r.RVariableBuilder"/>\r
+    </target>\r
+\r
+</adapters>\r
diff --git a/org.simantics.r/build.properties b/org.simantics.r/build.properties
new file mode 100644 (file)
index 0000000..fb3509c
--- /dev/null
@@ -0,0 +1,17 @@
+###############################################################################\r
+# Copyright (c) 2014, 2016 Association for Decentralized Information Management\r
+# in Industry THTH ry.\r
+# All rights reserved. This program and the accompanying materials\r
+# are made available under the terms of the Eclipse Public License v1.0\r
+# which accompanies this distribution, and is available at\r
+# http://www.eclipse.org/legal/epl-v10.html\r
+#\r
+# Contributors:\r
+#     VTT Technical Research Centre of Finland - initial API and implementation\r
+###############################################################################\r
+source.. = src/\r
+output.. = bin/\r
+bin.includes = META-INF/,\\r
+               .,\\r
+               adapters.xml,\\r
+               scl/\r
diff --git a/org.simantics.r/scl/R/RConfiguration.scl b/org.simantics.r/scl/R/RConfiguration.scl
new file mode 100644 (file)
index 0000000..eff8d88
--- /dev/null
@@ -0,0 +1,66 @@
+import "R/R" as R\r
+\r
+import "Simantics/DB"\r
+import "http://www.simantics.org/R-1.0" as ROntology\r
+import "http://www.simantics.org/Layer0-1.1" as L0\r
+\r
+"""Creates a new session configuration to graph. This function does\r
+not link it to any other resources."""\r
+createSessionConfiguration :: Resource -> R.SessionConfiguration -> <WriteGraph> Resource\r
+createSessionConfiguration parent (R.SessionConfiguration host port username password) = do \r
+    r = newResource ()\r
+    claim r L0.InstanceOf ROntology.SessionConfiguration\r
+    claim r L0.PartOf parent\r
+    claimRelatedValue r L0.HasName host\r
+    claimRelatedValue r ROntology.SessionConfiguration.host host\r
+    claimRelatedValue r ROntology.SessionConfiguration.port") port\r
+    claimRelatedValue r ROntology.SessionConfiguration.username username\r
+    claimRelatedValue r ROntology.SessionConfiguration.password password\r
+    r\r
+\r
+addScript :: Resource -> String -> <WriteGraph> Resource\r
+addScript sessionConfiguration scriptText = do \r
+    r = newResource ()\r
+    claim r L0.InstanceOf ROntology.Script\r
+    claim sessionConfiguration ROntology.SessionConfiguration.hasScript r\r
+    claimRelatedValue r ROntology.Script.text scriptText\r
+    r\r
+\r
+readSessionConfiguration :: Resource -> <ReadGraph> R.SessionConfiguration\r
+readSessionConfiguration r = R.SessionConfiguration\r
+    (relatedValue r ROntology.SessionConfiguration.host)\r
+    (relatedValue r ROntology.SessionConfiguration.port)\r
+    (relatedValue r ROntology.SessionConfiguration.username)\r
+    (relatedValue r ROntology.SessionConfiguration.password)\r
+\r
+writeSession :: R.Session -> <WriteGraph> Resource\r
+writeSession session = do \r
+    r = newResource ()\r
+    claim r L0.InstanceOf ROntology.Session\r
+    claimRelatedValue r L0.HasName $ R.sessionIdOf session\r
+    r\r
+\r
+readSession :: Resource -> <ReadGraph,Proc> R.Session\r
+readSession r = let\r
+    sessionId = relatedValue r L0.HasName\r
+  in match R.sessionById sessionId with\r
+    Just session -> session\r
+    Nothing -> R.getOrCreateSession (readSessionConfiguration $ singleObject r L0.PartOf) sessionId\r
+      \r
+\r
+createSession :: Resource -> <WriteGraph,Proc> Resource\r
+createSession configurationResource = do\r
+    session = R.createSession $ readSessionConfiguration configurationResource\r
+    for (configurationResource # ROntology.SessionConfiguration.hasScript $ \s -> do\r
+        scriptText = relatedValue s ROntology.Script.text")\r
+        R.asyncExec session (R.evalR_ scriptText)\r
+    sessionResource = writeSession session\r
+    claim configurationResource L0.ConsistsOf sessionResource\r
+    sessionResource\r
+\r
+deleteSession :: Resource -> <WriteGraph,Proc> ()\r
+deleteSession r = do\r
+    session = readSession r\r
+    R.closeSession session\r
+    denyAllStatements r\r
+\r
diff --git a/org.simantics.r/scl/R/TestRConfiguration.scl b/org.simantics.r/scl/R/TestRConfiguration.scl
new file mode 100644 (file)
index 0000000..ea7937a
--- /dev/null
@@ -0,0 +1,25 @@
+include "R/RConfiguration"\r
+include "R/R" as R\r
+include "Simantics/DB" as DB\r
+include "http://www.simantics.org/Layer0-1.1" as L0\r
+\r
+test currentModel = do\r
+    // Configure session\r
+    //currentModel = DB.syncRead $ \_ -> DB.currentModel\r
+    sessionConf = DB.syncWrite $ \_ -> do\r
+        DB.claim currentModel L0.IsLinkedTo (DB.resource "http://www.simantics.org/R-1.0")\r
+        sessionConf = createSessionConfiguration currentModel $ \r
+            R.SessionConfiguration "localhost" 6311 "simupedia" "simupedia"\r
+        addScript sessionConf """a = 13\r
+b = 14\r
+c = 15"""\r
+        sessionConf\r
+\r
+    // Create session\r
+    sessionR = DB.syncWrite $ \_ -> createSession sessionConf\r
+    \r
+    // Compute something\r
+    session = DB.syncRead $ \_ -> readSession sessionR\r
+    R.syncExec session (R.evalR_ "d = a+b+c")\r
+    print $ R.syncExec session (R.evalR "ls()" :: Vector String)\r
+    session
\ No newline at end of file
diff --git a/org.simantics.r/src/org/simantics/r/RVariableBuilder.java b/org.simantics.r/src/org/simantics/r/RVariableBuilder.java
new file mode 100644 (file)
index 0000000..90261a3
--- /dev/null
@@ -0,0 +1,61 @@
+/*******************************************************************************\r
+ * Copyright (c) 2014, 2016 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.r;\r
+\r
+import org.rosuda.REngine.Rserve.RserveException;\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.variable.StandardGraphChildVariable;\r
+import org.simantics.db.layer0.variable.Variable;\r
+import org.simantics.db.layer0.variable.VariableBuilder;\r
+import org.simantics.db.layer0.variable.VariableNode;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.r.scl.RSession;\r
+import org.simantics.r.scl.RSessionConfiguration;\r
+import org.simantics.r.scl.RSessionManager;\r
+import org.simantics.r.scl.variable.RVariableNode;\r
+import org.simantics.simulator.variable.NodeManager;\r
+\r
+public class RVariableBuilder implements VariableBuilder {\r
+\r
+    @Override\r
+    public Variable buildChild(ReadGraph graph, Variable parent, VariableNode node, Resource child) throws DatabaseException {\r
+        Layer0 L0 = Layer0.getInstance(graph);\r
+        RResource R = RResource.getInstance(graph);\r
+        String name = graph.getRelatedValue(child, L0.HasName, Bindings.STRING);\r
+        RSession session = RSessionManager.getSession(name);\r
+        if(session == null) {\r
+            Resource conf = graph.getSingleObject(child, L0.PartOf);\r
+            RSessionConfiguration configuration = new RSessionConfiguration(\r
+                    graph.<String>getRelatedValue(conf, R.SessionConfiguration_host, Bindings.STRING),\r
+                    graph.<Integer>getRelatedValue(conf, R.SessionConfiguration_port, Bindings.INTEGER),\r
+                    graph.<String>getRelatedValue(conf, R.SessionConfiguration_username, Bindings.STRING),\r
+                    graph.<String>getRelatedValue(conf, R.SessionConfiguration_password, Bindings.STRING));\r
+            try {\r
+                session = RSessionManager.getOrCreateSession(configuration, name);\r
+            } catch (RserveException e) {\r
+                throw new DatabaseException(e);\r
+            }\r
+        }\r
+        \r
+        NodeManager<RVariableNode> nodeManager = session.getNodeManager();\r
+        return new StandardGraphChildVariable(parent, new VariableNode(nodeManager, /*node=*/nodeManager), child);\r
+    }\r
+\r
+    @Override\r
+    public Variable buildProperty(ReadGraph graph, Variable parent, VariableNode node, Resource subject, Resource predicate) throws DatabaseException {\r
+        throw new UnsupportedOperationException();\r
+    }\r
+\r
+}\r