├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── build.sbt ├── project ├── build.properties └── plugins.sbt └── src ├── main ├── java │ └── com │ │ └── ansvia │ │ └── graph │ │ └── annotation │ │ └── Persistent.java └── scala │ └── com │ └── ansvia │ └── graph │ ├── AbstractDbObject.scala │ ├── AbstractIDGetter.scala │ ├── BlueprintsWrapper.scala │ ├── DbWrapper.scala │ ├── EdgeWrapper.scala │ ├── Exc.scala │ ├── IDGetter.scala │ ├── Log.scala │ ├── ObjectConverter.scala │ ├── TitanDbWrapper.scala │ ├── Wrapper.scala │ ├── gremlin.scala │ ├── testing │ └── model │ │ ├── Animal.scala │ │ ├── ColoredFish.scala │ │ ├── Complex.scala │ │ ├── ContainLazy.scala │ │ ├── Eatable.scala │ │ ├── Fish.scala │ │ ├── ModelUsingAbstractIDGetter.scala │ │ ├── Motor.scala │ │ ├── SeaFish.scala │ │ ├── Shark.scala │ │ ├── SimpleDbo.scala │ │ └── User.scala │ └── util │ ├── CallersContext.scala │ ├── Poso.scala │ └── scalax │ └── rules │ ├── Memoisable.scala │ ├── Result.scala │ ├── Rule.scala │ ├── Rules.scala │ ├── SeqRule.scala │ └── scalasig │ ├── ClassFileParser.scala │ ├── Flags.scala │ ├── ScalaSig.scala │ ├── Symbol.scala │ └── Type.scala └── test └── scala └── com └── ansvia └── graph ├── AbstractIDGetterSpec.scala ├── BlueprintsTransactSpec.scala ├── BlueprintsWrapperSpec.scala ├── DbObjectComplexSpec.scala ├── DbObjectOptionalSpec.scala ├── DbObjectSimpleSpec.scala ├── DifferentClassLoaderSpec.scala ├── GremlinWrapperSpec.scala ├── ObjectConverterSpec.scala ├── TitanBackedDb.scala └── VertexLabelSpec.scala /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | target 3 | .idea/ 4 | .idea_modules/ 5 | sbt/ 6 | atlassian-ide-plugin.xml 7 | .history 8 | 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | scala: 3 | - "2.10.4" 4 | - "2.11.0" 5 | 6 | jdk: 7 | - oraclejdk7 8 | - oraclejdk8 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2012-2013 Ansvia 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tinkerpop Blueprints Scala [![Build Status](https://travis-ci.org/anvie/blueprints-scala.png?branch=master)](https://travis-ci.org/anvie/blueprints-scala) 2 | =================================== 3 | 4 | 5 | Scala wrapper for tinkerpop blueprints, this library provide more scalastic code when working with graph database 6 | supported by blueprints. 7 | 8 | Usage 9 | --------- 10 | 11 | For installation see install section. 12 | 13 | * For Scala 2.10 see https://github.com/anvie/blueprints-scala/tree/scala-2.10 14 | * For Scala 2.9 see https://github.com/anvie/blueprints-scala/tree/scala-2.9 15 | 16 | 17 | More working and complete examples can be found in specs test. 18 | 19 | Creating new vertex: 20 | 21 | ```scala 22 | case class Person(name:String, kind:String) 23 | 24 | val hercules = Person("hercules", "demigod") 25 | 26 | db.save(hercules) 27 | ``` 28 | 29 | If your class subclassing from `DbObject` you will have more sweet code like using `save()` directly: 30 | 31 | ```scala 32 | 33 | case class Person(name:String, kind:String) extends DbObject 34 | 35 | val hercules = Person("hercules", "demigod").save() 36 | ``` 37 | 38 | The `save()` method return a Vertex object, if you want to get class back just: 39 | 40 | ```scala 41 | val herculesObject = hercules.toCC[Person].get 42 | ``` 43 | 44 | Creating edges: 45 | 46 | ```scala 47 | hercules --> "father" --> jupiter 48 | hercules --> "mother" --> alcmene 49 | ``` 50 | Or you can also add multiple edges in one line: 51 | 52 | ```scala 53 | hercules --> "father" --> jupiter --> "father" --> saturn 54 | ``` 55 | 56 | Creating mutual (both) edges: 57 | 58 | ```scala 59 | jupiter <--> "brother" <--> neptune 60 | ``` 61 | 62 | Or more complex mutual edges: 63 | 64 | ```scala 65 | jupiter <--> "brother" <--> neptune <--> "brother" <--> pluto 66 | ``` 67 | 68 | Shorthand property getter and setter: 69 | 70 | ```scala 71 | jupiter.set("kind", "god") 72 | 73 | val kind = jupiter.get[String]("kind").get 74 | ``` 75 | 76 | Getter `get` return Option instance, so you can handle unexpected return data efficiently using map: 77 | 78 | ```scala 79 | jupiter.get[String]("kind") map { kind => 80 | // do with kind here 81 | } 82 | ``` 83 | 84 | Or getting value by using default value if empty: 85 | 86 | ```scala 87 | jupiter.getOrElse[String]("status", "live") 88 | ``` 89 | 90 | Easy getting mutual connection, for example jupiter and pluto is brother both has IN and OUT edges, 91 | is very easy to get mutual connection using `mutual` method: 92 | 93 | ```scala 94 | val jupitersBrothers = jupiter.mutual("brother") 95 | ``` 96 | 97 | Inspect helpers to print vertex list: 98 | 99 | ```scala 100 | jupitersBrothers.printDump("jupiter brothers:", "name") 101 | 102 | // will produce output: 103 | 104 | jupiter brothers: 105 | + neptune 106 | + pluto 107 | ``` 108 | 109 | Using Gremlin Pipeline like a boss: 110 | 111 | ```scala 112 | val battled = hercules.pipe.out("battled") 113 | battled.printDump("hercules battled:", "name") 114 | 115 | // output: 116 | 117 | hercules battled: 118 | + nemean 119 | + hydra 120 | + cerberus 121 | ``` 122 | 123 | Syntactic sugar filtering on Gremlin Pipeline: 124 | 125 | ```scala 126 | // get monster battled with hercules more than 5 times 127 | val monsters = hercules.pipe.outE("battled").wrap.filter { edge => 128 | edge.getOrElse[Int]("time", 0).toString.toInt > 5 129 | } 130 | ``` 131 | 132 | Returning edges from chain by adding `<` on the last line: 133 | 134 | ```scala 135 | var edge = hercules --> "battled" --> hydra < 136 | 137 | edge.set("time", 2) 138 | ``` 139 | 140 | Using transaction: 141 | 142 | ```scala 143 | transact { 144 | hercules --> "father" --> jupiter 145 | hercules --> "mother" --> alcmene 146 | val edge = hercules --> "battled" --> hydra < 147 | 148 | edge.set("time", 15) 149 | } 150 | ``` 151 | 152 | Want more attributes that not fit for case class constructor parameter? 153 | 154 | Use `@Persistent` annotation: 155 | 156 | ```scala 157 | 158 | case class City(name:String){ 159 | 160 | @Persistent var province = "Jakarta" // this attribute will be saved 161 | @Persistent var country = "Indonesia" // this attribute will be saved 162 | 163 | val code = 123 // this not 164 | 165 | } 166 | 167 | ``` 168 | 169 | Support inheritance as well: 170 | 171 | ```scala 172 | 173 | abstract class City(name:String) { 174 | @Persistent var province = "" 175 | } 176 | 177 | trait Street { 178 | @Persistent var street = "" 179 | } 180 | 181 | trait Code { 182 | @Persistent var postalCode = 0 183 | } 184 | 185 | 186 | case class Jakarta extends City("Jakarta") with Street with Code with DbObject 187 | 188 | // usage 189 | 190 | val address = Jakarta() 191 | address.province = "Jakarta Barat" // this will be saved 192 | address.street = "KS. Tubun" // this will be saved 193 | address.postalCode = 12345 // this will be saved 194 | address.save() 195 | 196 | // getting back 197 | 198 | db.getVertex(address.getVertex.getId).toCC[Jakarta] map { adr => 199 | println(adr.province) // will printed `Jakarta Barat` 200 | println(adr.postalCode) // will printed `12345` 201 | } 202 | 203 | // instantiate to parent class 204 | // this is valid 205 | 206 | db.getVertex(address.getVertex.getId).toCC[City] map { adr => 207 | println(adr.province) // will printed `Jakarta Barat` 208 | } 209 | 210 | // this also valid 211 | 212 | db.getVertex(address.getVertex.getId).toCC[Street] map { adr => 213 | println(adr.street) // will printed `KS. Tubun` 214 | } 215 | 216 | ``` 217 | 218 | 219 | How it work? All you needs is to adding dependency (see install), import it, and summon Graph db instance with implicit: 220 | 221 | ```scala 222 | import com.ansvia.graph.BlueprintsWrapper._ 223 | 224 | // you can use any graph database that support tinkerpop blueprints 225 | // here is i'm using simple in-memory Tinkergraph db for example. 226 | implicit val db = TinkerGraphFactory.createTinkerGraph() 227 | 228 | //... work here .... 229 | ``` 230 | 231 | Done, now you can using Scalastic sweet syntactic sugar code. 232 | 233 | 234 | Install 235 | -------- 236 | 237 | 238 | 239 | 240 | 241 | 250 | 251 | For Scala 2.11 currently only SNAPSHOT releases available: 252 | 253 | "Sonatype repo" at "https://oss.sonatype.org/content/repositories/snapshots" 254 | 255 | "com.ansvia.graph" %% "blueprints-scala" % "0.1.61-20150416-SNAPSHOT" 256 | 257 | 258 | License 259 | --------- 260 | 261 | [Apache 2](http://www.apache.org/licenses/LICENSE-2.0.html) 262 | 263 | 264 | ***[] Robin Sy.*** 265 | 266 | [![Donation](https://pledgie.com/campaigns/23969.png)](https://pledgie.com/campaigns/23969) 267 | 268 | [Support me via Gittip](https://www.gittip.com/anvie/) 269 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | import java.text.SimpleDateFormat 2 | 3 | import SonatypeKeys._ 4 | 5 | organization := "com.ansvia.graph" 6 | 7 | name := "blueprints-scala" 8 | 9 | version := "0.1.61-SNAPSHOT" 10 | 11 | scalaVersion := "2.11.0" 12 | 13 | crossScalaVersions := Seq("2.11.0", "2.10.0") 14 | 15 | scalacOptions := Seq("-unchecked", "-deprecation", "-feature", "-language:implicitConversions") 16 | 17 | resolvers ++= Seq( 18 | "typesafe repo" at "http://repo.typesafe.com/typesafe/releases/", 19 | "Ansvia repo" at "http://scala.repo.ansvia.com/releases/" 20 | ) 21 | 22 | libraryDependencies ++= Seq( 23 | "org.scala-lang" % "scala-reflect" % scalaVersion.value, 24 | "org.slf4j" % "slf4j-api" % "1.7.6", 25 | "com.tinkerpop.blueprints" % "blueprints-core" % "2.5.0", 26 | "com.tinkerpop.gremlin" % "gremlin-groovy" % "2.5.0", 27 | "com.tinkerpop.gremlin" % "gremlin-java" % "2.5.0", 28 | "org.specs2" %% "specs2-core" % "2.4.15" % "test", 29 | "com.thinkaurelius.titan" % "titan-core" % "0.5.2" % "provided", 30 | "com.thinkaurelius.titan" % "titan-berkeleyje" % "0.5.2" % "test" 31 | ) 32 | 33 | 34 | scalacOptions ++= { 35 | CrossVersion.partialVersion(scalaVersion.value) match { 36 | // if scala 2.11+ is used, be strict about compiler warnings 37 | case Some((2, scalaMajor)) if scalaMajor >= 11 => 38 | Seq("-Xfatal-warnings") 39 | case _ => 40 | Nil 41 | } 42 | } 43 | 44 | 45 | 46 | sonatypeSettings 47 | 48 | profileName := "com.ansvia" 49 | 50 | publishTo <<= version { (v:String) => 51 | val nexus = "https://oss.sonatype.org" 52 | if(v.trim.endsWith("SNAPSHOT") || """.+\-\d{8}+$""".r.pattern.matcher(v.trim).matches()) 53 | Some("snapshots" at nexus + "/content/repositories/snapshots") 54 | else 55 | Some("releases" at nexus + "/service/local/staging/deploy/maven2") 56 | } 57 | 58 | version <<= version { (v:String) => 59 | if (v.trim.endsWith("-SNAPSHOT")){ 60 | val dateFormatter = new SimpleDateFormat("yyyyMMdd") 61 | v.trim.split("-").apply(0) + "-" + dateFormatter.format(new java.util.Date()) + "-SNAPSHOT" 62 | }else 63 | v 64 | } 65 | 66 | credentials += Credentials { 67 | val sonatype = Path.userHome / ".ivy2" / ".credentials-sonatype" 68 | if (new File(sonatype.toString).exists()) 69 | sonatype 70 | else 71 | Path.userHome / ".ivy2" / ".credentials" 72 | } 73 | 74 | publishArtifact in Test := false 75 | 76 | pomIncludeRepository := { _ => false } 77 | 78 | crossPaths := true 79 | 80 | publishMavenStyle := true 81 | 82 | pomExtra := ( 83 | http://www.ansvia.com 84 | 85 | 86 | Apache 2.0 87 | http://www.apache.org/licenses/LICENSE-2.0 88 | repo 89 | 90 | 91 | 92 | git@github.com:anvie/blueprints-scala.git 93 | scm:git:git@github.com:anvie/blueprints-scala.git 94 | 95 | 96 | 97 | robin 98 | Robin Syihab 99 | http://mindtalk.com/u/robin 100 | 101 | 102 | tysonjh 103 | Tyson Hamilton 104 | http://tysonhamilton.com 105 | 106 | 107 | vikraman 108 | Vikraman Choudhury 109 | http://vh4x0r.wordpress.com 110 | 111 | 112 | pdolega 113 | Pawel Dolega 114 | http://nexelem.com 115 | 116 | ) 117 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.7 -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "0.2.1") 2 | 3 | addSbtPlugin("com.typesafe.sbt" % "sbt-pgp" % "0.8.3") 4 | 5 | addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.6.0") 6 | -------------------------------------------------------------------------------- /src/main/java/com/ansvia/graph/annotation/Persistent.java: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.annotation; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.Target; 6 | 7 | /** 8 | * Author: robin 9 | * Date: 1/14/13 10 | * Time: 10:42 PM 11 | */ 12 | 13 | @Documented 14 | @Retention(java.lang.annotation.RetentionPolicy.RUNTIME) 15 | @Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) 16 | public @interface Persistent {} 17 | 18 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/AbstractDbObject.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import com.tinkerpop.blueprints._ 4 | 5 | trait AbstractDbObject { 6 | def getVertex:Vertex 7 | def save()(implicit db:Graph):Vertex 8 | def delete()(implicit db:Graph) 9 | def isSaved:Boolean 10 | def reload()(implicit db: Graph):this.type 11 | def -->(label:String)(implicit db:Graph):EdgeWrapper 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/AbstractIDGetter.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | /** 4 | * Author: robin 5 | * Date: 11/4/13 6 | * Time: 1:18 PM 7 | * 8 | */ 9 | 10 | trait AbstractIDGetter[IDType] { 11 | def getId:IDType 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/BlueprintsWrapper.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import java.lang.Iterable 4 | 5 | import com.ansvia.graph.Exc.NotBoundException 6 | import com.ansvia.graph.util.CallersContext 7 | import com.tinkerpop.blueprints._ 8 | import com.tinkerpop.blueprints.util.wrappers.id.{IdGraph, IdVertex} 9 | import com.tinkerpop.gremlin.java.GremlinPipeline 10 | import com.tinkerpop.pipes.util.structures.{Pair => BPPair} 11 | 12 | import scala.language.{implicitConversions, reflectiveCalls} 13 | import scala.reflect._ 14 | import scala.reflect.runtime.universe._ 15 | 16 | 17 | 18 | object BlueprintsWrapper { 19 | import scala.collection.JavaConversions._ 20 | 21 | val defaultClassloader = CallersContext.fetchDefaultClassLoader 22 | 23 | 24 | case class ScalasticPropertyAccessor[A <: Element : ClassTag](var obj:A) { 25 | 26 | /** 27 | * Syntactic sugar property getter. 28 | * will return Option[T]. 29 | * example usage: 30 | * 31 | *
 32 |          * val x = vertex.get[String]("location")
 33 |          * if (x.isDefined){
 34 |          *      // do here if defined
 35 |          * }
 36 |          *
 37 |          * or using map
 38 |          *
 39 |          * vertex.get[String]("location") map { location =>
 40 |          *      // do with naked location (String)
 41 |          * }
 42 |          * 
43 | * 44 | * @param key property key. 45 | * @tparam T template to return. 46 | * @return 47 | */ 48 | def get[T: TypeTag](key:String):Option[T] = { 49 | obj.getProperty[AnyRef](key) match { 50 | case null => None 51 | case None => None 52 | case anyOther => Some(anyOther.asInstanceOf[T]) 53 | } 54 | } 55 | 56 | /** 57 | * Syntactic sugar property getter with 58 | * default value if not defined. 59 | * 60 | * Example: 61 | * 62 | *
 63 |          * val x = vertex.getOrElse[String]("phone", "-")
 64 |          * 
65 | * 66 | * @param key property key. 67 | * @param default default value when empty. 68 | * @tparam T return type. 69 | * @return 70 | */ 71 | def getOrElse[T : TypeTag](key:String, default:T): T = get[T](key).getOrElse(default) 72 | 73 | /** 74 | * Syntactic sugar for property setter. 75 | * @param key property key. 76 | * @param value property value. 77 | */ 78 | def set(key:String, value:Any) = { 79 | obj.setProperty(key, value) 80 | this 81 | } 82 | 83 | /** 84 | * Check is has key 85 | * @param key property key 86 | * @return 87 | */ 88 | def has(key:String):Boolean = { 89 | obj.getProperty(key) != null 90 | } 91 | 92 | /** 93 | * Reload 94 | * @param db 95 | * @return 96 | */ 97 | def reload()(implicit db:Graph) = { 98 | _reloadInner(db) 99 | } 100 | 101 | private def _reloadInner(db:Graph)(implicit tag:ClassTag[A]) = { 102 | tag.runtimeClass.toString match { 103 | case "interface com.tinkerpop.blueprints.Vertex" => 104 | db.getVertex(obj.asInstanceOf[Vertex].getId).asInstanceOf[A] 105 | case "interface com.tinkerpop.blueprints.Edge" => 106 | db.getEdge(obj.asInstanceOf[Edge].getId).asInstanceOf[A] 107 | } 108 | } 109 | 110 | /** 111 | * Deserialize object to case class. 112 | * @tparam T case class type. 113 | */ 114 | def toCC[T: ClassTag]:Option[T] = toCC[T](defaultClassloader) 115 | 116 | /** 117 | * Deserialize object to case class. 118 | * @tparam T case class type. 119 | * @param classLoader explicitly specified classloader if needed. 120 | */ 121 | def toCC[T: ClassTag](classLoader: ClassLoader):Option[T] = { 122 | ObjectConverter.toCC[T](obj, classLoader) 123 | } 124 | } 125 | 126 | implicit def vertexToPropertyAccessor(elm:Vertex) = ScalasticPropertyAccessor(elm) 127 | implicit def edgeToPropertyAccessor(elm:Edge) = ScalasticPropertyAccessor(elm) 128 | implicit def elementToPropertyAccessor(elm:Element) = ScalasticPropertyAccessor(elm) 129 | 130 | 131 | /** 132 | * Vertex wrapper on arrow chain. 133 | * This wrapper automatically called via implicit vertexWrapper function. 134 | * @param vertex vertex. 135 | * @param label label. 136 | * @param db database object. 137 | */ 138 | case class VertexWrapper(var vertex:Vertex, var label:String, db:Graph) 139 | extends Wrapper { 140 | 141 | var prev:Option[EdgeWrapper] = None 142 | 143 | def -->(label:String):EdgeWrapper = { 144 | this.label = label 145 | 146 | // for performance reason 147 | // we using previous object if any 148 | 149 | val next = prev.getOrElse { 150 | EdgeWrapper(vertex, label, db) 151 | } 152 | next.prev = Some(this) 153 | next.vertex = vertex 154 | next.label = label 155 | next 156 | } 157 | 158 | def <--(label:String):EdgeWrapper = { 159 | this.label = label 160 | 161 | // for performance reason 162 | // we using previous object if any 163 | 164 | val next = prev.getOrElse { 165 | EdgeWrapper(vertex, label, db) 166 | } 167 | next.prev = Some(this) 168 | next.vertex = vertex 169 | next.label = label 170 | next 171 | } 172 | 173 | def <():Edge = { 174 | if (this.prev.isDefined) 175 | this.prev.get <() 176 | else 177 | null 178 | } 179 | 180 | /** 181 | * Create mutual connection. 182 | * @param label edge label. 183 | * @return 184 | */ 185 | def <-->(label:String):VertexWrapper = { 186 | this.label = label 187 | this 188 | } 189 | 190 | /** 191 | * Create mutual connection. 192 | * @param bothV another vertex to connect. 193 | * @return 194 | */ 195 | def <-->(bothV:Vertex):VertexWrapper = { 196 | assert(label != null, "no label?") 197 | db.addEdge(null, vertex, bothV, label) 198 | db.addEdge(null, bothV, vertex, label) 199 | // update current vertex in chain 200 | vertex = bothV 201 | this 202 | } 203 | 204 | /** 205 | * Get list of mutual vertices. 206 | * @param label label name. 207 | * @return 208 | */ 209 | def mutual(label:String):Iterable[Vertex] = { 210 | val vx = vertex.getVertices(Direction.BOTH, label).toList 211 | vx.filter { v => 212 | v.getId != vertex.getId && 213 | vx.count( vv => v == vv ) == 2 214 | } 215 | } 216 | 217 | /** 218 | * get gremlin pipe from the vertex. 219 | * @return 220 | */ 221 | def pipe = { 222 | (new GremlinPipeline[Vertex, AnyRef]()).start(vertex) 223 | } 224 | 225 | } 226 | 227 | 228 | case class EdgeWrapperRight(vertex:Vertex, edge:Edge, label:String, db:Graph) extends Wrapper { 229 | def -->(v2:Vertex) = { 230 | db.addEdge(null, vertex, v2, label) 231 | } 232 | } 233 | 234 | case class EdgeWrapperLeft(edge:Edge, db:Graph) extends Wrapper { 235 | def -->(label:String):EdgeWrapperRight = { 236 | val v = edge.getVertex(Direction.OUT) 237 | EdgeWrapperRight(v, edge, label, db) 238 | } 239 | 240 | def <--(label:String):VertexWrapper = { 241 | val vertex = edge.getVertex(Direction.IN) 242 | VertexWrapper(vertex, label, db) 243 | } 244 | } 245 | 246 | implicit def vertexWrapper(vertex:Vertex)(implicit db:Graph) = VertexWrapper(vertex, null, db) 247 | implicit def edgeWrapper(edge:Edge)(implicit db:Graph) = EdgeWrapperLeft(edge, db) 248 | implicit def edgeFormatter(edge:Edge) = new { 249 | def prettyPrint(key:String) = { 250 | val in = edge.getVertex(Direction.IN) 251 | val label = edge.getLabel 252 | val out = edge.getVertex(Direction.OUT) 253 | "%s -->%s--> %s".format(out.getProperty(key), label, in.getProperty(key)) 254 | } 255 | } 256 | implicit def edgeIterableDumper(edges:Iterable[Edge]) = new { 257 | def printDump(key:String){ 258 | edges.foreach( edge => println(edge.prettyPrint(key)) ) 259 | } 260 | } 261 | implicit def vertexIterableDumper(vx:Iterable[Vertex]) = new { 262 | def printDump(title:String, key:String)(implicit db:Graph){ 263 | println(title) 264 | vx.foreach( v => println(" + " + v.getOrElse[String](key, "id:" + v.getId.toString)) ) 265 | } 266 | def printDumpGetList(title:String, key:String)(implicit db:Graph) = { 267 | println(title) 268 | val vxList = vx.toList 269 | vx.foreach( v => println(" + " + v.getOrElse[String](key, "id:" + v.getId.toString)) ) 270 | vxList 271 | } 272 | } 273 | 274 | /** 275 | * Working in transactional fashion. 276 | * @param wrappedFunc function 277 | * @param db implicit db 278 | * @return 279 | */ 280 | def transact[T](wrappedFunc: => T)(implicit db:TransactionalGraph):T = { 281 | try { 282 | val rv = wrappedFunc 283 | db.commit() 284 | rv 285 | }catch{ 286 | case e:Exception => 287 | db.rollback() 288 | throw e 289 | } 290 | } 291 | 292 | implicit def dbWrapper(db:Graph) = StdDbWrapper.dbWrapper(db) //new DbWrapper(db) 293 | 294 | 295 | /** 296 | * All model should inherit this trait. 297 | */ 298 | trait DbObject extends AbstractDbObject { 299 | 300 | protected var vertex:Vertex = null 301 | 302 | /** 303 | * Save this object to database. 304 | */ 305 | def save()(implicit db:Graph):Vertex = { 306 | vertex = db.save(this) 307 | vertex 308 | } 309 | 310 | /** 311 | * Delete this object from database. 312 | */ 313 | def delete()(implicit db:Graph){ 314 | db.delete(this) 315 | vertex = null 316 | } 317 | 318 | /** 319 | * this method called when loading data from database. 320 | * override this for custom load routine 321 | * @param vertex vertex object. 322 | */ 323 | def __load__(vertex:Vertex){ 324 | this.vertex = vertex 325 | } 326 | 327 | /** 328 | * this method called before saving into database, 329 | * override this for custom kv properties. 330 | * all return from this method will be saved. 331 | * by default this is just return empty map. 332 | * @return Map[String, Any] 333 | */ 334 | def __save__(vertex:Vertex){} 335 | 336 | private[graph] def setVertex(v:Vertex){ 337 | this.vertex = v 338 | } 339 | 340 | /** 341 | * get bounded vertex. 342 | * throw [[com.ansvia.graph.Exc.NotBoundException]] if object not saved 343 | * see [[com.ansvia.graph.BlueprintsWrapper.DbObject#isSaved]] for checking is object saved or not. 344 | * @return 345 | */ 346 | def getVertex:Vertex = { 347 | if (vertex == null) 348 | throw NotBoundException("object %s not bound to existing vertex, unsaved vertex?".format(this)) 349 | vertex 350 | } 351 | 352 | /** 353 | * Check is object saved. 354 | * @return 355 | */ 356 | def isSaved = vertex != null 357 | 358 | /** 359 | * Create edge label. 360 | * @param label edge label. 361 | * @return 362 | */ 363 | def -->(label:String)(implicit db:Graph):EdgeWrapper = { 364 | vertex --> label 365 | } 366 | 367 | /** 368 | * Reload object from db. 369 | * @param db implicit Graph db object. 370 | * @return this object with updated vertex. 371 | */ 372 | def reload()(implicit db:Graph):this.type = { 373 | if (!isSaved) 374 | throw NotBoundException("object %s not saved yet".format(this)) 375 | 376 | val id = 377 | if (isSaved){ 378 | vertex match { 379 | case iv:IdVertex => 380 | 381 | db match { 382 | case ig:IdGraph[_] => 383 | iv.getId 384 | case _ => 385 | iv.getBaseVertex.getId 386 | } 387 | 388 | case _ => 389 | vertex.getId 390 | } 391 | }else 392 | vertex.getId 393 | 394 | val v = db.getVertex(id) 395 | 396 | if (v == null) 397 | throw NotBoundException("object %s not bound to any vertex".format(this)) 398 | 399 | this.vertex = v 400 | 401 | v.toCC[this.type](defaultClassloader).get 402 | } 403 | 404 | } 405 | 406 | trait IDGetter[IDType] extends AbstractIDGetter[IDType] { 407 | def isSaved:Boolean 408 | def getVertex:Vertex 409 | 410 | def getId:IDType = { 411 | if (!isSaved) 412 | throw NotBoundException("object %s not saved yet".format(this)) 413 | getVertex.getId.asInstanceOf[IDType] 414 | } 415 | } 416 | 417 | trait IdDbObject[IDType] extends DbObject with IDGetter[IDType] { 418 | 419 | type idType = IDType 420 | protected var id:IDType = _ 421 | private val _nullId:IDType = id 422 | 423 | /** 424 | * this method called when loading data from database. 425 | * override this for custom load routine 426 | * @param vertex vertex object. 427 | */ 428 | override def __load__(vertex: Vertex) { 429 | super.__load__(vertex) 430 | id = vertex.getId.asInstanceOf[IDType] 431 | } 432 | 433 | def isSaved:Boolean 434 | def getVertex:Vertex 435 | 436 | override def getId:IDType = { 437 | if (id != _nullId){ 438 | id 439 | }else{ 440 | if (!isSaved) 441 | throw NotBoundException("object %s not saved yet".format(this)) 442 | id = getVertex.getId.asInstanceOf[IDType] 443 | id 444 | } 445 | } 446 | 447 | /** 448 | * Save this object to database. 449 | */ 450 | override def save()(implicit db: Graph): Vertex = { 451 | val v = super.save()(db) 452 | id = v.getId.asInstanceOf[IDType] 453 | v 454 | } 455 | 456 | private[graph] def setId(id:IDType){ 457 | this.id = id 458 | } 459 | 460 | 461 | /** 462 | * Reload object from db. 463 | * @param db implicit Graph db object. 464 | * @return this object with updated vertex. 465 | */ 466 | override def reload()(implicit db: Graph) = { 467 | if (id == _nullId && isSaved){ 468 | vertex match { 469 | case iv:IdVertex => 470 | 471 | db match { 472 | case ig:IdGraph[_] => 473 | id = iv.getId.asInstanceOf[IDType] 474 | case _ => 475 | id = iv.getBaseVertex.getId.asInstanceOf[IDType] 476 | } 477 | 478 | case _ => 479 | id = vertex.getId.asInstanceOf[IDType] 480 | } 481 | } 482 | 483 | if (id != _nullId){ 484 | vertex = db.getVertex(id) 485 | 486 | if (vertex == null) 487 | throw NotBoundException("object %s not bound to any vertex".format(this)) 488 | 489 | }else{ 490 | throw NotBoundException("id return null, object %s not saved yet?".format(this)) 491 | } 492 | vertex.toCC[this.type](defaultClassloader).get 493 | } 494 | } 495 | 496 | } 497 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/DbWrapper.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import com.tinkerpop.blueprints.{Graph, Vertex} 4 | import com.ansvia.graph.BlueprintsWrapper.DbObject 5 | 6 | /** 7 | * Author: robin 8 | * Date: 9/1/14 9 | * Time: 12:47 PM 10 | * 11 | */ 12 | class DbWrapper(db:Graph) { 13 | 14 | def save[T:Manifest](cc:T):Vertex = { 15 | val (o, _new) = { 16 | cc match { 17 | case dbo:DbObject if dbo.isSaved => 18 | (db.getVertex(dbo.getVertex.getId), false) 19 | // case dbo:DbObject if !dbo.isSaved => 20 | // (db.addVertex(null), true) 21 | case _ => 22 | (db.addVertex(null), true) 23 | } 24 | } 25 | 26 | val elm:Vertex = ObjectConverter.serialize(cc.asInstanceOf[AnyRef], o, _new) 27 | 28 | cc match { 29 | case ccDbo:DbObject => 30 | ccDbo.setVertex(elm) 31 | ccDbo.__save__(elm) 32 | // val kv = ccDbo.__save__() 33 | // for ( (k, v) <- kv ){ 34 | // 35 | // // only set if different/new 36 | // if(elm.getOrElse(k,null) != v) 37 | // elm.set(k, v) 38 | // 39 | // } 40 | case _ => 41 | } 42 | elm 43 | } 44 | 45 | def delete[T:Manifest](cc:T):Unit = { 46 | cc match { 47 | case dbo:DbObject if dbo.isSaved => 48 | db.removeVertex(dbo.getVertex) 49 | case _ => 50 | } 51 | } 52 | } 53 | 54 | object StdDbWrapper { 55 | implicit def dbWrapper(db:Graph) = new DbWrapper(db) 56 | } 57 | 58 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/EdgeWrapper.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import com.tinkerpop.blueprints.{Edge, Graph, Vertex} 4 | import com.ansvia.graph.BlueprintsWrapper.VertexWrapper 5 | 6 | 7 | /** 8 | * Edge wrapper to support arrow operator. 9 | * @param vertex vertex 10 | * @param label label 11 | * @param db database 12 | */ 13 | private[graph] case class EdgeWrapper(var vertex:Vertex, var label:String, db:Graph) extends Wrapper { 14 | private var lastEdge:Edge = null 15 | var prev:Option[VertexWrapper] = None 16 | 17 | def -->(inV:Vertex):VertexWrapper = { 18 | lastEdge = db.addEdge(null, vertex, inV, label) 19 | 20 | // for performance reason 21 | // we using previous object if any 22 | 23 | val p = prev.getOrElse { 24 | VertexWrapper(inV, label, db) 25 | } 26 | 27 | p.prev = Some(this) 28 | p.vertex = inV 29 | p 30 | } 31 | 32 | def <--(outV:Vertex):VertexWrapper = { 33 | lastEdge = db.addEdge(null, outV, vertex, label) 34 | 35 | // for performance reason 36 | // we using previous object if any 37 | 38 | val p = prev.getOrElse { 39 | VertexWrapper(outV, label, db) 40 | } 41 | p.prev = Some(this) 42 | p.vertex = outV 43 | p 44 | } 45 | 46 | def -->(o:AbstractDbObject):VertexWrapper = { 47 | this --> o.getVertex 48 | } 49 | 50 | def <():Edge = this.lastEdge 51 | } 52 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/Exc.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | /** 4 | * Copyright (C) 2011-2013 Ansvia Inc. 5 | * Author: robin 6 | * Date: 1/7/13 7 | * 8 | */ 9 | object Exc { 10 | class BlueprintsScalaException(msg:String) extends Exception(msg) 11 | case class NotBoundException(msg:String) extends BlueprintsScalaException(msg) 12 | } 13 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/IDGetter.scala: -------------------------------------------------------------------------------- 1 | 2 | package com.ansvia.graph 3 | 4 | import com.tinkerpop.blueprints._ 5 | import com.ansvia.graph.Exc.{BlueprintsScalaException, NotBoundException} 6 | 7 | 8 | trait IDGetter[IDType] { 9 | def isSaved:Boolean 10 | def getVertex:Vertex 11 | 12 | def getId:IDType = { 13 | if (!isSaved) 14 | throw NotBoundException("object %s not saved yet".format(this)) 15 | getVertex.getId.asInstanceOf[IDType] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/Log.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import org.slf4j.LoggerFactory 4 | 5 | /** 6 | * Author: robin 7 | * Date: 2/24/14 8 | * Time: 11:27 PM 9 | * 10 | */ 11 | private[graph] trait Log { 12 | 13 | final lazy val log = LoggerFactory.getLogger(getClass) 14 | 15 | def error(msg:String){ 16 | log.error(msg) 17 | } 18 | 19 | def info(msg:String){ 20 | log.info(msg) 21 | } 22 | 23 | def warn(msg:String){ 24 | log.warn(msg) 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/ObjectConverter.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | /** 4 | * Copyright (C) 2011-2012 Ansvia Inc. 5 | * User: robin 6 | * Date: 12/31/12 7 | * Time: 5:21 AM 8 | * 9 | */ 10 | 11 | import collection.JavaConversions._ 12 | import com.tinkerpop.blueprints.{Vertex, Element} 13 | import com.ansvia.graph.util.{CallersContext, CaseClassDeserializer} 14 | import com.ansvia.graph.BlueprintsWrapper.DbObject 15 | import reflect.ClassTag 16 | import scala.collection.mutable 17 | import com.ansvia.graph.Exc.BlueprintsScalaException 18 | 19 | object ObjectConverter extends Log { 20 | 21 | /** 22 | * this name will be used to store the class name of 23 | * the serialized case class that will be verified 24 | * in deserialization 25 | */ 26 | var CLASS_PROPERTY_NAME = "_class_" 27 | 28 | val defaultClassloader = CallersContext.fetchDefaultClassLoader 29 | 30 | /** 31 | * serializes a given case class into a Node instance 32 | * for null values not property will be set 33 | */ 34 | def serialize[T <: Element](cc: AnyRef, pc: Element, newElement:Boolean): T = { 35 | assert(cc != null, "duno how to serialize null object :(") 36 | if (newElement){ 37 | val clz = pc.getProperty[String](CLASS_PROPERTY_NAME) 38 | if (clz != null) 39 | throw new BlueprintsScalaException("element `" + pc + "` treated as new but already has meta class `" + 40 | clz + "` requested to set `" + cc.getClass.getName + "`, we raised this error to prevent data overwrite") 41 | pc.setProperty(CLASS_PROPERTY_NAME, cc.getClass.getName) 42 | } 43 | 44 | CaseClassDeserializer.serialize(cc).foreach { 45 | case (name, null) => 46 | case (name, value) => 47 | try { 48 | assignValue(pc, name, value) 49 | } catch{ 50 | case e:IllegalArgumentException => 51 | error("cannot set property %s <= %s\nerror: %s".format(name, value, e.getMessage)) 52 | throw e 53 | } 54 | 55 | } 56 | pc.setProperty(CLASS_PROPERTY_NAME, cc.getClass.getName) 57 | 58 | // save non case class accessor 59 | 60 | pc.asInstanceOf[T] 61 | } 62 | 63 | /** 64 | * conditional case class deserialization 65 | * Some(T) if possible 66 | * None if not 67 | */ 68 | def toCC[T: ClassTag](pc: Element, classLoader: ClassLoader = defaultClassloader): Option[T] = 69 | _toCCPossible[T](pc, classLoader) match { 70 | case Some(serializedClass) => 71 | 72 | var kv:mutable.Set[(String, AnyRef)] = null 73 | try { 74 | kv = for (k <- pc.getPropertyKeys; v = pc.getProperty[AnyRef](k)) yield k -> v 75 | 76 | val o = CaseClassDeserializer.deserialize[T](serializedClass, kv.toMap) 77 | 78 | o match { 79 | case dbObject: DbObject => 80 | dbObject.__load__(pc.asInstanceOf[Vertex]) 81 | case _ => 82 | } 83 | 84 | Some(o) 85 | }catch{ 86 | case e:IllegalArgumentException => 87 | error("Cannot deserialize record from db, broken record? \n" + 88 | "for class: " + serializedClass.getName + "\n" + 89 | { 90 | if (kv != null) 91 | "kv: " + kv.toMap + "\n" 92 | else 93 | "" 94 | } + 95 | "error: " + e.getMessage) 96 | e.printStackTrace() 97 | None 98 | case e:IndexOutOfBoundsException => 99 | error("Cannot deserialize record from db, broken record? \n" + 100 | "for class: " + serializedClass.getName + "\n" + 101 | { 102 | if (kv != null) 103 | "kv: " + kv.toMap + "\n" 104 | else 105 | "" 106 | } + 107 | "error: " + e.getMessage) 108 | e.printStackTrace() 109 | None 110 | } 111 | 112 | case _ => None 113 | } 114 | 115 | private def assignValue(pc: Element, attributeName: String, value: Any) { 116 | value match { 117 | case Some(x) => 118 | assignValue(pc, attributeName, x) 119 | case None => 120 | pc.removeProperty(attributeName) 121 | () // forced Unit 122 | case _ => 123 | if(pc.getProperty(attributeName) != value) { 124 | pc.setProperty(attributeName, value) 125 | } 126 | } 127 | } 128 | 129 | private def _toCCPossible[T](pc: Element, classLoader: ClassLoader)(implicit tag: ClassTag[T]): Option[Class[_]] = { 130 | val pv = pc.getProperty[String](CLASS_PROPERTY_NAME) 131 | if( pv != null ){ 132 | val cpn = pv.toString 133 | val c = Class.forName(cpn, true, classLoader) 134 | if (tag.runtimeClass.isAssignableFrom(c)) 135 | Some(c) 136 | else 137 | None 138 | } else 139 | None 140 | 141 | 142 | } 143 | 144 | /** 145 | * only checks if this property container has been serialized 146 | * with T 147 | */ 148 | def toCCPossible[T: ClassTag](pc: Element, classLoader: ClassLoader = defaultClassloader): Boolean = 149 | _toCCPossible[T](pc, classLoader) match { 150 | case Some(_) => true 151 | case _ => false 152 | } 153 | 154 | /** 155 | * deserializes a given case class type from a given Node instance 156 | * throws a IllegalArgumentException if a Nodes properties 157 | * do not fit to the case class properties 158 | */ 159 | def deSerialize[T](pc: Element)(implicit tag: ClassTag[T]): T = { 160 | assert(pc != null, "duno how to deserialize null object :(") 161 | toCC[T](pc) match { 162 | case Some(t) => t 163 | case _ => throw new IllegalArgumentException("given Case Class: " + 164 | tag.runtimeClass.getName + " does not fit to serialized properties") 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/TitanDbWrapper.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import com.ansvia.graph.BlueprintsWrapper.{DbObject, IdDbObject} 4 | import com.thinkaurelius.titan.core.schema.EdgeLabelMaker 5 | import com.thinkaurelius.titan.core.{TitanGraph, TitanTransaction, VertexLabel} 6 | import com.thinkaurelius.titan.graphdb.types.StandardEdgeLabelMaker 7 | import com.tinkerpop.blueprints.util.wrappers.id.IdGraph.IdFactory 8 | import com.tinkerpop.blueprints.util.wrappers.id.{IdGraph, IdVertex} 9 | import com.tinkerpop.blueprints.{KeyIndexableGraph, Vertex} 10 | 11 | import scala.language.reflectiveCalls 12 | 13 | 14 | /** 15 | * Author: robin 16 | * Date: 9/1/14 17 | * Time: 12:46 PM 18 | * 19 | */ 20 | 21 | object TitanDbWrapper extends Helpers { 22 | 23 | 24 | class TitanDbWrapper(db:TitanGraph) extends DbWrapper(db){ 25 | def saveWithLabel[T: Manifest](cc:T, label:VertexLabel):Vertex = { 26 | val (v, _new) = { 27 | cc match { 28 | case dbo:DbObject if dbo.isSaved => 29 | (db.getVertex(dbo.getVertex.getId), false) 30 | case _ => 31 | (db.addVertex(label), true) 32 | } 33 | } 34 | 35 | val elm:Vertex = ObjectConverter.serialize(cc.asInstanceOf[AnyRef], v, _new) 36 | 37 | cc match { 38 | case ccDbo:DbObject => 39 | ccDbo.setVertex(elm) 40 | ccDbo.__save__(elm) 41 | case _ => 42 | } 43 | elm 44 | } 45 | 46 | def saveWithLabel[T: Manifest](cc:T, label:String):Vertex = { 47 | val lbl = db.getVertexLabel(label) 48 | require(lbl != null, "Unknown vertex label: " + label) 49 | saveWithLabel(cc, lbl) 50 | } 51 | 52 | def transact[T](f: (TitanTransaction) => T):T = { 53 | val trx = db.newTransaction() 54 | try { 55 | val rv = f(trx) 56 | trx.commit() 57 | rv 58 | }catch{ 59 | case e:IllegalArgumentException => 60 | throw e 61 | // case e:Exception => 62 | // e.printStackTrace() 63 | // try { 64 | // trx.rollback() 65 | // }catch{ 66 | // case e:Throwable => 67 | // } 68 | // throw e 69 | } 70 | } 71 | 72 | def transactIdGraph[T](idFactory:IdFactory)(f: (IdGraph[TitanTransaction]) => T):T = { 73 | val trx = new IdGraph(db.newTransaction(), true, false) 74 | trx.setVertexIdFactory(idFactory) 75 | try { 76 | val rv = f(trx) 77 | trx.commit() 78 | rv 79 | }catch{ 80 | case e:IllegalArgumentException => 81 | throw e 82 | // case e:Exception => 83 | // e.printStackTrace() 84 | // try { 85 | // trx.rollback() 86 | // }catch{ 87 | // case e:Throwable => 88 | // } 89 | // throw e 90 | } 91 | } 92 | 93 | } 94 | 95 | 96 | class TitanDbObjectWrapper(dbo:DbObject){ 97 | 98 | implicit private def transactionWrapper(trx:TitanTransaction) = new TitanTransactionDbWrapper(trx) 99 | 100 | 101 | def saveWithLabelTx(label:VertexLabel)(implicit db:TitanTransaction):Vertex = { 102 | db.saveWithLabel(dbo, label) 103 | // dbo.setVertex(v) 104 | // v 105 | } 106 | 107 | def saveWithLabelTx(label:String)(implicit db:TitanTransaction):Vertex = { 108 | val lbl = db.getVertexLabel(label) 109 | assert(lbl != null, "unknown vertex label: " + label) 110 | saveWithLabelTx(lbl) 111 | } 112 | 113 | def saveWithLabel(label:VertexLabel)(implicit db:TitanGraph):Vertex = { 114 | val v = db.saveWithLabel(dbo, label) 115 | // dbo.setVertex(v) 116 | 117 | 118 | dbo match { 119 | case iddbo:IdDbObject[_] => 120 | iddbo.setId(v.getId.asInstanceOf[iddbo.idType]) 121 | case _ => 122 | } 123 | 124 | v 125 | } 126 | 127 | def saveWithLabel(label:String)(implicit db:TitanGraph):Vertex = { 128 | val lbl = db.getVertexLabel(label) 129 | assert(lbl != null, "unknown vertex label: " + label) 130 | saveWithLabel(lbl) 131 | } 132 | 133 | 134 | } 135 | 136 | implicit def dbWrapper(db:TitanGraph):TitanDbWrapper = new TitanDbWrapper(db) 137 | implicit def titanDbObjectWrapper(dbo:DbObject):TitanDbObjectWrapper = 138 | new TitanDbObjectWrapper(dbo) 139 | 140 | } 141 | 142 | 143 | 144 | object IdGraphTitanDbWrapper extends Helpers { 145 | 146 | import TitanDbWrapper._ 147 | 148 | 149 | 150 | protected class _IdVertex(v:Vertex, db:IdGraph[_ <: KeyIndexableGraph]) 151 | extends com.tinkerpop.blueprints.util.wrappers.id.IdVertex(v, db) 152 | 153 | 154 | implicit def idTitanDbWrapper(db:IdGraph[TitanGraph]) = new TitanDbWrapper(db.getBaseGraph){ 155 | 156 | 157 | override def saveWithLabel[T: Manifest](cc: T, label: VertexLabel):IdVertex = { 158 | val (v, _new) = { 159 | cc match { 160 | case dbo:DbObject if dbo.isSaved => 161 | (db.getBaseGraph.getVertex(dbo.getVertex.getId), false) 162 | case _ => 163 | (db.getBaseGraph.addVertex(label), true) 164 | } 165 | } 166 | 167 | val elm:Vertex = ObjectConverter.serialize(cc.asInstanceOf[AnyRef], v, _new) 168 | val idV = new _IdVertex(elm, db) 169 | 170 | cc match { 171 | case ccDbo:DbObject => 172 | ccDbo.setVertex(idV) 173 | ccDbo.__save__(idV) 174 | case _ => 175 | } 176 | // elm 177 | idV 178 | } 179 | 180 | def addVertexWithLabel(label:VertexLabel):Vertex = { 181 | val rawV = db.getBaseGraph.addVertexWithLabel(label) 182 | 183 | val id = db.getVertexIdFactory.createId() 184 | 185 | rawV.setProperty(IdGraph.ID, id) 186 | 187 | new _IdVertex(rawV, db) 188 | } 189 | 190 | def addVertexWithLabel(label:String):Vertex = { 191 | val lbl = db.getBaseGraph.getVertexLabel(label) 192 | assert(lbl != null, "unknown vertex label: " + label) 193 | addVertexWithLabel(lbl) 194 | } 195 | 196 | } 197 | 198 | 199 | implicit def titanTransactionWrapper(trx:IdGraph[TitanTransaction]) = new TitanTransactionDbWrapper(trx.getBaseGraph){ 200 | 201 | // protected class IdVertex(v:Vertex, db:IdGraph[TitanTransaction]) 202 | // extends com.tinkerpop.blueprints.util.wrappers.id.IdVertex(v, db) 203 | // 204 | 205 | override def saveWithLabel[T: Manifest](cc: T, label: VertexLabel):_IdVertex = { 206 | val (v, _new) = { 207 | cc match { 208 | case dbo:DbObject if dbo.isSaved => 209 | (trx.getBaseGraph.getVertex(dbo.getVertex.getId), false) 210 | case _ => 211 | (trx.getBaseGraph.addVertex(label), true) 212 | } 213 | } 214 | 215 | val elm:Vertex = ObjectConverter.serialize(cc.asInstanceOf[AnyRef], v, _new) 216 | 217 | val idV = new _IdVertex(elm, trx) 218 | 219 | cc match { 220 | case ccDbo:DbObject => 221 | ccDbo.setVertex(idV) 222 | ccDbo.__save__(idV) 223 | case _ => 224 | } 225 | // elm 226 | 227 | idV 228 | } 229 | 230 | def addVertexWithLabel(label:VertexLabel):_IdVertex = { 231 | 232 | val id = trx.getVertexIdFactory.createId() 233 | 234 | val rawV = trx.getBaseGraph.addVertexWithLabel(label) 235 | 236 | rawV.setProperty(IdGraph.ID, id) 237 | 238 | new _IdVertex(rawV, trx) 239 | } 240 | 241 | def addVertexWithLabel(label:String):Vertex = { 242 | val lbl = trx.getBaseGraph.getVertexLabel(label) 243 | assert(lbl != null, "unknown vertex label: " + label) 244 | addVertexWithLabel(lbl) 245 | } 246 | 247 | } 248 | 249 | 250 | class IdGraphTitanDbObjectWrapper(dbo:DbObject){ 251 | 252 | 253 | import com.tinkerpop.blueprints.util.wrappers.id.IdGraph 254 | // 255 | // protected class IdVertex(v:Vertex, db:IdGraph[TitanGraph]) 256 | // extends com.tinkerpop.blueprints.util.wrappers.id.IdVertex(v, db) 257 | 258 | 259 | def saveWithLabel(label:String)(implicit db:IdGraph[TitanGraph]):IdVertex = { 260 | val lbl = db.getBaseGraph.getVertexLabel(label) 261 | 262 | assert(lbl != null, "unknown vertex label: " + label) 263 | 264 | saveWithLabel(lbl) 265 | } 266 | 267 | 268 | def saveWithLabel(label:VertexLabel)(implicit db:IdGraph[TitanGraph]):IdVertex = { 269 | val v:IdVertex = db.saveWithLabel(dbo, label) 270 | 271 | val id = db.getVertexIdFactory.createId() 272 | 273 | v.getBaseVertex.setProperty(IdGraph.ID, id) 274 | 275 | dbo match { 276 | case iddbo:IdDbObject[_] => 277 | iddbo.setId(id.asInstanceOf[iddbo.idType]) 278 | case _ => 279 | } 280 | 281 | v 282 | } 283 | 284 | def saveWithLabelTx(label:VertexLabel, trx:TitanTransaction)(implicit db:IdGraph[TitanGraph]):_IdVertex = { 285 | val idTrx = new IdGraph(trx, true, false) 286 | // val trxw = new TitanTransactionDbWrapper(trx) 287 | 288 | val v:_IdVertex = titanTransactionWrapper(idTrx).saveWithLabel(dbo, label) 289 | 290 | val id = db.getVertexIdFactory.createId() 291 | 292 | v.getBaseVertex.setProperty(IdGraph.ID, id) 293 | 294 | // val rv = new _IdVertex(v, db) 295 | 296 | // dbo.setVertex(rv) 297 | 298 | dbo match { 299 | case iddbo:IdDbObject[_] => 300 | iddbo.setId(id.asInstanceOf[iddbo.idType]) 301 | case _ => 302 | } 303 | 304 | // rv 305 | v 306 | } 307 | 308 | def saveWithLabelTx(label:String, trx:TitanTransaction)(implicit db:IdGraph[TitanGraph]):_IdVertex = { 309 | val lbl = trx.getVertexLabel(label) 310 | assert(lbl != null, "unknown vertex label: " + label) 311 | saveWithLabelTx(lbl, trx) 312 | } 313 | 314 | 315 | } 316 | 317 | implicit def idGraphTitanDbObjectWrapper(dbo:DbObject):IdGraphTitanDbObjectWrapper = 318 | new IdGraphTitanDbObjectWrapper(dbo) 319 | 320 | } 321 | 322 | 323 | private[graph] trait Helpers { 324 | 325 | 326 | class TitanTransactionDbWrapper(trx:TitanTransaction) extends DbWrapper(trx){ 327 | 328 | def saveWithLabel[T: Manifest](cc:T, label:VertexLabel):Vertex = { 329 | val (v, _new) = { 330 | cc match { 331 | case dbo:DbObject if dbo.isSaved => 332 | (trx.getVertex(dbo.getVertex.getId), false) 333 | case _ => 334 | (trx.addVertex(label), true) 335 | } 336 | } 337 | 338 | val elm:Vertex = ObjectConverter.serialize(cc.asInstanceOf[AnyRef], v, _new) 339 | 340 | cc match { 341 | case ccDbo:DbObject => 342 | ccDbo.setVertex(elm) 343 | ccDbo.__save__(elm) 344 | case _ => 345 | } 346 | elm 347 | } 348 | 349 | } 350 | 351 | 352 | implicit def edgeLabelMakerWrapper(elm:EdgeLabelMaker) = elm.asInstanceOf[StandardEdgeLabelMaker] 353 | implicit def titanTransactionWrapper(trx:TitanTransaction) = new TitanTransactionDbWrapper(trx) 354 | 355 | 356 | } 357 | 358 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/Wrapper.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | /** 4 | * Author: robin 5 | * Date: 4/15/13 6 | * Time: 1:31 AM 7 | * 8 | */ 9 | private[graph] trait Wrapper 10 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/gremlin.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import com.tinkerpop.pipes.PipeFunction 4 | import com.tinkerpop.pipes.util.structures.{Pair => BPPair} 5 | import com.tinkerpop.blueprints.{Edge, Element, Vertex} 6 | 7 | package object gremlin { 8 | 9 | // import scala.language.implicitConversions 10 | 11 | implicit def tupleToPair[A,B](pair:(A, B)) = new BPPair[A,B](pair._1, pair._2) 12 | 13 | implicit def gremlinPipeFuncWrapper[A,B](func:(A) => B) = { 14 | new PipeFunction[A,B]{ 15 | def compute(v:A):B = { 16 | func.apply(v) 17 | } 18 | } 19 | } 20 | 21 | private def gremlinPipeFilterFuncWrapperT[T <: Element](func:(T) => Boolean) = { 22 | new PipeFunction[T,java.lang.Boolean]{ 23 | def compute(elm:T):java.lang.Boolean = { 24 | func.apply(elm) 25 | } 26 | } 27 | } 28 | 29 | implicit def gremlinPipeFilterFuncWrapperVertex = gremlinPipeFilterFuncWrapperT[Vertex] _ 30 | implicit def gremlinPipeFilterFuncWrapperEdge = gremlinPipeFilterFuncWrapperT[Edge] _ 31 | 32 | implicit def gremlinPipeOrderFuncWrapper[T <: Element](func:(T, T) => Int) = { 33 | new PipeFunction[BPPair[T, T], java.lang.Integer] { 34 | def compute(arg: BPPair[T, T]):java.lang.Integer = 35 | func.apply(arg.getA, arg.getB) 36 | } 37 | } 38 | 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/testing/model/Animal.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.testing.model 2 | 3 | import com.tinkerpop.blueprints.Vertex 4 | import com.ansvia.graph.BlueprintsWrapper._ 5 | import com.ansvia.graph.annotation.Persistent 6 | 7 | 8 | /** 9 | * Author: robin 10 | * Date: 1/14/13 11 | * Time: 9:07 PM 12 | * 13 | */ 14 | private[graph] case class Animal(name:String) extends DbObject { 15 | 16 | @Persistent var age:Int = 0 17 | @Persistent var kind:String = "" 18 | 19 | /** 20 | * override this for custom load routine 21 | * @param vertex vertex object. 22 | */ 23 | override def __load__(vertex: Vertex) { 24 | super.__load__(vertex) 25 | age = vertex.getOrElse[Int]("age", 0) 26 | kind = vertex.getOrElse[String]("kind", "") 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/testing/model/ColoredFish.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.testing.model 2 | 3 | /** 4 | * 5 | * User: tysonjh 6 | * Date: 13/02/13 7 | * 8 | */ 9 | abstract class ColoredFish(color: String) extends Fish -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/testing/model/Complex.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.testing.model 2 | 3 | import com.ansvia.graph.annotation.Persistent 4 | import com.ansvia.graph.BlueprintsWrapper.DbObject 5 | import com.tinkerpop.blueprints.Vertex 6 | 7 | /** 8 | * Author: robin 9 | * Date: 1/16/13 10 | * Time: 12:15 AM 11 | * 12 | */ 13 | 14 | private[graph] trait B { 15 | @Persistent var b:Int = 0 16 | } 17 | 18 | private[graph] trait C { 19 | @Persistent var c:Int = 0 20 | } 21 | 22 | private[graph] abstract class A extends DbObject with B with C { 23 | @Persistent var a:Int = 0 24 | } 25 | 26 | private[graph] case class Complex(x:String) extends A { 27 | // import com.ansvia.graph.BlueprintsWrapper._ 28 | 29 | @Persistent var me = "" 30 | 31 | /** 32 | * this method called when loading data from database. 33 | * override this for custom load routine 34 | * @param vertex vertex object. 35 | */ 36 | override def __load__(vertex: Vertex) { 37 | super.__load__(vertex) 38 | // me = vertex.getOrElse("me", "") 39 | // a = vertex.getOrElse("a", 0) 40 | // b = vertex.getOrElse("b", 0) 41 | // c = vertex.getOrElse("c", 0) 42 | } 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/testing/model/ContainLazy.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.testing.model 2 | 3 | import com.ansvia.graph.BlueprintsWrapper._ 4 | 5 | 6 | /** 7 | * Author: robin 8 | * Date: 1/14/13 9 | * Time: 9:07 PM 10 | * 11 | */ 12 | private[graph] case class ContainLazy(test:Long) extends DbObject { 13 | lazy val x = { 14 | 2 15 | } 16 | val z = 3 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/testing/model/Eatable.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.testing.model 2 | 3 | import com.ansvia.graph.annotation.Persistent 4 | 5 | 6 | /** 7 | * Author: robin 8 | * Date: 1/14/13 9 | * Time: 9:07 PM 10 | * 11 | */ 12 | private[graph] trait Eatable { 13 | @Persistent var eatable:Boolean = true 14 | } 15 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/testing/model/Fish.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.testing.model 2 | 3 | import com.tinkerpop.blueprints.Vertex 4 | import com.ansvia.graph.BlueprintsWrapper._ 5 | import com.ansvia.graph.annotation.Persistent 6 | 7 | 8 | /** 9 | * Author: robin 10 | * Date: 1/14/13 11 | * Time: 9:07 PM 12 | * 13 | */ 14 | private[graph] abstract class Fish extends DbObject { 15 | @Persistent var name:String = "" 16 | 17 | // /** 18 | // * override this for custom load routine 19 | // * @param vertex vertex object. 20 | // */ 21 | // override def __load__(vertex: Vertex) { 22 | // super.__load__(vertex) 23 | // name = vertex.getOrElse("name", "") 24 | // } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/testing/model/ModelUsingAbstractIDGetter.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.testing.model 2 | 3 | import com.ansvia.graph.AbstractIDGetter 4 | import com.ansvia.graph.BlueprintsWrapper.DbObject 5 | 6 | /** 7 | * Author: robin 8 | * Date: 11/4/13 9 | * Time: 1:23 PM 10 | * 11 | */ 12 | private[graph] case class ModelUsingAbstractIDGetter(name:String) extends DbObject with AbstractIDGetter[String] { 13 | def getId = "xxx" 14 | } 15 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/testing/model/Motor.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.testing.model 2 | 3 | import com.ansvia.graph.BlueprintsWrapper._ 4 | 5 | 6 | /** 7 | * Author: robin 8 | * Date: 1/14/13 9 | * Time: 9:07 PM 10 | * 11 | */ 12 | private[graph] case class Motor(mark:String) extends DbObject 13 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/testing/model/SeaFish.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.testing.model 2 | 3 | 4 | /** 5 | * Author: robin 6 | * Date: 1/14/13 7 | * Time: 9:07 PM 8 | * 9 | */ 10 | private[graph] case class SeaFish(color: String) extends ColoredFish(color) 11 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/testing/model/Shark.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.testing.model 2 | 3 | import com.tinkerpop.blueprints.Vertex 4 | import com.ansvia.graph.BlueprintsWrapper._ 5 | import com.ansvia.graph.annotation.Persistent 6 | 7 | 8 | /** 9 | * Author: robin 10 | * Date: 1/14/13 11 | * Time: 9:07 PM 12 | * 13 | */ 14 | private[graph] case class Shark(kind:String, color: String = "blue") extends ColoredFish(color) with Eatable { 15 | 16 | @Persistent var lives:String = "" 17 | @Persistent var hungry:Boolean = false 18 | 19 | var children:Int = 0 20 | var animalProtected = false 21 | 22 | /** 23 | * override this for custom load routine 24 | * @param vertex vertex object. 25 | */ 26 | override def __load__(vertex: Vertex) { 27 | super.__load__(vertex) 28 | // lives = vertex.getOrElse("lives", "") 29 | eatable = vertex.getOrElse("eatable", true) 30 | children = vertex.getOrElse("children", 0) 31 | animalProtected = vertex.getOrElse("protected", false) 32 | } 33 | 34 | /** 35 | * this method called before saving into database, 36 | * override this for custom kv properties. 37 | * all return from this method will be saved. 38 | * by default this is just return empty map. 39 | * @return 40 | */ 41 | override def __save__(v:Vertex) = { 42 | v.setProperty("protected", animalProtected) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/testing/model/SimpleDbo.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.testing.model 2 | 3 | import com.ansvia.graph.BlueprintsWrapper.{IdDbObject, DbObject} 4 | import com.ansvia.graph.IDGetter 5 | 6 | /** 7 | * Author: robin 8 | * Date: 2/17/13 9 | * Time: 9:54 PM 10 | * 11 | * For test purpose. 12 | */ 13 | private[graph] case class SimpleDbo(a:String,var b:String) extends DbObject with IDGetter[String] 14 | private[graph] case class SimpleDboLong(a:String,var b:String) extends DbObject with IDGetter[Long] 15 | private[graph] case class IdSimpleDbo(a:String,var b:String) extends IdDbObject[String] 16 | private[graph] case class IdSimpleDboLong(a:String,var b:String) extends IdDbObject[Long] 17 | private[graph] case class IdSimpleDboOption(opt: Option[String] = None, a:String,var b:String) extends IdDbObject[Long] 18 | private[graph] case class IdSimpleDboVarOption(var opt: Option[String] = None, a:String,var b:String) extends IdDbObject[Long] 19 | 20 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/testing/model/User.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.testing.model 2 | 3 | 4 | /** 5 | * Author: robin 6 | * Date: 1/14/13 7 | * Time: 8:52 PM 8 | * 9 | * used for testing only. 10 | * 11 | */ 12 | private[graph] case class User(name:String, age:Int) 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/util/CallersContext.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.util 2 | 3 | /** 4 | * Utility object used for fetching callers classLoader 5 | */ 6 | object CallersContext { 7 | 8 | def fetchDefaultClassLoader = { 9 | var context: Array[Class[_]] = null 10 | new SecurityManager { 11 | context = getClassContext() 12 | } 13 | 14 | if(context.length > 2) context(2).getClassLoader else null 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/util/Poso.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.util 2 | 3 | import java.lang 4 | import java.lang.reflect 5 | import java.util.concurrent.ConcurrentHashMap 6 | 7 | import com.ansvia.graph.BlueprintsWrapper.DbObject 8 | import com.ansvia.graph.Log 9 | import com.ansvia.graph.annotation.Persistent 10 | import com.ansvia.graph.util.scalax.rules.scalasig._ 11 | 12 | import scala.collection.mutable 13 | import scala.collection.mutable.ArrayBuffer 14 | import scala.language.existentials 15 | import scala.reflect.ClassTag 16 | 17 | import collection.JavaConversions._ 18 | 19 | /** 20 | * helper class to store Class object 21 | */ 22 | case class JavaType(c: Class[_]) 23 | 24 | /** 25 | * Case Class deserializing object 26 | */ 27 | object CaseClassDeserializer extends Log { 28 | 29 | /** 30 | * Method Map cache for method serialize 31 | */ 32 | private val methodCache = new ConcurrentHashMap[Class[_], Map[String, java.lang.reflect.Method]]() 33 | 34 | private val methodSetterCache = new ConcurrentHashMap[Class[_], Map[String, java.lang.reflect.Method]]() 35 | 36 | /** 37 | * signature parser cache 38 | */ 39 | private val sigParserCache = new ConcurrentHashMap[Class[_], Seq[(String, JavaType)]]() 40 | 41 | /** 42 | * default behaviour for T == serialized class 43 | */ 44 | def deserialize[T](m: Map[String, AnyRef])(implicit tag: ClassTag[T]): T = 45 | deserialize[T](tag.runtimeClass, m) 46 | 47 | /** 48 | * convenience method using class manifest 49 | * use it like val test = deserialize[Test](myMap) 50 | */ 51 | def deserialize[T](serializedClass: Class[_], m: Map[String, AnyRef])(implicit tag: ClassTag[T]): T = 52 | deserialize(m, JavaType(serializedClass)).asInstanceOf[T] 53 | 54 | /** 55 | * Creates a case class instance from parameter map 56 | * 57 | * @param m Map[String, AnyRef] map of parameter name an parameter type 58 | * @param javaTypeTarget JavaType case class class to create 59 | */ 60 | def deserialize(m: Map[String, AnyRef], javaTypeTarget: JavaType) = { 61 | require(javaTypeTarget.c.getConstructors.length == 1, "Case classes must only have one constructor.") 62 | 63 | val constructor = javaTypeTarget.c.getConstructors.head 64 | val params = sigParserCache.getOrElseUpdate(javaTypeTarget.c, CaseClassSigParser.parse(javaTypeTarget.c)) 65 | 66 | val values = buildValues(m, params) 67 | 68 | val paramsCount = constructor.getParameterTypes.length 69 | val ccParams = values.slice(0, paramsCount) 70 | 71 | val summoned = try { 72 | constructor.newInstance(ccParams.toArray: _*).asInstanceOf[AnyRef] 73 | }catch{ 74 | case e:IllegalArgumentException => 75 | error("cannot spawn object: " + e.getMessage + "\n" + 76 | "paramsCount: " + paramsCount + "\n" + 77 | "ccParams: " + ccParams.toList) 78 | throw e 79 | } 80 | 81 | val methods = { 82 | 83 | var symbols = Map.empty[String, reflect.Method] 84 | var curClazz:Class[_] = javaTypeTarget.c 85 | var done = false 86 | 87 | while(!done){ 88 | 89 | val rv: Map[String, reflect.Method] = 90 | methodSetterCache.getOrElseUpdate(curClazz, 91 | curClazz.getDeclaredMethods 92 | .filter{ z => 93 | z.getParameterTypes.length == 1 94 | }.map { 95 | m => m.getName -> m 96 | }.toMap) 97 | 98 | symbols ++= rv 99 | 100 | curClazz = curClazz.getSuperclass 101 | done = curClazz == classOf[Object] || curClazz == null 102 | } 103 | 104 | symbols 105 | } 106 | 107 | 108 | for ((paramName, paramType) <- params){ 109 | val field = m.getOrElse(paramName, null) 110 | 111 | // scala using _$eq suffix for setter method name 112 | val paramNameSet = paramName + "_$eq" 113 | 114 | field match { 115 | // option with value handled 116 | case x: AnyRef if paramType.c == classOf[Option[_]] => 117 | Some(x) 118 | // option with null handled 119 | case null if paramType.c == classOf[Option[_]] => 120 | None 121 | 122 | case null => 123 | // skip null 124 | 125 | case x:Integer if paramType.c == classOf[lang.Long] => 126 | 127 | methods.get(paramNameSet).map(_.invoke(summoned, x)) 128 | 129 | // if the value is directly assignable: use it 130 | case x: AnyRef if (x.getClass.isAssignableFrom(paramType.c)) => 131 | methods.get(paramNameSet).map(_.invoke(summoned, x)) 132 | case x: Array[_] => 133 | methods.get(paramNameSet).map(_.invoke(summoned, x)) 134 | // otherwise try to create an instance using der String Constructor 135 | case x: AnyRef => 136 | val paramCtor = paramType.c.getConstructor(classOf[String]) 137 | val value = paramCtor.newInstance(x).asInstanceOf[AnyRef] 138 | methods.get(paramNameSet).map(_.invoke(summoned, value)) 139 | } 140 | } 141 | 142 | summoned 143 | } 144 | 145 | def buildValues(vertexParams: Map[String, AnyRef], classParams: Seq[(String, JavaType)]): ArrayBuffer[AnyRef] = { 146 | val values = new ArrayBuffer[AnyRef] 147 | for ((paramName, paramType) <- classParams) { 148 | val field = vertexParams.getOrElse(paramName, null) 149 | 150 | if(paramType.c == classOf[Option[_]]) { 151 | values += handleOption(paramName, paramType, field) 152 | } else { 153 | values += handleRegularValue(paramType, field) 154 | } 155 | } 156 | values 157 | } 158 | 159 | def handleOption(paramName: String, paramType: JavaType, field: AnyRef): Option[AnyRef] = { 160 | if (field != null) { 161 | Some(field) 162 | } else { 163 | None 164 | } 165 | } 166 | 167 | def handleRegularValue(paramType: JavaType, field: AnyRef): AnyRef = { 168 | field match { 169 | // use null if the property does not exist 170 | case null => 171 | if (paramType.c == classOf[Option[_]]) { 172 | None 173 | } else { 174 | null 175 | } 176 | // if there is Long in case class and Integer in graph 177 | case x: Integer if paramType.c == classOf[lang.Long] => 178 | x 179 | // if the value is directly assignable: use it 180 | case x: AnyRef if (x.getClass.isAssignableFrom(paramType.c)) => 181 | x 182 | case x: Array[_] => 183 | x 184 | // otherwise try to create an instance using der String Constructor 185 | case x: AnyRef => 186 | val paramCtor = paramType.c.getConstructor(classOf[String]) 187 | val value = paramCtor.newInstance(x).asInstanceOf[AnyRef] 188 | value 189 | } 190 | } 191 | 192 | /** 193 | * creates a map from case class parameter 194 | * @param o AnyRef case class instance 195 | */ 196 | def serialize(o: AnyRef): Map[String, AnyRef] = { 197 | 198 | val methods = { 199 | 200 | var symbols = Map.empty[String, reflect.Method] 201 | var curClazz:Class[_] = o.getClass 202 | var done = false 203 | 204 | while(!done){ 205 | 206 | val rv: Map[String, reflect.Method] = 207 | methodCache.getOrElseUpdate(curClazz, 208 | curClazz.getDeclaredMethods 209 | .filter{ z => 210 | z.getParameterTypes.isEmpty 211 | }.map { 212 | m => m.getName -> m 213 | }.toMap) 214 | 215 | symbols ++= rv 216 | 217 | curClazz = curClazz.getSuperclass 218 | done = curClazz == classOf[java.lang.Object] || curClazz == null 219 | } 220 | 221 | symbols 222 | } 223 | 224 | val params: Seq[(String, JavaType)] = sigParserCache.getOrElseUpdate(o.getClass, CaseClassSigParser.parse(o.getClass)) 225 | val l = for { 226 | (paramName, jt) <- params; 227 | value = methods.get(paramName).get.invoke(o) 228 | } yield { 229 | (paramName, value) 230 | } 231 | l.toMap 232 | } 233 | 234 | def getParsedParams(k:Class[_]):Option[Seq[(String, JavaType)]] = { 235 | Some(sigParserCache.getOrElseUpdate(k.getClass, CaseClassSigParser.parse(k.getClass))) 236 | } 237 | } 238 | 239 | class MissingPickledSig(clazz: Class[_]) extends Error("Failed to parse pickled Scala signature from: %s".format(clazz)) 240 | 241 | class MissingExpectedType(clazz: Class[_]) extends Error( 242 | "Parsed pickled Scala signature, but no expected type found: %s" 243 | .format(clazz) 244 | ) 245 | 246 | object CaseClassSigParser { 247 | val SCALA_SIG = "ScalaSig" 248 | val SCALA_SIG_ANNOTATION = "Lscala/reflect/ScalaSignature;" 249 | val BYTES_VALUE = "bytes" 250 | 251 | protected def parseScalaSig[A](clazz: Class[A]): Option[ScalaSig] = { 252 | val firstPass = ScalaSigParser.parse(clazz) 253 | firstPass match { 254 | case Some(x) => { 255 | Some(x) 256 | } 257 | case None if clazz.getName.endsWith("$") => { 258 | val clayy = Class.forName(clazz.getName.replaceFirst("\\$$", "")) 259 | val secondPass = ScalaSigParser.parse(clayy) 260 | secondPass 261 | } 262 | case x => x 263 | } 264 | } 265 | 266 | protected def findSym[A](clazz: Class[A]): SymbolInfoSymbol with Product with Serializable = { 267 | val pss = parseScalaSig(clazz) 268 | pss match { 269 | case Some(x) => { 270 | val topLevelClasses = x.topLevelClasses 271 | topLevelClasses.headOption match { 272 | case Some(tlc) => { 273 | tlc 274 | } 275 | case None => { 276 | val topLevelObjects = x.topLevelObjects 277 | topLevelObjects.headOption match { 278 | case Some(tlo) => { 279 | tlo 280 | } 281 | case _ => throw new MissingExpectedType(clazz) 282 | } 283 | } 284 | } 285 | } 286 | case None => throw new MissingPickledSig(clazz) 287 | } 288 | } 289 | 290 | private val persistedVarCache: mutable.Map[Class[_], Array[String]] = new ConcurrentHashMap[Class[_], Array[String]]() 291 | // private val traitItCache = new mutable.HashMap[Class[_], Seq[Class[_]]]() 292 | // with mutable.SynchronizedMap[Class[_], Seq[Class[_]]] 293 | 294 | private val classesTreeCache = new ConcurrentHashMap[Class[_], Array[Class[_]]]() 295 | 296 | private def isExcluded(clazz: Class[_]) = { 297 | if (clazz == null) 298 | true 299 | else{ 300 | clazz == classOf[java.lang.Object] || 301 | clazz == classOf[scala.Any] || 302 | clazz == classOf[scala.Product] || 303 | clazz == classOf[scala.Serializable] || 304 | clazz == classOf[DbObject] || 305 | clazz.getName == "com.ansvia.graph.BlueprintsWrapper$IDGetter" || 306 | clazz.getName == "com.ansvia.graph.BlueprintsWrapper$IdDbObject" 307 | } 308 | } 309 | 310 | // @tailrec 311 | private def crawlClassesTree(clazz:Class[_]):Array[Class[_]] = { 312 | 313 | classesTreeCache.getOrElseUpdate(clazz, 314 | { 315 | var rv:Array[Class[_]] = clazz.getInterfaces.flatMap { c => 316 | if (c!=null && !isExcluded(c)) 317 | crawlClassesTree(c) ++ Array(c) 318 | else 319 | Array.empty[Class[_]] 320 | } 321 | 322 | val sp = clazz.getSuperclass 323 | if (!isExcluded(sp)){ 324 | rv ++= crawlClassesTree(sp) 325 | } 326 | 327 | rv 328 | } 329 | ) 330 | 331 | 332 | } 333 | 334 | 335 | def parse[A](clazz: Class[A]): Seq[(String, JavaType)] = { 336 | 337 | var symbols = Array.empty[(String, JavaType)] 338 | var curClazz:Class[_] = clazz 339 | var done = false 340 | var traitIterator = curClazz.getInterfaces.toIterator 341 | val mainClazz = clazz 342 | 343 | // fill cache 344 | if (persistedVarCache.get(mainClazz).isEmpty){ 345 | 346 | var fieldNames = Array.empty[String] 347 | 348 | while(!done){ 349 | fieldNames ++= curClazz.getDeclaredFields.filter { v => 350 | v.isAnnotationPresent(classOf[Persistent]) 351 | }.map(_.getName) 352 | 353 | curClazz = curClazz.getSuperclass 354 | done = isExcluded(curClazz) 355 | 356 | if (done && traitIterator.hasNext){ 357 | 358 | // try searching interfaces / traits 359 | curClazz = traitIterator.next() 360 | 361 | done = isExcluded(curClazz) 362 | } 363 | 364 | } 365 | persistedVarCache.update(mainClazz, fieldNames) 366 | } 367 | 368 | curClazz = mainClazz 369 | traitIterator = crawlClassesTree(curClazz).toIterator 370 | done = false 371 | 372 | while(!done){ 373 | 374 | // println("curClazz: " + curClazz.getName) 375 | 376 | val rv = 377 | findSym(curClazz).children 378 | .filter{ c => 379 | if (c.isCaseAccessor && !c.isPrivate){ 380 | true 381 | }else if (c.isAccessor && !c.isPrivate && !c.isLazy && !c.isProtected){ 382 | 383 | val pv = persistedVarCache.get(mainClazz).get 384 | 385 | // if (pv.length > 0) 386 | // println(curClazz.getSimpleName + ": " + pv.reduceOption(_ + ", " + _).getOrElse("") + " contains " + c.name + "?") 387 | 388 | pv.contains(c.name) 389 | 390 | }else{ 391 | false 392 | } 393 | }.map(_.asInstanceOf[MethodSymbol]) 394 | .zipWithIndex 395 | .flatMap { 396 | case (ms, idx) => { 397 | ms.infoType match { 398 | case NullaryMethodType(t: TypeRefType) => 399 | Some(ms.name -> typeRef2JavaType(t)) 400 | case _ => 401 | None 402 | } 403 | } 404 | } 405 | symbols ++= rv 406 | 407 | curClazz = curClazz.getSuperclass 408 | done = curClazz == null || 409 | curClazz == classOf[java.lang.Object] 410 | 411 | if (done && traitIterator.hasNext){ 412 | 413 | // try searching interfaces / traits 414 | curClazz = traitIterator.next() 415 | 416 | done = isExcluded(curClazz) 417 | } 418 | 419 | 420 | } 421 | symbols.toSeq 422 | } 423 | 424 | protected def typeRef2JavaType(ref: TypeRefType): JavaType = { 425 | try { 426 | JavaType(loadClass(ref.symbol.path)) 427 | } catch { 428 | case e: Throwable => { 429 | e.printStackTrace() 430 | null 431 | } 432 | } 433 | } 434 | 435 | protected def loadClass(path: String) = path match { 436 | case "scala.Predef.Map" => classOf[Map[_, _]] 437 | case "scala.Predef.Set" => classOf[Set[_]] 438 | case "scala.Predef.String" => classOf[String] 439 | case "scala.package.List" => classOf[List[_]] 440 | case "scala.package.Seq" => classOf[Seq[_]] 441 | case "scala.package.Sequence" => classOf[Seq[_]] 442 | case "scala.package.Collection" => classOf[Seq[_]] 443 | case "scala.package.IndexedSeq" => classOf[IndexedSeq[_]] 444 | case "scala.package.RandomAccessSeq" => classOf[IndexedSeq[_]] 445 | case "scala.package.Iterable" => classOf[Iterable[_]] 446 | case "scala.package.Iterator" => classOf[Iterator[_]] 447 | case "scala.package.Vector" => classOf[Vector[_]] 448 | case "scala.package.BigDecimal" => classOf[BigDecimal] 449 | case "scala.package.BigInt" => classOf[BigInt] 450 | case "scala.package.Integer" => classOf[java.lang.Integer] 451 | case "scala.package.Character" => classOf[java.lang.Character] 452 | case "scala.Long" => classOf[java.lang.Long] 453 | case "scala.Int" => classOf[java.lang.Integer] 454 | case "scala.Boolean" => classOf[java.lang.Boolean] 455 | case "scala.Short" => classOf[java.lang.Short] 456 | case "scala.Byte" => classOf[java.lang.Byte] 457 | case "scala.Float" => classOf[java.lang.Float] 458 | case "scala.Double" => classOf[java.lang.Double] 459 | case "scala.Char" => classOf[java.lang.Character] 460 | case "scala.Any" => classOf[Any] 461 | case "scala.AnyRef" => classOf[AnyRef] 462 | case name => Class.forName(name) 463 | } 464 | } 465 | 466 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/util/scalax/rules/Memoisable.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.util 2 | package scalax 3 | package rules 4 | 5 | import scala.collection.mutable.HashMap 6 | import scala.language.reflectiveCalls 7 | 8 | trait MemoisableRules extends Rules { 9 | def memo[In <: Memoisable, Out, A, X](key: AnyRef) 10 | (toRule: => In => Result[Out, A, X]) = { 11 | lazy val rule = toRule 12 | from[In] {in => in.memo(key, rule(in))} 13 | } 14 | 15 | override def ruleWithName[In, Out, A, X](name: String, 16 | f: In => rules.Result[Out, A, X]) = super.ruleWithName( 17 | name, (in: In) => 18 | in match { 19 | case s: Memoisable => s.memo(name, f(in)) 20 | case _ => f(in) 21 | } 22 | ) 23 | } 24 | 25 | trait Memoisable { 26 | def memo[A](key: AnyRef, a: => A): A 27 | } 28 | 29 | 30 | object DefaultMemoisable { 31 | var debug = false 32 | } 33 | 34 | trait DefaultMemoisable extends Memoisable { 35 | protected val map = new HashMap[AnyRef, Any] 36 | 37 | def memo[A](key: AnyRef, a: => A) = { 38 | map.getOrElseUpdate(key, compute(key, a)).asInstanceOf[A] 39 | } 40 | 41 | protected def compute[A](key: AnyRef, a: => A): Any = a match { 42 | case success: Success[_, _] => onSuccess(key, success); success 43 | case other => 44 | if (DefaultMemoisable.debug) println(key + " -> " + other) 45 | other 46 | } 47 | 48 | protected def onSuccess[S, T](key: AnyRef, result: Success[S, T]) { 49 | val Success(out, t) = result 50 | if (DefaultMemoisable.debug) println(key + " -> " + t + " (" + out + ")") 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/util/scalax/rules/Result.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.util 2 | package scalax 3 | package rules 4 | 5 | /**Represents the combined value of two rules applied in sequence. 6 | * 7 | * @see the Scala parser combinator 8 | */ 9 | case class ~[+A, +B](_1: A, _2: B) { 10 | override def toString = "(" + _1 + " ~ " + _2 + ")" 11 | } 12 | 13 | 14 | sealed abstract class Result[+Out, +A, +X] { 15 | def out: Out 16 | 17 | def value: A 18 | 19 | def error: X 20 | 21 | implicit def toOption: Option[A] 22 | 23 | def map[B](f: A => B): Result[Out, B, X] 24 | 25 | def mapOut[Out2](f: Out => Out2): Result[Out2, A, X] 26 | 27 | def map[Out2, B](f: (Out, A) => (Out2, B)): Result[Out2, B, X] 28 | 29 | def flatMap[Out2, B](f: (Out, A) => Result[Out2, B, Nothing]): Result[Out2, B, X] 30 | 31 | def orElse[Out2 >: Out, B >: A](other: => Result[Out2, B, Nothing]): Result[Out2, B, X] 32 | } 33 | 34 | case class Success[+Out, +A](out: Out, 35 | value: A) extends Result[Out, A, Nothing] { 36 | def error = throw new ScalaSigParserError("No error") 37 | 38 | def toOption = Some(value) 39 | 40 | def map[B](f: A => B): Result[Out, B, Nothing] = Success(out, f(value)) 41 | 42 | def mapOut[Out2](f: Out => Out2): Result[Out2, A, Nothing] = Success(f(out), value) 43 | 44 | def map[Out2, B](f: (Out, A) => (Out2, B)): Success[Out2, B] = f(out, value) match {case (out2, b) => Success(out2, b)} 45 | 46 | def flatMap[Out2, B](f: (Out, A) => Result[Out2, B, Nothing]): Result[Out2, B, Nothing] = f(out, value) 47 | 48 | def orElse[Out2 >: Out, B >: A](other: => Result[Out2, B, Nothing]): Result[Out2, B, Nothing] = this 49 | } 50 | 51 | sealed abstract class NoSuccess[+X] extends Result[Nothing, Nothing, X] { 52 | def out = throw new ScalaSigParserError("No output") 53 | 54 | def value = throw new ScalaSigParserError("No value") 55 | 56 | def toOption = None 57 | 58 | def map[B](f: Nothing => B) = this 59 | 60 | def mapOut[Out2](f: Nothing => Out2) = this 61 | 62 | def map[Out2, B](f: (Nothing, Nothing) => (Out2, B)) = this 63 | 64 | def flatMap[Out2, B](f: (Nothing, Nothing) => Result[Out2, B, Nothing]) = this 65 | 66 | def orElse[Out2, B](other: => Result[Out2, B, Nothing]) = other 67 | } 68 | 69 | case object Failure extends NoSuccess[Nothing] { 70 | def error = throw new ScalaSigParserError("No error") 71 | } 72 | 73 | case class ScalaSigParserError(msg: String) extends RuntimeException(msg) 74 | 75 | case class Error[+X](error: X) extends NoSuccess[X] { 76 | } 77 | 78 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/util/scalax/rules/Rule.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.util 2 | package scalax 3 | package rules 4 | 5 | import scala.language.postfixOps 6 | 7 | /**A Rule is a function from some input to a Result. The result may be: 8 | * 13 | * 14 | * @author Andrew Foggin 15 | * 16 | * Inspired by the Scala parser combinator. 17 | */ 18 | trait Rule[-In, +Out, +A, +X] extends (In => Result[Out, A, X]) { 19 | val factory: Rules 20 | 21 | import factory._ 22 | 23 | def as(name: String) = ruleWithName(name, this) 24 | 25 | def flatMap[Out2, B, X2 >: X](fa2ruleb: A => Out => Result[Out2, B, X2]) = mapResult { 26 | case Success(out, a) => fa2ruleb(a)(out) 27 | case Failure => Failure 28 | case err@Error(_) => err 29 | } 30 | 31 | def map[B](fa2b: A => B) = flatMap {a => out => Success(out, fa2b(a))} 32 | 33 | def filter(f: A => Boolean) = flatMap {a => 34 | out => if (f(a)) Success(out, a) else Failure 35 | } 36 | 37 | def mapResult[Out2, B, Y](f: Result[Out, A, X] => Result[Out2, B, Y]) = rule { 38 | in: In => f(apply(in)) 39 | } 40 | 41 | def orElse[In2 <: In, Out2 >: Out, A2 >: A, X2 >: X](other: => Rule[In2, Out2, A2, X2]): Rule[In2, Out2, A2, X2] = new Choice[In2, Out2, A2, X2] { 42 | val factory = Rule.this.factory 43 | lazy val choices = Rule.this :: other :: Nil 44 | } 45 | 46 | def orError[In2 <: In] = this orElse (error[In2]) 47 | 48 | def |[In2 <: In, Out2 >: Out, A2 >: A, X2 >: X](other: => Rule[In2, Out2, A2, X2]) = orElse(other) 49 | 50 | def ^^[B](fa2b: A => B) = map(fa2b) 51 | 52 | def ^^?[B](pf: PartialFunction[A, B]) = filter(pf.isDefinedAt(_)) ^^ pf 53 | 54 | def ??(pf: PartialFunction[A, Any]) = filter(pf.isDefinedAt(_)) 55 | 56 | def -^[B](b: B) = map {any => b} 57 | 58 | /**Maps an Error */ 59 | def !^[Y](fx2y: X => Y) = mapResult { 60 | case s@Success(_, _) => s 61 | case Failure => Failure 62 | case Error(x) => Error(fx2y(x)) 63 | } 64 | 65 | def >>[Out2, B, X2 >: X](fa2ruleb: A => Out => Result[Out2, B, X2]) = flatMap(fa2ruleb) 66 | 67 | def >->[Out2, B, X2 >: X](fa2resultb: A => Result[Out2, B, X2]) = flatMap { 68 | a => any => fa2resultb(a) 69 | } 70 | 71 | def >>?[Out2, B, X2 >: X](pf: PartialFunction[A, Rule[Out, Out2, B, X2]]) = filter(pf isDefinedAt _) flatMap pf 72 | 73 | def >>&[B, X2 >: X](fa2ruleb: A => Out => Result[Any, B, X2]) = flatMap {a => 74 | out => fa2ruleb(a)(out) mapOut {any => out} 75 | } 76 | 77 | def ~[Out2, B, X2 >: X](next: => Rule[Out, Out2, B, X2]) = for (a <- this; b <- next) yield new ~(a, b) 78 | 79 | def ~-[Out2, B, X2 >: X](next: => Rule[Out, Out2, B, X2]) = for (a <- this; b <- next) yield a 80 | 81 | def -~[Out2, B, X2 >: X](next: => Rule[Out, Out2, B, X2]) = for (a <- this; b <- next) yield b 82 | 83 | def ~++[Out2, B >: A, X2 >: X](next: => Rule[Out, Out2, Seq[B], X2]) = for (a <- this; b <- next) yield a :: b.toList 84 | 85 | /**Apply the result of this rule to the function returned by the next rule */ 86 | def ~>[Out2, B, X2 >: X](next: => Rule[Out, Out2, A => B, X2]) = for (a <- this; fa2b <- next) yield fa2b(a) 87 | 88 | /**Apply the result of this rule to the function returned by the previous rule */ 89 | def <~:[InPrev, B, X2 >: X](prev: => Rule[InPrev, In, A => B, X2]) = for (fa2b <- prev; a <- this) yield fa2b(a) 90 | 91 | def ~![Out2, B, X2 >: X](next: => Rule[Out, Out2, B, X2]) = for (a <- this; b <- next orError) yield new ~(a, b) 92 | 93 | def ~-![Out2, B, X2 >: X](next: => Rule[Out, Out2, B, X2]) = for (a <- this; b <- next orError) yield a 94 | 95 | def -~![Out2, B, X2 >: X](next: => Rule[Out, Out2, B, X2]) = for (a <- this; b <- next orError) yield b 96 | 97 | def -[In2 <: In](exclude: => Rule[In2, Any, Any, Any]) = !exclude -~ this 98 | 99 | /**^~^(f) is equivalent to ^^ { case b1 ~ b2 => f(b1, b2) } 100 | */ 101 | def ^~^[B1, B2, B >: A <% B1 ~ B2, C](f: (B1, B2) => C) = map {a => 102 | (a: B1 ~ B2) match {case b1 ~ b2 => f(b1, b2)} 103 | } 104 | 105 | /**^~~^(f) is equivalent to ^^ { case b1 ~ b2 ~ b3 => f(b1, b2, b3) } 106 | */ 107 | def ^~~^[B1, B2, B3, B >: A <% B1 ~ B2 ~ B3, C](f: (B1, B2, B3) => C) = map { 108 | a => 109 | (a: B1 ~ B2 ~ B3) match {case b1 ~ b2 ~ b3 => f(b1, b2, b3)} 110 | } 111 | 112 | /**^~~~^(f) is equivalent to ^^ { case b1 ~ b2 ~ b3 ~ b4 => f(b1, b2, b3, b4) } 113 | */ 114 | def ^~~~^[B1, B2, B3, B4, B >: A <% B1 ~ B2 ~ B3 ~ B4, C](f: (B1, B2, B3, B4) => C) = map { 115 | a => 116 | (a: B1 ~ B2 ~ B3 ~ B4) match {case b1 ~ b2 ~ b3 ~ b4 => f(b1, b2, b3, b4)} 117 | } 118 | 119 | /**^~~~~^(f) is equivalent to ^^ { case b1 ~ b2 ~ b3 ~ b4 ~ b5 => f(b1, b2, b3, b4, b5) } 120 | */ 121 | def ^~~~~^[B1, B2, B3, B4, B5, B >: A <% B1 ~ B2 ~ B3 ~ B4 ~ B5, C](f: (B1, B2, B3, B4, B5) => C) = map { 122 | a => 123 | (a: B1 ~ B2 ~ B3 ~ B4 ~ B5) match {case b1 ~ b2 ~ b3 ~ b4 ~ b5 => f(b1, b2, b3, b4, b5)} 124 | } 125 | 126 | /**^~~~~~^(f) is equivalent to ^^ { case b1 ~ b2 ~ b3 ~ b4 ~ b5 ~ b6 => f(b1, b2, b3, b4, b5, b6) } 127 | */ 128 | def ^~~~~~^[B1, B2, B3, B4, B5, B6, B >: A <% B1 ~ B2 ~ B3 ~ B4 ~ B5 ~ B6, C](f: (B1, B2, B3, B4, B5, B6) => C) = map { 129 | a => 130 | (a: B1 ~ B2 ~ B3 ~ B4 ~ B5 ~ B6) match {case b1 ~ b2 ~ b3 ~ b4 ~ b5 ~ b6 => f(b1, b2, b3, b4, b5, b6)} 131 | } 132 | 133 | /**^~~~~~~^(f) is equivalent to ^^ { case b1 ~ b2 ~ b3 ~ b4 ~ b5 ~ b6 => f(b1, b2, b3, b4, b5, b6) } 134 | */ 135 | def ^~~~~~~^[B1, B2, B3, B4, B5, B6, B7, B >: A <% B1 ~ B2 ~ B3 ~ B4 ~ B5 ~ B6 ~ B7, C](f: (B1, B2, B3, B4, B5, B6, B7) => C) = map { 136 | a => 137 | (a: B1 ~ B2 ~ B3 ~ B4 ~ B5 ~ B6 ~ B7) match {case b1 ~ b2 ~ b3 ~ b4 ~ b5 ~ b6 ~ b7 => f(b1, b2, b3, b4, b5, b6, b7)} 138 | } 139 | 140 | /**>~>(f) is equivalent to >> { case b1 ~ b2 => f(b1, b2) } 141 | */ 142 | def >~>[Out2, B1, B2, B >: A <% B1 ~ B2, C, X2 >: X](f: (B1, B2) => Out => Result[Out2, C, X2]) = flatMap { 143 | a => 144 | (a: B1 ~ B2) match {case b1 ~ b2 => f(b1, b2)} 145 | } 146 | 147 | /**^-^(f) is equivalent to ^^ { b2 => b1 => f(b1, b2) } 148 | */ 149 | def ^-^[B1, B2 >: A, C](f: (B1, B2) => C) = map {b2: B2 => 150 | b1: B1 => f(b1, b2) 151 | } 152 | 153 | /**^~>~^(f) is equivalent to ^^ { case b2 ~ b3 => b1 => f(b1, b2, b3) } 154 | */ 155 | def ^~>~^[B1, B2, B3, B >: A <% B2 ~ B3, C](f: (B1, B2, B3) => C) = map {a => 156 | (a: B2 ~ B3) match {case b2 ~ b3 => b1: B1 => f(b1, b2, b3)} 157 | } 158 | } 159 | 160 | 161 | trait Choice[-In, +Out, +A, +X] extends Rule[In, Out, A, X] { 162 | def choices: List[Rule[In, Out, A, X]] 163 | 164 | def apply(in: In) = { 165 | def oneOf(list: List[Rule[In, Out, A, X]]): Result[Out, A, X] = list match { 166 | case Nil => Failure 167 | case first :: rest => first(in) match { 168 | case Failure => oneOf(rest) 169 | case result => result 170 | } 171 | } 172 | oneOf(choices) 173 | } 174 | 175 | override def orElse[In2 <: In, Out2 >: Out, A2 >: A, X2 >: X](other: => Rule[In2, Out2, A2, X2]): Rule[In2, Out2, A2, X2] = new Choice[In2, Out2, A2, X2] { 176 | val factory = Choice.this.factory 177 | lazy val choices = Choice.this.choices ::: other :: Nil 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/util/scalax/rules/Rules.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.util 2 | package scalax 3 | package rules 4 | 5 | import scala.language.postfixOps 6 | import scala.language.implicitConversions 7 | 8 | trait Name { 9 | def name: String 10 | 11 | override def toString = name 12 | } 13 | 14 | /**A factory for rules. 15 | * 16 | * @author Andrew Foggin 17 | * 18 | * Inspired by the Scala parser combinator. 19 | */ 20 | trait Rules { 21 | implicit def rule[In, Out, A, X](f: In => Result[Out, A, X]): Rule[In, Out, A, X] = new DefaultRule(f) 22 | 23 | implicit def inRule[In, Out, A, X](rule: Rule[In, Out, A, X]): InRule[In, Out, A, X] = new InRule(rule) 24 | 25 | implicit def seqRule[In, A, X](rule: Rule[In, In, A, X]): SeqRule[In, A, X] = new SeqRule(rule) 26 | 27 | def from[In] = new { 28 | def apply[Out, A, X](f: In => Result[Out, A, X]) = rule(f) 29 | } 30 | 31 | def state[s] = new StateRules { 32 | type S = s 33 | val factory = Rules.this 34 | } 35 | 36 | def success[Out, A](out: Out, a: A) = rule {in: Any => Success(out, a)} 37 | 38 | def failure = rule {in: Any => Failure} 39 | 40 | def error[In] = rule {in: In => Error(in)} 41 | 42 | def error[X](err: X) = rule {in: Any => Error(err)} 43 | 44 | def oneOf[In, Out, A, X](rules: Rule[In, Out, A, X]*): Rule[In, Out, A, X] = new Choice[In, Out, A, X] { 45 | val factory = Rules.this 46 | val choices = rules.toList 47 | } 48 | 49 | def ruleWithName[In, Out, A, X](_name: String, 50 | f: In => Result[Out, A, X]): Rule[In, Out, A, X] with Name = 51 | new DefaultRule(f) with Name { 52 | val name = _name 53 | } 54 | 55 | class DefaultRule[In, Out, A, X](f: In => Result[Out, A, X]) extends Rule[In, Out, A, X] { 56 | val factory = Rules.this 57 | 58 | def apply(in: In) = f(in) 59 | } 60 | 61 | /**Converts a rule into a function that throws an Exception on failure. */ 62 | def expect[In, Out, A, Any](rule: Rule[In, Out, A, Any]): In => A = (in) => 63 | rule(in) match { 64 | case Success(_, a) => a 65 | case Failure => throw new ScalaSigParserError("Unexpected failure") 66 | case Error(x) => throw new ScalaSigParserError("Unexpected error: " + x) 67 | } 68 | } 69 | 70 | /**A factory for rules that apply to a particular context. 71 | * 72 | * Requires S the context to which rules apply. 73 | * 74 | * @author Andrew Foggin 75 | * 76 | * Inspired by the Scala parser combinator. 77 | */ 78 | trait StateRules { 79 | type S 80 | type Rule[+A, +X] = rules.Rule[S, S, A, X] 81 | 82 | val factory: Rules 83 | 84 | import factory._ 85 | 86 | def apply[A, X](f: S => Result[S, A, X]) = rule(f) 87 | 88 | def unit[A](a: => A) = apply {s => Success(s, a)} 89 | 90 | def read[A](f: S => A) = apply {s => Success(s, f(s))} 91 | 92 | def get = apply {s => Success(s, s)} 93 | 94 | def set(s: => S) = apply {oldS => Success(s, oldS)} 95 | 96 | def update(f: S => S) = apply {s => Success(s, f(s))} 97 | 98 | def nil = unit(Nil) 99 | 100 | def none = unit(None) 101 | 102 | /**Create a rule that identities if f(in) is true. */ 103 | def cond(f: S => Boolean) = get filter f 104 | 105 | /**Create a rule that succeeds if all of the given rules succeed. 106 | @param rules the rules to apply in sequence. 107 | */ 108 | def allOf[A, X](rules: Seq[Rule[A, X]]) = { 109 | def rep(in: S, rules: List[Rule[A, X]], 110 | results: List[A]): Result[S, List[A], X] = { 111 | rules match { 112 | case Nil => Success(in, results.reverse) 113 | case rule :: tl => rule(in) match { 114 | case Failure => Failure 115 | case Error(x) => Error(x) 116 | case Success(out, v) => rep(out, tl, v :: results) 117 | } 118 | } 119 | } 120 | in: S => rep(in, rules.toList, Nil) 121 | } 122 | 123 | 124 | /**Create a rule that succeeds with a list of all the provided rules that succeed. 125 | @param rules the rules to apply in sequence. 126 | */ 127 | def anyOf[A, X](rules: Seq[Rule[A, X]]) = allOf(rules.map(_ ?)) ^^ { 128 | opts => opts.flatMap(x => x) 129 | } 130 | 131 | /**Repeatedly apply a rule from initial value until finished condition is met. */ 132 | def repeatUntil[T, X](rule: Rule[T => T, X])(finished: T => Boolean) 133 | (initial: T) = apply { 134 | // more compact using HoF but written this way so it's tail-recursive 135 | def rep(in: S, t: T): Result[S, T, X] = { 136 | if (finished(t)) Success(in, t) 137 | else rule(in) match { 138 | case Success(out, f) => rep(out, f(t)) 139 | case Failure => Failure 140 | case Error(x) => Error(x) 141 | } 142 | } 143 | in => rep(in, initial) 144 | } 145 | 146 | 147 | } 148 | 149 | trait RulesWithState extends Rules with StateRules { 150 | val factory = this 151 | } 152 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/util/scalax/rules/SeqRule.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.util 2 | package scalax 3 | package rules 4 | 5 | import scala.language.reflectiveCalls 6 | import scala.language.postfixOps 7 | 8 | /** 9 | * A workaround for the difficulties of dealing with 10 | * a contravariant 'In' parameter type... 11 | */ 12 | class InRule[In, +Out, +A, +X](rule: Rule[In, Out, A, X]) { 13 | 14 | def mapRule[Out2, B, Y](f: Result[Out, A, X] => In => Result[Out2, B, Y]): Rule[In, Out2, B, Y] = rule.factory.rule { 15 | in: In => f(rule(in))(in) 16 | } 17 | 18 | /**Creates a rule that succeeds only if the original rule would fail on the given context. */ 19 | def unary_! : Rule[In, In, Unit, Nothing] = mapRule { 20 | case Success(_, _) => in: In => Failure 21 | case _ => in: In => Success(in, ()) 22 | } 23 | 24 | /**Creates a rule that succeeds if the original rule succeeds, but returns the original input. */ 25 | def & : Rule[In, In, A, X] = mapRule { 26 | case Success(_, a) => in: In => Success(in, a) 27 | case Failure => in: In => Failure 28 | case Error(x) => in: In => Error(x) 29 | } 30 | } 31 | 32 | class SeqRule[S, +A, +X](rule: Rule[S, S, A, X]) { 33 | 34 | import rule.factory._ 35 | 36 | def ? = rule mapRule { 37 | case Success(out, a) => in: S => Success(out, Some(a)) 38 | case Failure => in: S => Success(in, None) 39 | case Error(x) => in: S => Error(x) 40 | } 41 | 42 | /**Creates a rule that always succeeds with a Boolean value. 43 | * Value is 'true' if this rule succeeds, 'false' otherwise */ 44 | def -? = ? map {_ isDefined} 45 | 46 | def * = from[S] { 47 | // tail-recursive function with reverse list accumulator 48 | def rep(in: S, acc: List[A]): Result[S, List[A], X] = rule(in) match { 49 | case Success(out, a) => rep(out, a :: acc) 50 | case Failure => Success(in, acc.reverse) 51 | case err: Error[_] => err 52 | } 53 | in => rep(in, Nil) 54 | } 55 | 56 | def + = rule ~++ * 57 | 58 | def ~>?[B >: A, X2 >: X](f: => Rule[S, S, B => B, X2]) = for (a <- rule; fs <- f ?) yield fs.foldLeft[B](a) {(b, 59 | f) => f(b) 60 | } 61 | 62 | def ~>*[B >: A, X2 >: X](f: => Rule[S, S, B => B, X2]) = for (a <- rule; fs <- f *) yield fs.foldLeft[B](a) {(b, 63 | f) => f(b) 64 | } 65 | 66 | def ~*~[B >: A, X2 >: X](join: => Rule[S, S, (B, B) => B, X2]) = { 67 | this ~>* (for (f <- join; a <- rule) yield f(_: B, a)) 68 | } 69 | 70 | /**Repeats this rule one or more times with a separator (which is discarded) */ 71 | def +/[X2 >: X](sep: => Rule[S, S, Any, X2]) = rule ~++ (sep -~ rule *) 72 | 73 | /**Repeats this rule zero or more times with a separator (which is discarded) */ 74 | def */[X2 >: X](sep: => Rule[S, S, Any, X2]) = +/(sep) | state[S].nil 75 | 76 | def *~-[Out, X2 >: X](end: => Rule[S, Out, Any, X2]) = (rule - end *) ~- end 77 | 78 | def +~-[Out, X2 >: X](end: => Rule[S, Out, Any, X2]) = (rule - end +) ~- end 79 | 80 | /**Repeats this rule num times */ 81 | def times(num: Int): Rule[S, S, Seq[A], X] = from[S] { 82 | val result = new collection.mutable.ArraySeq[A](num) 83 | // more compact using HoF but written this way so it's tail-recursive 84 | def rep(i: Int, in: S): Result[S, Seq[A], X] = { 85 | if (i == num) Success(in, result) 86 | else rule(in) match { 87 | case Success(out, a) => { 88 | result(i) = a 89 | rep(i + 1, out) 90 | } 91 | case Failure => Failure 92 | case err: Error[_] => err 93 | } 94 | } 95 | in => rep(0, in) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/util/scalax/rules/scalasig/ClassFileParser.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.util 2 | package scalax 3 | package rules 4 | package scalasig 5 | 6 | 7 | import java.io.IOException 8 | import scala.language.postfixOps 9 | 10 | import scala._ 11 | import scala.Predef._ 12 | 13 | object ByteCode { 14 | def apply(bytes: Array[Byte]) = new ByteCode(bytes, 0, bytes.length) 15 | 16 | def forClass(clazz: Class[_]) = { 17 | val name = clazz.getName 18 | val subPath = name.substring(name.lastIndexOf('.') + 1) + ".class" 19 | val in = clazz.getResourceAsStream(subPath) 20 | 21 | try { 22 | var rest = in.available() 23 | val bytes = new Array[Byte](rest) 24 | while (rest > 0) { 25 | val res = in.read(bytes, bytes.length - rest, rest) 26 | if (res == -1) throw new IOException("read error") 27 | rest -= res 28 | } 29 | ByteCode(bytes) 30 | 31 | } finally { 32 | in.close() 33 | } 34 | } 35 | } 36 | 37 | /**Represents a chunk of raw bytecode. Used as input for the parsers 38 | */ 39 | class ByteCode(val bytes: Array[Byte], val pos: Int, val length: Int) { 40 | 41 | assert(pos >= 0 && length >= 0 && pos + length <= bytes.length) 42 | 43 | def nextByte = if (length == 0) Failure else Success(drop(1), bytes(pos)) 44 | 45 | def next(n: Int) = if (length >= n) Success(drop(n), take(n)) else Failure 46 | 47 | def take(n: Int) = new ByteCode(bytes, pos, n) 48 | 49 | def drop(n: Int) = new ByteCode(bytes, pos + n, length - n) 50 | 51 | def fold[X](x: X)(f: (X, Byte) => X): X = { 52 | var result = x 53 | var i = pos 54 | while (i < pos + length) { 55 | result = f(result, bytes(i)) 56 | i += 1 57 | } 58 | result 59 | } 60 | 61 | override def toString = length + " bytes" 62 | 63 | def toInt = fold(0) {(x, b) => (x << 8) + (b & 0xFF)} 64 | 65 | def toLong = fold(0L) {(x, b) => (x << 8) + (b & 0xFF)} 66 | 67 | private val utf8: Array[Byte] => Array[Char] = { 68 | // scala 2.8.1 69 | try { 70 | val m1 = io.Codec.getClass.getDeclaredMethod("toUTF8", classOf[Array[Byte]]); 71 | {f => m1.invoke(io.Codec, f).asInstanceOf[Array[Char]]} 72 | } catch { 73 | case e: NoSuchMethodException => { 74 | // scala 2.9.0.RC1 75 | val m2 = io.Codec.getClass.getDeclaredMethod("fromUTF8", classOf[Array[Byte]]); 76 | {f => m2.invoke(io.Codec, f).asInstanceOf[Array[Char]]} 77 | } 78 | } 79 | } 80 | 81 | /** 82 | * Transforms array subsequence of the current buffer into the UTF8 String and 83 | * stores and array of bytes for the decompiler 84 | */ 85 | def fromUTF8StringAndBytes: StringBytesPair = { 86 | val chunk: Array[Byte] = bytes drop pos take length 87 | StringBytesPair(utf8(chunk).mkString, chunk) 88 | } 89 | 90 | def byte(i: Int) = bytes(pos) & 0xFF 91 | } 92 | 93 | /** 94 | * The wrapper for decode UTF-8 string 95 | */ 96 | case class StringBytesPair(string: String, bytes: Array[Byte]) 97 | 98 | /**Provides rules for parsing byte-code. 99 | */ 100 | trait ByteCodeReader extends RulesWithState { 101 | type S = ByteCode 102 | type Parser[A] = Rule[A, String] 103 | 104 | val byte = apply(_ nextByte) 105 | 106 | val u1 = byte ^^ (_ & 0xFF) 107 | val u2 = bytes(2) ^^ (_ toInt) 108 | val u4 = bytes(4) ^^ (_ toInt) 109 | // should map to Long?? 110 | 111 | def bytes(n: Int) = apply(_ next n) 112 | } 113 | 114 | object ClassFileParser extends ByteCodeReader { 115 | def parse(byteCode: ByteCode) = expect(classFile)(byteCode) 116 | 117 | def parseAnnotations(byteCode: ByteCode) = expect(annotations)(byteCode) 118 | 119 | val magicNumber = (u4 filter (_ == 0xCAFEBABE)) | error("Not a valid class file") 120 | val version = u2 ~ u2 ^^ {case minor ~ major => (major, minor)} 121 | val constantPool = (u2 ^^ ConstantPool) >> repeatUntil(constantPoolEntry)(_ isFull) 122 | 123 | // NOTE currently most constants just evaluate to a string description 124 | // TODO evaluate to useful values 125 | val utf8String = (u2 >> bytes) ^^ add1 {raw => 126 | pool => raw.fromUTF8StringAndBytes 127 | } 128 | val intConstant = u4 ^^ add1 {x => pool => x} 129 | val floatConstant = bytes(4) ^^ add1 {raw => pool => "Float: TODO"} 130 | val longConstant = bytes(8) ^^ add2 {raw => pool => raw.toLong} 131 | val doubleConstant = bytes(8) ^^ add2 {raw => pool => "Double: TODO"} 132 | val classRef = u2 ^^ add1 {x => pool => "Class: " + pool(x)} 133 | val stringRef = u2 ^^ add1 {x => pool => "String: " + pool(x)} 134 | val fieldRef = memberRef("Field") 135 | val methodRef = memberRef("Method") 136 | val interfaceMethodRef = memberRef("InterfaceMethod") 137 | val nameAndType = u2 ~ u2 ^^ add1 { 138 | case name ~ descriptor => 139 | pool => "NameAndType: " + pool(name) + ", " + pool(descriptor) 140 | } 141 | 142 | val constantPoolEntry = u1 >> { 143 | case 1 => utf8String 144 | case 3 => intConstant 145 | case 4 => floatConstant 146 | case 5 => longConstant 147 | case 6 => doubleConstant 148 | case 7 => classRef 149 | case 8 => stringRef 150 | case 9 => fieldRef 151 | case 10 => methodRef 152 | case 11 => interfaceMethodRef 153 | case 12 => nameAndType 154 | } 155 | 156 | val interfaces = u2 >> u2.times 157 | 158 | // bytes are parametrizes by the length, declared in u4 section 159 | val attribute = u2 ~ (u4 >> bytes) ^~^ Attribute 160 | // parse attributes u2 times 161 | val attributes = u2 >> attribute.times 162 | 163 | // parse runtime-visible annotations 164 | abstract class ElementValue 165 | 166 | case class AnnotationElement(elementNameIndex: Int, 167 | elementValue: ElementValue) 168 | 169 | case class ConstValueIndex(index: Int) extends ElementValue 170 | 171 | case class EnumConstValue(typeNameIndex: Int, 172 | constNameIndex: Int) extends ElementValue 173 | 174 | case class ClassInfoIndex(index: Int) extends ElementValue 175 | 176 | case class Annotation(typeIndex: Int, 177 | elementValuePairs: Seq[AnnotationElement]) extends ElementValue 178 | 179 | case class ArrayValue(values: Seq[ElementValue]) extends ElementValue 180 | 181 | def element_value: Parser[ElementValue] = u1 >> { 182 | case 'B' | 'C' | 'D' | 'F' | 'I' | 'J' | 'S' | 'Z' | 's' => u2 ^^ ConstValueIndex 183 | case 'e' => u2 ~ u2 ^~^ EnumConstValue 184 | case 'c' => u2 ^^ ClassInfoIndex 185 | case '@' => annotation //nested annotation 186 | case '[' => u2 >> element_value.times ^^ ArrayValue 187 | } 188 | 189 | val element_value_pair = u2 ~ element_value ^~^ AnnotationElement 190 | val annotation: Parser[Annotation] = u2 ~ (u2 >> element_value_pair.times) ^~^ Annotation 191 | val annotations = u2 >> annotation.times 192 | 193 | val field = u2 ~ u2 ~ u2 ~ attributes ^~~~^ Field 194 | val fields = u2 >> field.times 195 | 196 | val method = u2 ~ u2 ~ u2 ~ attributes ^~~~^ Method 197 | val methods = u2 >> method.times 198 | 199 | val header = magicNumber -~ u2 ~ u2 ~ constantPool ~ u2 ~ u2 ~ u2 ~ interfaces ^~~~~~~^ ClassFileHeader 200 | val classFile = header ~ fields ~ methods ~ attributes ~- !u1 ^~~~^ ClassFile 201 | 202 | // TODO create a useful object, not just a string 203 | def memberRef(description: String) = u2 ~ u2 ^^ add1 { 204 | case classRef ~ nameAndTypeRef => 205 | pool => description + ": " + pool(classRef) + ", " + pool(nameAndTypeRef) 206 | } 207 | 208 | def add1[T](f: T => ConstantPool => Any)(raw: T) 209 | (pool: ConstantPool) = pool add f(raw) 210 | 211 | def add2[T](f: T => ConstantPool => Any)(raw: T) 212 | (pool: ConstantPool) = pool add f(raw) add {pool => ""} 213 | } 214 | 215 | case class ClassFile( 216 | header: ClassFileHeader, 217 | fields: Seq[Field], 218 | methods: Seq[Method], 219 | attributes: Seq[Attribute]) { 220 | 221 | def majorVersion = header.major 222 | 223 | def minorVersion = header.minor 224 | 225 | def className = constant(header.classIndex) 226 | 227 | def superClass = constant(header.superClassIndex) 228 | 229 | def interfaces = header.interfaces.map(constant) 230 | 231 | def constant(index: Int) = header.constants(index) match { 232 | case StringBytesPair(str, _) => str 233 | case z => z 234 | } 235 | 236 | def constantWrapped(index: Int) = header.constants(index) 237 | 238 | def attribute(name: String) = attributes.find { 239 | attrib => constant(attrib.nameIndex) == name 240 | } 241 | 242 | val RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations" 243 | 244 | def annotations = (attributes.find( 245 | attr => constant(attr.nameIndex) == RUNTIME_VISIBLE_ANNOTATIONS 246 | ) 247 | .map( 248 | attr => ClassFileParser.parseAnnotations(attr.byteCode) 249 | )) 250 | 251 | def annotation(name: String) = annotations.flatMap( 252 | seq => seq.find( 253 | annot => constant(annot.typeIndex) == name 254 | ) 255 | ) 256 | } 257 | 258 | case class Attribute(nameIndex: Int, byteCode: ByteCode) 259 | 260 | case class Field(flags: Int, nameIndex: Int, descriptorIndex: Int, 261 | attributes: Seq[Attribute]) 262 | 263 | case class Method(flags: Int, nameIndex: Int, descriptorIndex: Int, 264 | attributes: Seq[Attribute]) 265 | 266 | case class ClassFileHeader( 267 | minor: Int, 268 | major: Int, 269 | constants: ConstantPool, 270 | flags: Int, 271 | classIndex: Int, 272 | superClassIndex: Int, 273 | interfaces: Seq[Int]) { 274 | 275 | def constant(index: Int) = constants(index) 276 | } 277 | 278 | case class ConstantPool(len: Int) { 279 | val size = len - 1 280 | 281 | private val buffer = new scala.collection.mutable.ArrayBuffer[ConstantPool => Any] 282 | private val values = Array.fill[Option[Any]](size)(None) 283 | 284 | def isFull = buffer.length >= size 285 | 286 | def apply(index: Int) = { 287 | // Note constant pool indices are 1-based 288 | val i = index - 1 289 | values(i) getOrElse { 290 | val value = buffer(i)(this) 291 | buffer(i) = null 292 | values(i) = Some(value) 293 | value 294 | } 295 | } 296 | 297 | def add(f: ConstantPool => Any) = { 298 | buffer += f 299 | this 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/util/scalax/rules/scalasig/Flags.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.util.scalax.rules.scalasig 2 | 3 | trait Flags { 4 | def hasFlag(flag: Long): Boolean 5 | 6 | def isImplicit = hasFlag(0x00000001) 7 | 8 | def isFinal = hasFlag(0x00000002) 9 | 10 | def isPrivate = hasFlag(0x00000004) 11 | 12 | def isProtected = hasFlag(0x00000008) 13 | 14 | def isSealed = hasFlag(0x00000010) 15 | 16 | def isOverride = hasFlag(0x00000020) 17 | 18 | def isCase = hasFlag(0x00000040) 19 | 20 | def isAbstract = hasFlag(0x00000080) 21 | 22 | def isDeferred = hasFlag(0x00000100) 23 | 24 | def isMethod = hasFlag(0x00000200) 25 | 26 | def isModule = hasFlag(0x00000400) 27 | 28 | def isInterface = hasFlag(0x00000800) 29 | 30 | def isMutable = hasFlag(0x00001000) 31 | 32 | def isParam = hasFlag(0x00002000) 33 | 34 | def isPackage = hasFlag(0x00004000) 35 | 36 | def isDeprecated = hasFlag(0x00008000) 37 | 38 | def isCovariant = hasFlag(0x00010000) 39 | 40 | def isCaptured = hasFlag(0x00010000) 41 | 42 | def isByNameParam = hasFlag(0x00010000) 43 | 44 | def isContravariant = hasFlag(0x00020000) 45 | 46 | def isLabel = hasFlag(0x00020000) 47 | 48 | // method symbol is a label. Set by TailCall 49 | def isInConstructor = hasFlag(0x00020000) 50 | 51 | // class symbol is defined in this/superclass constructor 52 | 53 | def isAbstractOverride = hasFlag(0x00040000) 54 | 55 | def isLocal = hasFlag(0x00080000) 56 | 57 | def isJava = hasFlag(0x00100000) 58 | 59 | def isSynthetic = hasFlag(0x00200000) 60 | 61 | def isStable = hasFlag(0x00400000) 62 | 63 | def isStatic = hasFlag(0x00800000) 64 | 65 | def isCaseAccessor = hasFlag(0x01000000) 66 | 67 | def isTrait = hasFlag(0x02000000) 68 | 69 | def isBridge = hasFlag(0x04000000) 70 | 71 | def isAccessor = hasFlag(0x08000000) 72 | 73 | def isSuperAccessor = hasFlag(0x10000000) 74 | 75 | def isParamAccessor = hasFlag(0x20000000) 76 | 77 | def isModuleVar = hasFlag(0x40000000) 78 | 79 | // for variables: is the variable caching a module value 80 | def isMonomorphic = hasFlag(0x40000000) 81 | 82 | // for type symbols: does not have type parameters 83 | def isLazy = hasFlag(0x80000000L) 84 | 85 | // symbol is a lazy val. can't have MUTABLE unless transformed by typer 86 | 87 | def isError = hasFlag(0x100000000L) 88 | 89 | def isOverloaded = hasFlag(0x200000000L) 90 | 91 | def isLifted = hasFlag(0x400000000L) 92 | 93 | def isMixedIn = hasFlag(0x800000000L) 94 | 95 | def isExistential = hasFlag(0x800000000L) 96 | 97 | def isExpandedName = hasFlag(0x1000000000L) 98 | 99 | def isImplementationClass = hasFlag(0x2000000000L) 100 | 101 | def isPreSuper = hasFlag(0x2000000000L) 102 | 103 | } 104 | 105 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/util/scalax/rules/scalasig/ScalaSig.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.util 2 | package scalax 3 | package rules 4 | package scalasig 5 | 6 | import ClassFileParser.{ConstValueIndex, Annotation} 7 | import reflect.internal.pickling.ByteCodecs 8 | import scala.language.postfixOps 9 | import scala.language.implicitConversions 10 | 11 | object ScalaSigParser { 12 | 13 | import CaseClassSigParser.{SCALA_SIG, SCALA_SIG_ANNOTATION, BYTES_VALUE} 14 | 15 | def scalaSigFromAnnotation(classFile: ClassFile): Option[ScalaSig] = { 16 | import classFile._ 17 | 18 | classFile.annotation(SCALA_SIG_ANNOTATION) map { 19 | case Annotation(_, elements) => 20 | val bytesElem = elements.find( 21 | elem => constant(elem.elementNameIndex) == BYTES_VALUE 22 | ).get 23 | val bytes = ((bytesElem.elementValue match {case ConstValueIndex(index) => constantWrapped(index)}) 24 | .asInstanceOf[StringBytesPair].bytes) 25 | val length = ByteCodecs.decode(bytes) 26 | 27 | ScalaSigAttributeParsers.parse(ByteCode(bytes.take(length))) 28 | } 29 | } 30 | 31 | def scalaSigFromAttribute(classFile: ClassFile): Option[ScalaSig] = 32 | classFile.attribute(SCALA_SIG).map(_.byteCode).map(ScalaSigAttributeParsers.parse) 33 | 34 | def parse(classFile: ClassFile): Option[ScalaSig] = { 35 | val scalaSig = scalaSigFromAttribute(classFile) 36 | 37 | scalaSig match { 38 | // No entries in ScalaSig attribute implies that the signature is stored in the annotation 39 | case Some(ScalaSig(_, _, entries)) if entries.length == 0 => 40 | scalaSigFromAnnotation(classFile) 41 | case x => x 42 | } 43 | } 44 | 45 | def parse(clazz: Class[_]): Option[ScalaSig] = { 46 | val byteCode = ByteCode.forClass(clazz) 47 | val classFile = ClassFileParser.parse(byteCode) 48 | 49 | parse(classFile) 50 | } 51 | } 52 | 53 | object ScalaSigAttributeParsers extends ByteCodeReader { 54 | def parse(byteCode: ByteCode) = expect(scalaSig)(byteCode) 55 | 56 | val nat = apply { 57 | def natN(in: ByteCode, 58 | x: Int): Result[ByteCode, Int, Nothing] = in.nextByte match { 59 | case Success(out, b) => { 60 | val y = (x << 7) + (b & 0x7f) 61 | if ((b & 0x80) == 0) Success(out, y) else natN(out, y) 62 | } 63 | case _ => Failure 64 | } 65 | in => natN(in, 0) 66 | } 67 | 68 | val rawBytes = nat >> bytes 69 | val entry = nat ~ rawBytes 70 | val symtab = nat >> entry.times 71 | val scalaSig = nat ~ nat ~ symtab ^~~^ ScalaSig 72 | 73 | val utf8 = read(x => x.fromUTF8StringAndBytes.string) 74 | val longValue = read(_ toLong) 75 | } 76 | 77 | case class ScalaSig(majorVersion: Int, minorVersion: Int, 78 | table: Seq[Int ~ ByteCode]) extends DefaultMemoisable { 79 | 80 | case class Entry(index: Int, entryType: Int, 81 | byteCode: ByteCode) extends DefaultMemoisable { 82 | def scalaSig = ScalaSig.this 83 | 84 | def setByteCode(byteCode: ByteCode) = Entry(index, entryType, byteCode) 85 | } 86 | 87 | def hasEntry(index: Int) = table isDefinedAt index 88 | 89 | def getEntry(index: Int) = { 90 | val entryType ~ byteCode = table(index) 91 | Entry(index, entryType, byteCode) 92 | } 93 | 94 | def parseEntry(index: Int) = applyRule(ScalaSigParsers.parseEntry(ScalaSigEntryParsers.entry)(index)) 95 | 96 | implicit def applyRule[A](parser: ScalaSigParsers.Parser[A]) = ScalaSigParsers.expect(parser)(this) 97 | 98 | override def toString = "ScalaSig version " + majorVersion + "." + minorVersion + { 99 | for (i <- 0 until table.size) yield i + ":\t" + parseEntry(i) // + "\n\t" + getEntry(i) 100 | }.mkString("\n", "\n", "") 101 | 102 | lazy val symbols: Seq[Symbol] = ScalaSigParsers.symbols 103 | 104 | lazy val topLevelClasses: List[ClassSymbol] = ScalaSigParsers.topLevelClasses 105 | lazy val topLevelObjects: List[ObjectSymbol] = ScalaSigParsers.topLevelObjects 106 | } 107 | 108 | object ScalaSigParsers extends RulesWithState with MemoisableRules { 109 | type S = ScalaSig 110 | type Parser[A] = Rule[A, String] 111 | 112 | val symTab = read(_.table) 113 | val size = symTab ^^ (_.size) 114 | 115 | def entry(index: Int) = memo(("entry", index)) { 116 | cond(_ hasEntry index) -~ read(_ getEntry index) >-> { 117 | entry => Success(entry, entry.entryType) 118 | } 119 | } 120 | 121 | def parseEntry[A](parser: ScalaSigEntryParsers.EntryParser[A]) 122 | (index: Int): Parser[A] = 123 | entry(index) -~ parser >> {a => entry => Success(entry.scalaSig, a)} 124 | 125 | def allEntries[A](f: ScalaSigEntryParsers.EntryParser[A]) = size >> { 126 | n => anyOf((0 until n) map parseEntry(f)) 127 | } 128 | 129 | lazy val entries = allEntries(ScalaSigEntryParsers.entry) as "entries" 130 | lazy val symbols = allEntries(ScalaSigEntryParsers.symbol) as "symbols" 131 | lazy val methods = allEntries(ScalaSigEntryParsers.methodSymbol) as "methods" 132 | lazy val attributes = allEntries(ScalaSigEntryParsers.attributeInfo) as "attributes" 133 | 134 | lazy val topLevelClasses = allEntries(ScalaSigEntryParsers.topLevelClass) 135 | lazy val topLevelObjects = allEntries(ScalaSigEntryParsers.topLevelObject) 136 | } 137 | 138 | object ScalaSigEntryParsers extends RulesWithState with MemoisableRules { 139 | 140 | import ScalaSigAttributeParsers.{nat, utf8, longValue} 141 | 142 | type S = ScalaSig#Entry 143 | type EntryParser[A] = Rule[A, String] 144 | 145 | implicit def byteCodeEntryParser[A](rule: ScalaSigAttributeParsers.Parser[A]): EntryParser[A] = apply { 146 | entry => 147 | rule(entry.byteCode) mapOut (entry setByteCode _) 148 | } 149 | 150 | def toEntry[A](index: Int) = apply { 151 | sigEntry => ScalaSigParsers.entry(index)(sigEntry.scalaSig) 152 | } 153 | 154 | def parseEntry[A](parser: EntryParser[A]) 155 | (index: Int) = (toEntry(index) -~ parser) 156 | 157 | implicit def entryType(code: Int) = key filter (_ == code) 158 | 159 | val index = read(_.index) 160 | val key = read(_.entryType) 161 | 162 | lazy val entry: EntryParser[Any] = symbol | typeEntry | literal | name | attributeInfo | annotInfo | children | get 163 | 164 | val ref = byteCodeEntryParser(nat) 165 | 166 | val termName = 1 -~ utf8 167 | val typeName = 2 -~ utf8 168 | 169 | val name = termName | typeName as "name" 170 | 171 | def refTo[A](rule: EntryParser[A]): EntryParser[A] = ref >>& parseEntry(rule) 172 | 173 | lazy val nameRef = refTo(name) 174 | lazy val symbolRef = refTo(symbol) 175 | lazy val typeRef = refTo(typeEntry) 176 | lazy val constantRef = refTo(literal) 177 | 178 | val symbolInfo = nameRef ~ symbolRef ~ nat ~ (symbolRef ?) ~ ref ~ get ^~~~~~^ SymbolInfo 179 | 180 | def symHeader(key: Int) = (key -~ none | (key + 64) -~ nat) 181 | 182 | def symbolEntry(key: Int) = symHeader(key) -~ symbolInfo 183 | 184 | /*************************************************** 185 | * Symbol table attribute format: 186 | * Symtab = nentries_Nat {Entry} 187 | * Entry = 1 TERMNAME len_Nat NameInfo 188 | * | 2 TYPENAME len_Nat NameInfo 189 | * | 3 NONEsym len_Nat 190 | * | 4 TYPEsym len_Nat SymbolInfo 191 | * | 5 ALIASsym len_Nat SymbolInfo 192 | * | 6 CLASSsym len_Nat SymbolInfo [thistype_Ref] 193 | * | 7 MODULEsym len_Nat SymbolInfo 194 | * | 8 VALsym len_Nat [defaultGetter_Ref /* no longer needed*/] SymbolInfo [alias_Ref] 195 | * | 9 EXTref len_Nat name_Ref [owner_Ref] 196 | * | 10 EXTMODCLASSref len_Nat name_Ref [owner_Ref] 197 | * | 11 NOtpe len_Nat 198 | * | 12 NOPREFIXtpe len_Nat 199 | * | 13 THIStpe len_Nat sym_Ref 200 | * | 14 SINGLEtpe len_Nat type_Ref sym_Ref 201 | * | 15 CONSTANTtpe len_Nat constant_Ref 202 | * | 16 TYPEREFtpe len_Nat type_Ref sym_Ref {targ_Ref} 203 | * | 17 TYPEBOUNDStpe len_Nat tpe_Ref tpe_Ref 204 | * | 18 REFINEDtpe len_Nat classsym_Ref {tpe_Ref} 205 | * | 19 CLASSINFOtpe len_Nat classsym_Ref {tpe_Ref} 206 | * | 20 METHODtpe len_Nat tpe_Ref {sym_Ref} 207 | * | 21 POLYTtpe len_Nat tpe_Ref {sym_Ref} 208 | * | 22 IMPLICITMETHODtpe len_Nat tpe_Ref {sym_Ref} /* no longer needed */ 209 | * | 52 SUPERtpe len_Nat tpe_Ref tpe_Ref 210 | * | 24 LITERALunit len_Nat 211 | * | 25 LITERALboolean len_Nat value_Long 212 | * | 26 LITERALbyte len_Nat value_Long 213 | * | 27 LITERALshort len_Nat value_Long 214 | * | 28 LITERALchar len_Nat value_Long 215 | * | 29 LITERALint len_Nat value_Long 216 | * | 30 LITERALlong len_Nat value_Long 217 | * | 31 LITERALfloat len_Nat value_Long 218 | * | 32 LITERALdouble len_Nat value_Long 219 | * | 33 LITERALstring len_Nat name_Ref 220 | * | 34 LITERALnull len_Nat 221 | * | 35 LITERALclass len_Nat tpe_Ref 222 | * | 36 LITERALenum len_Nat sym_Ref 223 | * | 40 SYMANNOT len_Nat sym_Ref AnnotInfoBody 224 | * | 41 CHILDREN len_Nat sym_Ref {sym_Ref} 225 | * | 42 ANNOTATEDtpe len_Nat [sym_Ref /* no longer needed */] tpe_Ref {annotinfo_Ref} 226 | * | 43 ANNOTINFO len_Nat AnnotInfoBody 227 | * | 44 ANNOTARGARRAY len_Nat {constAnnotArg_Ref} 228 | * | 47 DEBRUIJNINDEXtpe len_Nat level_Nat index_Nat 229 | * | 48 EXISTENTIALtpe len_Nat type_Ref {symbol_Ref} 230 | */ 231 | val noSymbol = 3 -^ NoSymbol 232 | val typeSymbol = symbolEntry(4) ^^ TypeSymbol as "typeSymbol" 233 | val aliasSymbol = symbolEntry(5) ^^ AliasSymbol as "alias" 234 | val classSymbol = symbolEntry(6) ~ (ref ?) ^~^ ClassSymbol as "class" 235 | val objectSymbol = symbolEntry(7) ^^ ObjectSymbol as "object" 236 | val methodSymbol = symHeader(8) -~ /*(ref?) -~*/ symbolInfo ~ (ref ?) ^~^ MethodSymbol as "method" 237 | val extRef = 9 -~ nameRef ~ (symbolRef ?) ~ get ^~~^ ExternalSymbol as "extRef" 238 | val extModClassRef = 10 -~ nameRef ~ (symbolRef ?) ~ get ^~~^ ExternalSymbol as "extModClassRef" 239 | 240 | lazy val symbol: EntryParser[Symbol] = oneOf( 241 | noSymbol, 242 | typeSymbol, 243 | aliasSymbol, 244 | classSymbol, 245 | objectSymbol, 246 | methodSymbol, 247 | extRef, 248 | extModClassRef 249 | ) as "symbol" 250 | 251 | val classSymRef = refTo(classSymbol) 252 | val attribTreeRef = ref 253 | val typeLevel = nat 254 | val typeIndex = nat 255 | 256 | lazy val typeEntry: EntryParser[Type] = oneOf( 257 | 11 -^ NoType, 258 | 12 -^ NoPrefixType, 259 | 13 -~ symbolRef ^^ ThisType, 260 | 14 -~ typeRef ~ symbolRef ^~^ SingleType, 261 | 15 -~ constantRef ^^ ConstantType, 262 | 16 -~ typeRef ~ symbolRef ~ (typeRef *) ^~~^ TypeRefType, 263 | 17 -~ typeRef ~ typeRef ^~^ TypeBoundsType, 264 | 18 -~ classSymRef ~ (typeRef *) ^~^ RefinedType, 265 | 19 -~ symbolRef ~ (typeRef *) ^~^ ClassInfoType, 266 | 20 -~ typeRef ~ (symbolRef *) ^~^ MethodType, 267 | 21 -~ typeRef ~ (refTo(typeSymbol) +) ^~^ PolyType, // TODO: make future safe for past by doing the same transformation as in the full unpickler in case we're reading pre-2.9 classfiles 268 | 21 -~ typeRef ^^ NullaryMethodType, 269 | 22 -~ typeRef ~ (symbolRef *) ^~^ ImplicitMethodType, 270 | 42 -~ typeRef ~ (attribTreeRef *) ^~^ AnnotatedType, 271 | 51 -~ typeRef ~ symbolRef ~ (attribTreeRef *) ^~~^ AnnotatedWithSelfType, 272 | 47 -~ typeLevel ~ typeIndex ^~^ DeBruijnIndexType, 273 | 48 -~ typeRef ~ (symbolRef *) ^~^ ExistentialType 274 | ) as "type" 275 | 276 | lazy val literal = oneOf( 277 | 24 -^ (Unit), 278 | 25 -~ longValue ^^ (_ != 0L), 279 | 26 -~ longValue ^^ (_.toByte), 280 | 27 -~ longValue ^^ (_.toShort), 281 | 28 -~ longValue ^^ (_.toChar), 282 | 29 -~ longValue ^^ (_.toInt), 283 | 30 -~ longValue ^^ (_.toLong), 284 | 31 -~ longValue ^^ (l => java.lang.Float.intBitsToFloat(l.toInt)), 285 | 32 -~ longValue ^^ (java.lang.Double.longBitsToDouble), 286 | 33 -~ nameRef, 287 | 34 -^ null, 288 | 35 -~ typeRef 289 | ) 290 | 291 | lazy val attributeInfo = 40 -~ symbolRef ~ typeRef ~ (constantRef ?) ~ (nameRef ~ constantRef *) ^~~~^ AttributeInfo 292 | // sym_Ref info_Ref {constant_Ref} {nameRef constantRef} 293 | lazy val children = 41 -~ (nat *) ^^ Children 294 | //sym_Ref {sym_Ref} 295 | lazy val annotInfo = 43 -~ (nat *) ^^ AnnotInfo 296 | // attarg_Ref {constant_Ref attarg_Ref} 297 | 298 | lazy val topLevelClass = classSymbol filter isTopLevelClass 299 | lazy val topLevelObject = objectSymbol filter isTopLevel 300 | 301 | def isTopLevel(symbol: Symbol) = symbol.parent match { 302 | case Some(ext: ExternalSymbol) => true 303 | case _ => false 304 | } 305 | 306 | def isTopLevelClass(symbol: Symbol) = !symbol.isModule && isTopLevel(symbol) 307 | } 308 | 309 | case class AttributeInfo(symbol: Symbol, typeRef: Type, value: Option[Any], 310 | values: Seq[String ~ Any]) 311 | 312 | // sym_Ref info_Ref {constant_Ref} {nameRef constantRef} 313 | case class Children(symbolRefs: Seq[Int]) 314 | 315 | //sym_Ref {sym_Ref} 316 | 317 | case class AnnotInfo(refs: Seq[Int]) 318 | 319 | // attarg_Ref {constant_Ref attarg_Ref} 320 | 321 | /*************************************************** 322 | * | 49 TREE len_Nat 1 EMPTYtree 323 | * | 49 TREE len_Nat 2 PACKAGEtree type_Ref sym_Ref mods_Ref name_Ref {tree_Ref} 324 | * | 49 TREE len_Nat 3 CLASStree type_Ref sym_Ref mods_Ref name_Ref tree_Ref {tree_Ref} 325 | * | 49 TREE len_Nat 4 MODULEtree type_Ref sym_Ref mods_Ref name_Ref tree_Ref 326 | * | 49 TREE len_Nat 5 VALDEFtree type_Ref sym_Ref mods_Ref name_Ref tree_Ref tree_Ref 327 | * | 49 TREE len_Nat 6 DEFDEFtree type_Ref sym_Ref mods_Ref name_Ref numtparams_Nat {tree_Ref} numparamss_Nat {numparams_Nat {tree_Ref}} tree_Ref tree_Ref 328 | * | 49 TREE len_Nat 7 TYPEDEFtree type_Ref sym_Ref mods_Ref name_Ref tree_Ref {tree_Ref} 329 | * | 49 TREE len_Nat 8 LABELtree type_Ref sym_Ref tree_Ref {tree_Ref} 330 | * | 49 TREE len_Nat 9 IMPORTtree type_Ref sym_Ref tree_Ref {name_Ref name_Ref} 331 | * | 49 TREE len_Nat 11 DOCDEFtree type_Ref sym_Ref string_Ref tree_Ref 332 | * | 49 TREE len_Nat 12 TEMPLATEtree type_Ref sym_Ref numparents_Nat {tree_Ref} tree_Ref {tree_Ref} 333 | * | 49 TREE len_Nat 13 BLOCKtree type_Ref tree_Ref {tree_Ref} 334 | * | 49 TREE len_Nat 14 CASEtree type_Ref tree_Ref tree_Ref tree_Ref 335 | * | 49 TREE len_Nat 15 SEQUENCEtree type_Ref {tree_Ref} 336 | * | 49 TREE len_Nat 16 ALTERNATIVEtree type_Ref {tree_Ref} 337 | * | 49 TREE len_Nat 17 STARtree type_Ref {tree_Ref} 338 | * | 49 TREE len_Nat 18 BINDtree type_Ref sym_Ref name_Ref tree_Ref 339 | * | 49 TREE len_Nat 19 UNAPPLYtree type_Ref tree_Ref {tree_Ref} 340 | * | 49 TREE len_Nat 20 ARRAYVALUEtree type_Ref tree_Ref {tree_Ref} 341 | * | 49 TREE len_Nat 21 FUNCTIONtree type_Ref sym_Ref tree_Ref {tree_Ref} 342 | * | 49 TREE len_Nat 22 ASSIGNtree type_Ref tree_Ref tree_Ref 343 | * | 49 TREE len_Nat 23 IFtree type_Ref tree_Ref tree_Ref tree_Ref 344 | * | 49 TREE len_Nat 24 MATCHtree type_Ref tree_Ref {tree_Ref} 345 | * | 49 TREE len_Nat 25 RETURNtree type_Ref sym_Ref tree_Ref 346 | * | 49 TREE len_Nat 26 TREtree type_Ref tree_Ref tree_Ref {tree_Ref} 347 | * | 49 TREE len_Nat 27 THROWtree type_Ref tree_Ref 348 | * | 49 TREE len_Nat 28 NEWtree type_Ref tree_Ref 349 | * | 49 TREE len_Nat 29 TYPEDtree type_Ref tree_Ref tree_Ref 350 | * | 49 TREE len_Nat 30 TYPEAPPLYtree type_Ref tree_Ref {tree_Ref} 351 | * | 49 TREE len_Nat 31 APPLYtree type_Ref tree_Ref {tree_Ref} 352 | * | 49 TREE len_Nat 32 APPLYDYNAMICtree type_Ref sym_Ref tree_Ref {tree_Ref} 353 | * | 49 TREE len_Nat 33 SUPERtree type_Ref sym_Ref tree_Ref name_Ref 354 | * | 49 TREE len_Nat 34 THIStree type_Ref sym_Ref name_Ref 355 | * | 49 TREE len_Nat 35 SELECTtree type_Ref sym_Ref tree_Ref name_Ref 356 | * | 49 TREE len_Nat 36 IDENTtree type_Ref sym_Ref name_Ref 357 | * | 49 TREE len_Nat 37 LITERALtree type_Ref constant_Ref 358 | * | 49 TREE len_Nat 38 TYPEtree type_Ref 359 | * | 49 TREE len_Nat 39 ANNOTATEDtree type_Ref tree_Ref tree_Ref 360 | * | 49 TREE len_Nat 40 SINGLETONTYPEtree type_Ref tree_Ref 361 | * | 49 TREE len_Nat 41 SELECTFROMTYPEtree type_Ref tree_Ref name_Ref 362 | * | 49 TREE len_Nat 42 COMPOUNDTYPEtree type_Ref tree_Ref 363 | * | 49 TREE len_Nat 43 APPLIEDTYPEtree type_Ref tree_Ref {tree_Ref} 364 | * | 49 TREE len_Nat 44 TYPEBOUNDStree type_Ref tree_Ref tree_Ref 365 | * | 49 TREE len_Nat 45 EXISTENTIALTYPEtree type_Ref tree_Ref {tree_Ref} 366 | * | 50 MODIFIERS len_Nat flags_Long privateWithin_Ref 367 | * SymbolInfo = name_Ref owner_Ref flags_LongNat [privateWithin_Ref] info_Ref 368 | * NameInfo = 369 | * NumInfo = 370 | * Ref = Nat 371 | * AnnotInfoBody = info_Ref {annotArg_Ref} {name_Ref constAnnotArg_Ref} 372 | * AnnotArg = Tree | Constant 373 | * ConstAnnotArg = Constant | AnnotInfo | AnnotArgArray 374 | * 375 | * len is remaining length after `len'. 376 | */ 377 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/util/scalax/rules/scalasig/Symbol.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.util 2 | package scalax 3 | package rules 4 | package scalasig 5 | 6 | import ScalaSigEntryParsers._ 7 | 8 | trait Symbol extends Flags { 9 | def name: String 10 | 11 | def parent: Option[Symbol] 12 | 13 | def children: Seq[Symbol] 14 | 15 | def path: String = parent.map(_.path + ".").getOrElse("") + name 16 | } 17 | 18 | case object NoSymbol extends Symbol { 19 | def name = "" 20 | 21 | def parent = None 22 | 23 | def hasFlag(flag: Long) = false 24 | 25 | def children = Nil 26 | } 27 | 28 | abstract class ScalaSigSymbol extends Symbol { 29 | def applyRule[A](rule: EntryParser[A]): A = expect(rule)(entry) 30 | 31 | def applyScalaSigRule[A](rule: ScalaSigParsers.Parser[A]) = ScalaSigParsers.expect(rule)(entry.scalaSig) 32 | 33 | def entry: ScalaSig#Entry 34 | 35 | def index = entry.index 36 | 37 | lazy val children: Seq[Symbol] = applyScalaSigRule(ScalaSigParsers.symbols) filter (_.parent == Some(this)) 38 | lazy val attributes: Seq[AttributeInfo] = applyScalaSigRule(ScalaSigParsers.attributes) filter (_.symbol == this) 39 | } 40 | 41 | case class ExternalSymbol(name: String, parent: Option[Symbol], 42 | entry: ScalaSig#Entry) extends ScalaSigSymbol { 43 | override def toString = path 44 | 45 | def hasFlag(flag: Long) = false 46 | } 47 | 48 | case class SymbolInfo(name: String, owner: Symbol, flags: Int, 49 | privateWithin: Option[AnyRef], info: Int, 50 | entry: ScalaSig#Entry) { 51 | def symbolString(any: AnyRef) = any match { 52 | case sym: SymbolInfoSymbol => sym.index.toString 53 | case other => other.toString 54 | } 55 | 56 | override def toString = name + ", owner=" + symbolString(owner) + ", flags=" + flags.toHexString + ", info=" + info + (privateWithin match { 57 | case Some(any) => ", privateWithin=" + symbolString(any) 58 | case None => " " 59 | }) 60 | } 61 | 62 | abstract class SymbolInfoSymbol extends ScalaSigSymbol { 63 | def symbolInfo: SymbolInfo 64 | 65 | def entry = symbolInfo.entry 66 | 67 | def name = symbolInfo.name 68 | 69 | def parent = Some(symbolInfo.owner) 70 | 71 | def hasFlag(flag: Long) = (symbolInfo.flags & flag) != 0L 72 | 73 | lazy val infoType = applyRule(parseEntry(typeEntry)(symbolInfo.info)) 74 | } 75 | 76 | case class TypeSymbol(symbolInfo: SymbolInfo) extends SymbolInfoSymbol { 77 | override def path = name 78 | } 79 | 80 | case class AliasSymbol(symbolInfo: SymbolInfo) extends SymbolInfoSymbol { 81 | override def path = name 82 | } 83 | 84 | case class ClassSymbol(symbolInfo: SymbolInfo, 85 | thisTypeRef: Option[Int]) extends SymbolInfoSymbol { 86 | lazy val selfType = thisTypeRef.map {(x: Int) => applyRule(parseEntry(typeEntry)(x))} 87 | } 88 | 89 | case class ObjectSymbol(symbolInfo: SymbolInfo) extends SymbolInfoSymbol 90 | 91 | case class MethodSymbol(symbolInfo: SymbolInfo, 92 | aliasRef: Option[Int]) extends SymbolInfoSymbol 93 | 94 | -------------------------------------------------------------------------------- /src/main/scala/com/ansvia/graph/util/scalax/rules/scalasig/Type.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph.util 2 | package scalax 3 | package rules 4 | package scalasig 5 | 6 | abstract class Type 7 | 8 | case object NoType extends Type 9 | 10 | case object NoPrefixType extends Type 11 | 12 | case class ThisType(symbol: Symbol) extends Type 13 | 14 | case class SingleType(typeRef: Type, symbol: Symbol) extends Type 15 | 16 | case class ConstantType(constant: Any) extends Type 17 | 18 | case class TypeRefType(prefix: Type, symbol: Symbol, 19 | typeArgs: Seq[Type]) extends Type 20 | 21 | case class TypeBoundsType(lower: Type, upper: Type) extends Type 22 | 23 | case class RefinedType(classSym: Symbol, typeRefs: List[Type]) extends Type 24 | 25 | case class ClassInfoType(symbol: Symbol, typeRefs: Seq[Type]) extends Type 26 | 27 | case class ClassInfoTypeWithCons(symbol: Symbol, typeRefs: Seq[Type], 28 | cons: String) extends Type 29 | 30 | case class MethodType(resultType: Type, paramSymbols: Seq[Symbol]) extends Type 31 | 32 | case class NullaryMethodType(resultType: Type) extends Type 33 | 34 | case class PolyType(typeRef: Type, symbols: Seq[TypeSymbol]) extends Type 35 | 36 | case class PolyTypeWithCons(typeRef: Type, symbols: Seq[TypeSymbol], 37 | cons: String) extends Type 38 | 39 | case class ImplicitMethodType(resultType: Type, 40 | paramSymbols: Seq[Symbol]) extends Type 41 | 42 | case class AnnotatedType(typeRef: Type, attribTreeRefs: List[Int]) extends Type 43 | 44 | case class AnnotatedWithSelfType(typeRef: Type, symbol: Symbol, 45 | attribTreeRefs: List[Int]) extends Type 46 | 47 | case class DeBruijnIndexType(typeLevel: Int, typeIndex: Int) extends Type 48 | 49 | case class ExistentialType(typeRef: Type, symbols: Seq[Symbol]) extends Type 50 | -------------------------------------------------------------------------------- /src/test/scala/com/ansvia/graph/AbstractIDGetterSpec.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import org.specs2.mutable.Specification 4 | import com.ansvia.graph.testing.model.ModelUsingAbstractIDGetter 5 | import org.specs2.specification.Scope 6 | import com.tinkerpop.blueprints.impls.tg.TinkerGraphFactory 7 | 8 | /** 9 | * Author: robin 10 | * Date: 11/4/13 11 | * Time: 1:19 PM 12 | * 13 | */ 14 | class AbstractIDGetterSpec extends Specification { 15 | args(sequential=true) 16 | 17 | import com.ansvia.graph.BlueprintsWrapper._ 18 | 19 | implicit val db = TinkerGraphFactory.createTinkerGraph() 20 | 21 | class Ctx extends Scope { 22 | val v = ModelUsingAbstractIDGetter("zzz").save() 23 | // db.commit() 24 | } 25 | 26 | "Abstract ID getter" should { 27 | "using it" in new Ctx { 28 | v.toCC[ModelUsingAbstractIDGetter].get must beAnInstanceOf[ModelUsingAbstractIDGetter] 29 | } 30 | } 31 | 32 | step { 33 | db.shutdown() 34 | } 35 | 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/test/scala/com/ansvia/graph/BlueprintsTransactSpec.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import org.specs2.mutable.Specification 4 | import com.tinkerpop.blueprints.Edge 5 | 6 | /** 7 | * Copyright (C) 2011-2012 Ansvia Inc. 8 | * User: robin 9 | * Date: 12/31/12 10 | * Time: 2:12 AM 11 | * 12 | */ 13 | class BlueprintsTransactSpec extends Specification with TitanBackedDb { 14 | 15 | 16 | import BlueprintsWrapper._ 17 | 18 | sequential 19 | 20 | val data = Map( 21 | "hercules" -> "demigod", 22 | "nemean" -> "monster", 23 | "hydra" -> "monster", 24 | "cerberus" -> "monster" 25 | ) 26 | 27 | val vertices = for((name, kind) <- data) yield { 28 | val vertex = db.addVertex(null) 29 | vertex.setProperty("name", name) 30 | vertex.setProperty("kind", kind) 31 | (name, vertex) 32 | } 33 | 34 | val hercules = vertices("hercules") 35 | 36 | // test with property mutator 37 | 38 | val edge1 = hercules -->"battled"--> vertices("nemean") <() 39 | val edge2 = hercules -->"battled"--> vertices("hydra") <() 40 | val edge3 = hercules -->"battled"--> vertices("cerberus") <() 41 | 42 | edge1.set("time", 1) 43 | edge2.set("time", 2) 44 | edge3.set("time", 12) 45 | 46 | var edge4:Edge = null 47 | 48 | val edge = hercules --> "test" --> vertices("cerberus") <() 49 | edge.set("timestamp", 1) 50 | 51 | // test success transaction 52 | transact { 53 | edge1.set("win", true) 54 | 55 | // test set datetime 56 | val edge = hercules --> "test" --> vertices("cerberus") <() 57 | edge.set("timestamp", 1) 58 | 59 | edge4 = edge 60 | } 61 | 62 | // test fail transaction 63 | try { 64 | transact { 65 | edge2.set("win", true) 66 | edge3.set("win", false) 67 | throw new Exception("oops error man!") 68 | } 69 | }catch{ 70 | case e:Exception => 71 | }finally { 72 | 73 | } 74 | 75 | "Transaction wrapper" should { 76 | "handle success transaction correctly" in { 77 | edge1.has("win") must beTrue 78 | } 79 | "handle failed transaction correctly #1" in { 80 | edge2.has("win") must beFalse 81 | } 82 | "handle failed transaction correctly #2" in { 83 | edge3.has("win") must beFalse 84 | } 85 | "create edge and set inside transact" in { 86 | edge4.has("timestamp") must beTrue 87 | } 88 | "create edge and set inside transact return expected data" in { 89 | edge4.get[Int]("timestamp") must beEqualTo(Some(1)) 90 | } 91 | 92 | } 93 | 94 | step { 95 | db.shutdown() 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/test/scala/com/ansvia/graph/BlueprintsWrapperSpec.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import org.specs2.mutable.Specification 4 | import com.tinkerpop.blueprints.impls.tg.TinkerGraphFactory 5 | import com.tinkerpop.blueprints.{Edge, Vertex, Direction} 6 | import java.lang.Iterable 7 | import com.tinkerpop.gremlin.java.GremlinPipeline 8 | import org.specs2.execute.Skipped 9 | import scala.language.reflectiveCalls 10 | 11 | /** 12 | * Copyright (C) 2011-2012 Ansvia Inc. 13 | * Author: robin 14 | * 15 | * This example simulating gods graph example from 16 | * https://github.com/thinkaurelius/titan/wiki/Getting-Started 17 | */ 18 | class BlueprintsWrapperSpec extends Specification { 19 | 20 | import BlueprintsWrapper._ 21 | import scala.collection.JavaConversions._ 22 | import com.ansvia.graph.gremlin._ 23 | 24 | // for test case we using simple in-memory tinkergraph db 25 | // in reality you can use any graph db that support 26 | // tinkerpop blueprints stack 27 | implicit val db = TinkerGraphFactory.createTinkerGraph() 28 | 29 | val data = Map( 30 | "hercules" -> "demigod", 31 | "jupiter" -> "god", 32 | "alcmene" -> "human", 33 | "nemean" -> "monster", 34 | "hydra" -> "monster", 35 | "cerberus" -> "monster", 36 | "pluto" -> "god", 37 | "neptune" -> "god", 38 | "sky" -> "location", 39 | "sea" -> "location", 40 | "tartarus" -> "location", 41 | "saturn" -> "titan" 42 | ) 43 | 44 | val vertices = for((name, kind) <- data) yield { 45 | val vertex = db.addVertex(null) 46 | vertex.setProperty("name", name) 47 | vertex.setProperty("kind", kind) 48 | (name, vertex) 49 | } 50 | 51 | vertices("hercules") --> "father" --> vertices("jupiter") --> "father" --> vertices("saturn") 52 | vertices("hercules") --> "mother" --> vertices("alcmene") 53 | vertices("jupiter") <--> "brother" <--> vertices("pluto") 54 | vertices("jupiter") <--> "brother" <--> vertices("neptune") <--> "brother" <--> vertices("pluto") 55 | 56 | val sea = vertices("sea") 57 | val jupiter = vertices("jupiter") 58 | val sky = vertices("sky") 59 | 60 | vertices("neptune") --> "lives" --> sea 61 | sky <-- "lives" <-- jupiter 62 | vertices("pluto") --> "lives" --> vertices("tartarus") <-- "lives" <-- vertices("cerberus") <-- "pet" <-- vertices("pluto") 63 | 64 | sequential 65 | 66 | "Blueprints vertex wrapper" should { 67 | "create edge out" in { 68 | val father = vertices("hercules").getVertices(Direction.OUT,"father").iterator().next() 69 | father.getProperty[String]("name") must beEqualTo("jupiter") 70 | } 71 | "handle defined field" in { 72 | vertices("hercules").get[String]("kind").isDefined must beTrue 73 | } 74 | "handle undefined field" in { 75 | vertices("hercules").get[String]("not_exists").isDefined must beFalse 76 | } 77 | "able to using getter or else exists" in { 78 | vertices("hercules").getOrElse[String]("kind", "") must beEqualTo("demigod") 79 | } 80 | "able to using getter or else not-exists" in { 81 | vertices("hercules").getOrElse[String]("sex", "male") must beEqualTo("male") 82 | } 83 | "create edge out using getter" in { 84 | val father = vertices("hercules").getVertices(Direction.OUT,"father").iterator().next() 85 | father.get[String]("name").get must beEqualTo("jupiter") 86 | } 87 | "create edge in" in { 88 | val father = vertices("jupiter").getVertices(Direction.IN,"father").iterator().next() 89 | father.get[String]("name").get must beEqualTo("hercules") 90 | } 91 | "create mutual connection" in { 92 | val it = vertices("jupiter").getEdges(Direction.BOTH, "brother").iterator() 93 | val edge1 = it.next() 94 | val edge2 = it.next() 95 | edge1.getLabel must beEqualTo(edge2.getLabel) 96 | } 97 | "using mutual wrapper #1" in { 98 | val vx = vertices("jupiter").mutual("brother") 99 | vx.iterator().next().get[String]("name").get must beOneOf("pluto", "neptune") 100 | } 101 | "using mutual wrapper #2" in { 102 | val vx: Iterable[Vertex] = vertices("pluto").mutual("brother") 103 | vx.iterator().next().get[String]("name").get must beOneOf("jupiter", "neptune") 104 | } 105 | "dump iterable vertices" in { 106 | vertices("hercules").getEdges(Direction.OUT).printDump("name") 107 | true must beTrue 108 | } 109 | "long right edges" in { 110 | val edge = vertices("pluto").getEdges(Direction.OUT, "pet").iterator().next() 111 | edge.getVertex(Direction.IN).getOrElse[String]("name", "") must beEqualTo("cerberus") 112 | } 113 | } 114 | 115 | val hercules = vertices("hercules") 116 | 117 | // test with property mutator 118 | 119 | val edge1 = hercules -->"battled"--> vertices("nemean") < 120 | val edge2 = hercules -->"battled"--> vertices("hydra") < 121 | val edge3 = hercules -->"battled"--> vertices("cerberus") < 122 | 123 | edge1.set("time", 1) 124 | edge2.set("time", 2) 125 | edge3.set("time", 12) 126 | 127 | // val multiFilterResult = hercules.pipe.outE("battled").wrap.filter { 128 | // _.getOrElse[Int]("time", 0).toString.toInt < 12 129 | // }.wrap.filter { 130 | // _.getOrElse[Int]("time", 0).toString.toInt > 1 131 | // }.toList 132 | 133 | "Blueprints Gremlin pipe wrapper" should { 134 | "do simple query" in { 135 | val vx = vertices("tartarus").pipe.in("lives") 136 | val lst = vx.printDumpGetList("they are lives in tartarus:", "name") 137 | lst.length must beEqualTo(2) 138 | } 139 | "iterate and map both vertices" in { 140 | val jupiterBrother = jupiter.pipe.out("brother").iterator() 141 | .toList.map( v => v.getOrElse[String]("name", "") ) 142 | 143 | println("jupiterBrother: ") 144 | jupiterBrother.foreach(b => println(" + " + b)) 145 | 146 | jupiterBrother.length == 2 && 147 | jupiterBrother.contains("pluto") && 148 | jupiterBrother.contains("neptune") must beTrue 149 | } 150 | "get count of out edges" in { 151 | hercules.pipe.outE("battled").count() must beEqualTo(3) 152 | } 153 | // "able to using currying filter / gremlin pipe wrapper" in { 154 | // val vx = hercules.pipe.outE("battled").filter { (edge:Edge) => 155 | // edge.getOrElse[Int]("time", 0).toString.toInt > 5 156 | // } 157 | // vx.iterator().next().getOrElse[Int]("time", 0) must beEqualTo(12) 158 | // } 159 | // "use multi filtering #1" in { 160 | // multiFilterResult.length must beEqualTo(1) 161 | // } 162 | // "use multi filtering #2 must not contains nemean" in { 163 | // multiFilterResult.contains(edge1) must beFalse 164 | // } 165 | // "use multi filtering #3 must contains hydra" in { 166 | // multiFilterResult.contains(edge2) must beTrue 167 | // } 168 | // "use multi filtering #4 must not contains cerberus" in { 169 | // multiFilterResult.contains(edge3) must beFalse 170 | // } 171 | // deprecated 172 | // "use inFirst" in { 173 | // sea.pipe.inFirst("lives").get.getOrElse("name", "") must beEqualTo("neptune") 174 | // } 175 | // "not throwing any error when inFirst didn't returning any values" in { 176 | // sea.pipe.inFirst("destroy").isEmpty must beTrue 177 | // } 178 | // "use outFirst" in { 179 | // hercules.pipe.outFirst("battled").get.getOrElse("name", "") must beEqualTo("nemean") 180 | // } 181 | // "not throwing any error when outFirst didn't returning any values" in { 182 | // hercules.pipe.outFirst("destroy").isEmpty must beTrue 183 | // } 184 | "using sorting method" in { 185 | val orderedMonsters = hercules.pipe.out("battled").order { (v1:Vertex, v2:Vertex) => 186 | v1.get[String]("name").get.compareTo(v2.get[String]("name").get) 187 | }.iterator().map(_.getProperty[String]("name")).toArray 188 | orderedMonsters(0) must beEqualTo("cerberus") 189 | orderedMonsters(1) must beEqualTo("hydra") 190 | orderedMonsters(2) must beEqualTo("nemean") 191 | } 192 | // "using reload in edge" in { 193 | // edge1.set("time", 2) 194 | // db.asInstanceOf[TransactionalGraph].stopTransaction(TransactionalGraph.Conclusion.FAILURE) 195 | // edge1.reload() 196 | // edge1.get[Int]("time").get must beEqualTo(1) 197 | // } 198 | // "using reload in vertex" in { 199 | // hercules.set("kind", "human") 200 | // hercules.reload() 201 | // hercules.get[String]("kind").get must beEqualTo("demigod") 202 | // } 203 | } 204 | 205 | 206 | } 207 | -------------------------------------------------------------------------------- /src/test/scala/com/ansvia/graph/DbObjectComplexSpec.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import org.specs2.Specification 4 | import com.tinkerpop.blueprints.impls.tg.TinkerGraphFactory 5 | 6 | /** 7 | * Author: robin 8 | * 9 | */ 10 | class DbObjectComplexSpec extends Specification { 11 | 12 | import com.ansvia.graph.testing.model._ 13 | import BlueprintsWrapper._ 14 | 15 | def is = s2"""$sequential ^ 16 | "Complex DbObject inheritance should 17 | get level 1 var ${cdbo.getLevel1Var} 18 | get level 1b var ${cdbo.getLevel1bVar} 19 | get level 2 var ${cdbo.getLevel2Var} 20 | get level 2b var ${cdbo.getLevel2bVar} 21 | get level 2c var ${cdbo.getLevel2cVar} 22 | """ 23 | 24 | object cdbo { 25 | implicit val db = TinkerGraphFactory.createTinkerGraph() 26 | 27 | val complexDraft = Complex("complex1") 28 | complexDraft.me = "complex" 29 | complexDraft.a = 1 30 | complexDraft.b = 2 31 | complexDraft.c = 3 32 | val complex = complexDraft.save().toCC[Complex].get 33 | 34 | def close(){ 35 | db.shutdown() 36 | } 37 | 38 | def getLevel1Var = complex.x must beEqualTo("complex1") 39 | def getLevel1bVar = complex.me must beEqualTo("complex") 40 | def getLevel2Var = complex.a must beEqualTo(1) 41 | def getLevel2bVar = complex.b must beEqualTo(2) 42 | def getLevel2cVar = complex.c must beEqualTo(3) 43 | 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/test/scala/com/ansvia/graph/DbObjectOptionalSpec.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import com.tinkerpop.blueprints.impls.tg.TinkerGraphFactory 4 | import org.specs2.Specification 5 | import org.specs2.specification.Step 6 | 7 | /** 8 | * Test verifying if optional values are stored correctly. 9 | * Author: pdolega 10 | */ 11 | class DbObjectOptionalSpec extends Specification { 12 | 13 | import com.ansvia.graph.testing.model._ 14 | 15 | def is = s2""" 16 | This is specification to check Option type as the db object attribute. 17 | 18 | DbObject with optional attribute should 19 | save NONE property ${trees.getOptionalEntityNone} 20 | save SOME property ${trees.getOptionalWithValue} 21 | reload Option attribute ${trees.reload} 22 | overwrite with None ${trees.overwriteWithNone} 23 | ${Step(trees.close())} 24 | """ 25 | 26 | 27 | object trees { 28 | import com.ansvia.graph.BlueprintsWrapper._ 29 | 30 | implicit val db = TinkerGraphFactory.createTinkerGraph() 31 | 32 | def close(){ 33 | db.shutdown() 34 | } 35 | 36 | def getOptionalEntityNone = { 37 | val o = IdSimpleDboOption(a="d", b="f") 38 | o.save() 39 | 40 | val a: String = o.getVertex.getProperty("a") 41 | val b: String = o.getVertex.getProperty("b") 42 | 43 | a must be equalTo("a") 44 | b must be equalTo("b") 45 | 46 | val optNull: String = o.getVertex.getProperty("c") 47 | 48 | optNull must beNull 49 | } 50 | 51 | def getOptionalWithValue = { 52 | val o = IdSimpleDboOption(a="d", b="f", opt=Some("opt")) 53 | o.save() 54 | 55 | val opt: String = o.getVertex.getProperty("opt") 56 | opt must beEqualTo("opt") 57 | } 58 | 59 | def reload = { 60 | val noneDbo = IdSimpleDboVarOption(opt=None, a="d", b="f") 61 | val noneDboSave = noneDbo.save().toCC[IdSimpleDboVarOption].get 62 | 63 | noneDboSave.opt = Some("test") 64 | val dboReload1 = noneDboSave.reload() 65 | dboReload1.opt must beNone 66 | 67 | val someDbo = IdSimpleDboVarOption(opt=Some("test"), a="d", b="f") 68 | val someDboSave = someDbo.save().toCC[IdSimpleDboVarOption].get 69 | 70 | someDboSave.opt = None 71 | val dboReload2 = someDboSave.reload() 72 | dboReload2.opt must beEqualTo(Some("test")) 73 | } 74 | 75 | def overwriteWithNone = { 76 | val someDbo = IdSimpleDboVarOption(opt=Some("some value"), a="d", b="f") 77 | val someDboSave = someDbo.save().toCC[IdSimpleDboVarOption].get 78 | someDboSave.opt must beEqualTo(Some("some value")) 79 | 80 | someDboSave.opt = None 81 | val noneDboSave = someDboSave.save().toCC[IdSimpleDboVarOption].get 82 | noneDboSave.opt must beEqualTo(None) 83 | 84 | val reloadedNoneDbo = noneDboSave.reload() 85 | reloadedNoneDbo.opt must beEqualTo(None) 86 | } 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/test/scala/com/ansvia/graph/DbObjectSimpleSpec.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import com.ansvia.graph.Exc.NotBoundException 4 | import com.tinkerpop.blueprints.impls.tg.TinkerGraphFactory 5 | import org.specs2.Specification 6 | import org.specs2.specification.Step 7 | 8 | /** 9 | * Author: robin 10 | * Date: 1/16/13 11 | * Time: 12:58 AM 12 | * 13 | */ 14 | class DbObjectSimpleSpec extends Specification { 15 | 16 | import com.ansvia.graph.BlueprintsWrapper._ 17 | import com.ansvia.graph.testing.model._ 18 | 19 | 20 | def is = 21 | s2""" $sequential 22 | This specification is to test basic DbObject functionality. 23 | 24 | DbObject non tx should 25 | able to reload ${treesNonTx.reload} 26 | change property and save ${treesNonTx.changeProperty} 27 | use getId via IDGetter ${treesNonTx.useGetId} 28 | able to delete ${treesNonTx.getIdAfterSave} 29 | ${Step(treesNonTx.close())} 30 | 31 | DbObject tx should 32 | able to reload ${treesTx.reload} 33 | change property and save ${treesTx.changeProperty} 34 | use getId via IDGetter ${treesTx.useGetId} 35 | able to delete ${treesTx.getIdAfterSave} 36 | ${Step(treesTx.close())} 37 | """ 38 | 39 | object treesNonTx { 40 | implicit val db = TinkerGraphFactory.createTinkerGraph() 41 | 42 | val dboDraft = SimpleDbo("a", "b") 43 | val dbo = dboDraft.save().toCC[SimpleDbo].get 44 | 45 | val dbo2Draft = IdSimpleDbo("b", "c") 46 | val dbo2 = dbo2Draft.save().toCC[IdSimpleDbo].get 47 | 48 | def close(){ 49 | db.shutdown() 50 | } 51 | 52 | def reload = { 53 | dbo.b = "c" 54 | val dbo2 = dbo.reload() 55 | dbo2.a must beEqualTo("a") and(dbo2.b must beEqualTo("b")) and (dbo2.b must not equalTo "c") 56 | } 57 | 58 | def changeProperty = { 59 | dbo.b = "d" 60 | dbo.save() 61 | db.getVertex(dbo.getId).toCC[SimpleDbo].get.b must beEqualTo("d") 62 | } 63 | 64 | def useGetId = { 65 | val v = db.getVertex(dbo.getVertex.getId) 66 | v.getId must beEqualTo(dbo.getId) 67 | } 68 | 69 | def useGetIdDbObject = { 70 | val d = db.getVertex(dbo2.getVertex.getId).toCC[IdSimpleDbo].get 71 | d.getId must beEqualTo(dbo2.getId) 72 | } 73 | 74 | def delete = { 75 | dbo.delete() 76 | dbo.isSaved must beEqualTo(false) 77 | } 78 | 79 | def getIdAfterSave = { 80 | val o = SimpleDbo("d", "f") 81 | o.save() 82 | 83 | (o.getId must not be throwAn[NotBoundException]) and 84 | (o.getId must not be equalTo(null)) and 85 | (o.isSaved must beTrue) 86 | } 87 | } 88 | 89 | object treesTx extends TitanBackedDb { 90 | 91 | val dboDraft = SimpleDboLong("a", "b") 92 | val dbo = transact { 93 | dboDraft.save().toCC[SimpleDboLong].get 94 | } 95 | 96 | 97 | val dbo2Draft = IdSimpleDboLong("b", "c") 98 | val dbo2 = transact { 99 | dbo2Draft.save().toCC[IdSimpleDboLong].get 100 | } 101 | 102 | 103 | def close(){ 104 | db.shutdown() 105 | } 106 | 107 | def reload = { 108 | dbo.b = "c" 109 | val dbo2 = dbo.reload() 110 | dbo2.a must beEqualTo("a") and(dbo2.b must beEqualTo("b")) and (dbo2.b must not equalTo "c") 111 | } 112 | 113 | def changeProperty = { 114 | dbo.b = "d" 115 | dbo.save() 116 | db.commit() 117 | db.getVertex(dbo.getId).toCC[SimpleDboLong].get.b must beEqualTo("d") 118 | } 119 | 120 | def useGetId = { 121 | val v = db.getVertex(dbo.getVertex.getId) 122 | v.getId must beEqualTo(dbo.getId) 123 | } 124 | 125 | def useGetIdDbObject = { 126 | val d = db.getVertex(dbo2.getVertex.getId).toCC[IdSimpleDboLong].get 127 | d.getId must beEqualTo(dbo2.getId) 128 | } 129 | 130 | def delete = { 131 | dbo.delete() 132 | dbo.isSaved must beEqualTo(false) 133 | } 134 | 135 | def getIdAfterSave = { 136 | val o = SimpleDboLong("d", "f") 137 | o.save() 138 | db.commit() 139 | 140 | (o.getId must not be throwAn[NotBoundException]) and 141 | (o.getId must not be equalTo(null)) and 142 | (o.isSaved must beTrue) 143 | } 144 | } 145 | 146 | 147 | 148 | } 149 | -------------------------------------------------------------------------------- /src/test/scala/com/ansvia/graph/DifferentClassLoaderSpec.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import com.ansvia.graph.BlueprintsWrapper._ 4 | import com.tinkerpop.blueprints.impls.tg.TinkerGraphFactory 5 | import org.specs2.mutable.Specification 6 | 7 | /** 8 | * This specification simulates behavior of converting vertex with use of different classloader. 9 | * Author: pdolega 10 | */ 11 | class DifferentClassLoaderSpec extends Specification { 12 | 13 | "Blueprints vertex wrapper" should { 14 | "attempt to create case class if specified class loader" in { 15 | val myClassLoader = new CustomClassLoader(getClass.getClassLoader) 16 | 17 | implicit val db = TinkerGraphFactory.createTinkerGraph() 18 | db.save(Person(name = "Keyser Soze")).toCC[Person](myClassLoader) must throwA(new ClassLoaderUsedException(classOf[Person].getName)) 19 | } 20 | 21 | "load case class with parent classloader if not specified otherwise" in { 22 | implicit val db = TinkerGraphFactory.createTinkerGraph() 23 | val herkules = db.save(Person(name = "Tony Stark")).toCC[Person] 24 | 25 | herkules must beEqualTo(Some(Person(name = "Tony Stark"))) 26 | } 27 | } 28 | } 29 | 30 | class CustomClassLoader(val parentClassLoader: ClassLoader) extends ClassLoader { 31 | override def loadClass(name: String, resolve: Boolean): Class[_] = { 32 | throw new ClassLoaderUsedException(name) 33 | } 34 | } 35 | 36 | class ClassLoaderUsedException(message: String) extends Exception(message) 37 | 38 | case class Person(name: String) 39 | -------------------------------------------------------------------------------- /src/test/scala/com/ansvia/graph/GremlinWrapperSpec.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import org.specs2.mutable.Specification 4 | import com.tinkerpop.blueprints.impls.tg.TinkerGraphFactory 5 | import org.specs2.specification.Scope 6 | import com.tinkerpop.blueprints.{Direction, Edge, Vertex} 7 | 8 | /** 9 | * Author: robin 10 | * Date: 2/24/14 11 | * Time: 1:07 AM 12 | * 13 | */ 14 | class GremlinWrapperSpec extends Specification { 15 | 16 | import scala.collection.JavaConversions._ 17 | import com.ansvia.graph.BlueprintsWrapper._ 18 | import gremlin._ 19 | 20 | implicit val db = TinkerGraphFactory.createTinkerGraph() 21 | 22 | class Ctx extends Scope { 23 | 24 | val follow = "follow" 25 | 26 | val name1 = "unyil" 27 | val name2 = "usrok" 28 | val name3 = "ogah" 29 | 30 | val v1 = db.addVertex(null) 31 | val v2 = db.addVertex(null) 32 | val v3 = db.addVertex(null) 33 | 34 | v1.setProperty("name", name1) 35 | v2.setProperty("name", name2) 36 | v3.setProperty("name", name3) 37 | 38 | v1.setProperty("age", 17) 39 | v2.setProperty("age", 22) 40 | v3.setProperty("age", 19) 41 | 42 | v1 --> follow --> v2 43 | v1 --> follow --> v3 44 | } 45 | 46 | "gremlin wrapper" should { 47 | "wrap transform" in new Ctx { 48 | v1.pipe.out(follow).transform { (v:Vertex) => 49 | v.getOrElse("name","") 50 | }.iterator().toList must contain(name2, name3) 51 | } 52 | "wrap side effect" in new Ctx { 53 | v1.pipe.out(follow).sideEffect { (v:Vertex) => 54 | v.setProperty("name", v.getProperty[String]("name") + "!") 55 | } 56 | val vx = db.getVertex(v1.getId) 57 | vx.get("name") must_== Some(name1) 58 | } 59 | "wrap order" in new Ctx { 60 | v1.pipe.out(follow).order { (a:Vertex, b:Vertex) => 61 | a.getProperty[java.lang.Integer]("age").compareTo(b.getProperty[java.lang.Integer]("age")) 62 | }.iterator().toList must contain(v3,v2).inOrder 63 | } 64 | "wrap filter vertex" in new Ctx { 65 | v1.pipe.out(follow).filter { (v:Vertex) => 66 | v.get[String]("name") == Some(name3) 67 | }.iterator().toList must beEqualTo(List(v3)) 68 | } 69 | "wrap filter edge" in new Ctx { 70 | v1.pipe.outE(follow).filter { (e:Edge) => 71 | e.getVertex(Direction.IN).get[String]("name") == Some(name3) 72 | }.inV().iterator().toList must beEqualTo(List(v3)) 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/test/scala/com/ansvia/graph/ObjectConverterSpec.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import com.ansvia.graph.BlueprintsWrapper._ 4 | import com.tinkerpop.blueprints.Vertex 5 | import com.tinkerpop.blueprints.impls.tg.TinkerGraphFactory 6 | import org.specs2.Specification 7 | import org.specs2.specification.Step 8 | 9 | import scala.collection.JavaConversions._ 10 | import scala.language.reflectiveCalls 11 | 12 | /** 13 | * User: robin 14 | * 15 | */ 16 | class ObjectConverterSpec extends Specification { 17 | 18 | import com.ansvia.graph.testing.model._ 19 | 20 | def is = s2"""$sequential 21 | Object converter should 22 | convert vertex to case class #1 ${oc.vtocc1} 23 | convert vertex to case class #2 ${oc.vtocc2} 24 | convert vertex to case class #3 ${oc.vtocc3} 25 | convert case class to vertex #1 ${oc.cctov1} 26 | convert case class to vertex #3 ${oc.cctov2} 27 | convert back from vertex to case class #1 ${oc.cbvtocc1} 28 | convert back from vertex to case class #2 ${oc.cbvtocc2} 29 | convert back from vertex to case class #3 ${oc.cbvtocc3} 30 | ${Step(oc.close())} 31 | 32 | DbObject inherited class should 33 | work with non param var get parameter ${dboic.nonParamVarGet} 34 | work with non param var get inner variable #1 ${dboic.nonParamVarGetInner} 35 | work with non param var get inner variable #2 ${dboic.nonParamVarGetInner2} 36 | save directly using .save() ${dboic.saveDirectlyUsingSave} 37 | has expected field ${dboic.hasExpectedField} 38 | get back saved field data ${dboic.getBackSavedFieldData} 39 | deserializable ${dboic.deserializable} 40 | has expected data in deserialized object ${dboic.expectedDataDeserializedObj} 41 | able to get raw vertex from case class ${dboic.getRawVertexFromCC} 42 | make relation DbObject to DbObject #1 ${dboic.makeRelDbo2Dbo} 43 | make relation DbObject to DbObject #2 ${dboic.makeRelDbo2Dbo2} 44 | saved DbObject return true in isSaved ${dboic.savedDboRetTrue} 45 | class contain lazy or val should not raising invocation error ${dboic.ccContainLazyNotError} 46 | access upper variable #1 ${dboic.accessUpperVar1} 47 | access upper variable #2 ${dboic.accessUpperVar2} 48 | access upper-upper variable #1 ${dboic.accessUpperUpperVar1} 49 | access upper-upper variable #2 ${dboic.accessUpperUpperVar2} 50 | access upper-upper variable #3 ${dboic.accessUpperUpperVar3} 51 | access lower variable via loader from upper-upper ${dboic.accessLowVarViaLoaderFromUpperUpper} 52 | access trait variable ${dboic.accessTraitVar} 53 | not saving non persisted var ${dboic.noSaveNonPersistedVar} 54 | saved via __save__() ${dboic.savedViaSaveMethod} 55 | ${Step(dboic.close())} 56 | """ 57 | 58 | object oc { 59 | 60 | implicit val db = TinkerGraphFactory.createTinkerGraph() 61 | 62 | val v = db.addVertex(null) 63 | v.set("name", "robin") 64 | v.set("age", 25) 65 | v.set("_class_", "com.ansvia.graph.testing.model.User") 66 | 67 | val vtcc1 = ObjectConverter.toCC[User](v) 68 | 69 | val ccU = User("gondez", 35) 70 | 71 | val v2 = db.save(ccU) 72 | 73 | val vtcc2 = ObjectConverter.toCC[User](v2) 74 | 75 | def close(){ 76 | db.shutdown() 77 | } 78 | 79 | def vtocc1 = vtcc1.isDefined must beTrue 80 | def vtocc2 = vtcc1.get.name must beEqualTo("robin") 81 | def vtocc3 = vtcc1.get.age must beEqualTo(25) 82 | def cctov1 = v2.get("_class_").isDefined must beTrue 83 | def cctov2 = v2.get[String]("_class_").get must beEqualTo("com.ansvia.graph.testing.model.User") 84 | def cbvtocc1 = vtcc2.isDefined must beTrue 85 | def cbvtocc2 = vtcc2.get.name must beEqualTo("gondez") 86 | def cbvtocc3 = vtcc2.get.age must beEqualTo(35) 87 | 88 | 89 | } 90 | 91 | object dboic { 92 | 93 | implicit val db = TinkerGraphFactory.createTinkerGraph() 94 | 95 | val v4o = Animal("cat") 96 | v4o.age = 5 97 | v4o.kind = "mamalia" 98 | val v4 = v4o.save() 99 | val v4ob = v4.toCC[Animal].get 100 | 101 | val v3 = Motor("Honda").save() 102 | v3 --> "hit" --> v4ob 103 | 104 | 105 | val nemoDraft = SeaFish("yellow") 106 | nemoDraft.name = "nemo" 107 | val nemo = nemoDraft.save().toCC[SeaFish].get 108 | 109 | val sharkDraft = Shark("Hammer head") 110 | sharkDraft.name = "the killer" 111 | sharkDraft.lives = "Atlantica" 112 | sharkDraft.eatable = false 113 | sharkDraft.children = 3 // this should not saved 114 | sharkDraft.animalProtected = true // should saved using custom __save__() 115 | val shark = sharkDraft.save().toCC[Shark].get 116 | 117 | def close(){ 118 | db.shutdown() 119 | } 120 | 121 | def nonParamVarGet = v4ob.name must beEqualTo("cat") 122 | def nonParamVarGetInner = v4ob.age must beEqualTo(5) 123 | def nonParamVarGetInner2 = v4ob.kind must beEqualTo("mamalia") 124 | 125 | def saveDirectlyUsingSave = v3.isInstanceOf[Vertex] must beTrue 126 | def hasExpectedField = v3.has("mark") must beTrue 127 | def getBackSavedFieldData = v3.get[String]("mark").getOrElse("mark", "esemka") must beEqualTo("Honda") 128 | def deserializable = v3.toCC[Motor].isDefined must beTrue 129 | def expectedDataDeserializedObj = v3.toCC[Motor].get.mark must beEqualTo("Honda") 130 | def getRawVertexFromCC = v4ob.getVertex must beEqualTo(v4) 131 | def makeRelDbo2Dbo = v4ob.getVertex.pipe.in("hit").headOption.isDefined must beTrue 132 | def makeRelDbo2Dbo2 = v4ob.getVertex.pipe.in("hit").headOption.get.toCC[Motor].get.mark must beEqualTo("Honda") 133 | def savedDboRetTrue = (v4o.isSaved && v4ob.isSaved) must beTrue 134 | def ccContainLazyNotError = ContainLazy(1).save() must not equalTo(null) 135 | def accessUpperVar1 = nemo.name must beEqualTo("nemo") 136 | def accessUpperVar2 = nemo.color must beEqualTo("yellow") 137 | def accessUpperUpperVar1 = shark.color must beEqualTo("blue") 138 | def accessUpperUpperVar2 = shark.name must beEqualTo("the killer") 139 | def accessUpperUpperVar3 = shark.kind must beEqualTo("Hammer head") 140 | def accessLowVarViaLoaderFromUpperUpper = shark.lives must beEqualTo("Atlantica") 141 | def accessTraitVar = shark.eatable must beFalse 142 | def noSaveNonPersistedVar = shark.children must beEqualTo(0) 143 | def savedViaSaveMethod = shark.animalProtected must beTrue 144 | 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/test/scala/com/ansvia/graph/TitanBackedDb.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | //import com.lambdazen.bitsy.BitsyGraph 4 | //import java.nio.file.Paths 5 | import com.thinkaurelius.titan.core.TitanFactory 6 | import org.apache.commons.configuration.BaseConfiguration 7 | import java.io.File 8 | import com.tinkerpop.blueprints.{TransactionalGraph, KeyIndexableGraph} 9 | 10 | 11 | /** 12 | * Author: robin 13 | * Date: 9/1/14 14 | * Time: 2:22 PM 15 | * 16 | */ 17 | 18 | trait TitanBackedDb { 19 | implicit def db = TitanBackedDb.db 20 | } 21 | 22 | object TitanBackedDb { 23 | 24 | type GraphType = KeyIndexableGraph with TransactionalGraph 25 | 26 | val dirTls = new ThreadLocal[String]() 27 | val dbTls = new ThreadLocal[GraphType]() 28 | 29 | def db:GraphType = { 30 | 31 | val _db = dbTls.get() 32 | 33 | if (_db != null){ 34 | _db 35 | }else{ 36 | val dir = "/tmp/digaku2-test-" + Thread.currentThread().getId 37 | dirTls.set(dir) 38 | 39 | // clean up old data files 40 | val fDir = new File(dir) 41 | if (fDir.exists()){ 42 | for ( f <- fDir.listFiles() ) 43 | f.delete() 44 | fDir.delete() 45 | } 46 | 47 | val dbConf = new BaseConfiguration 48 | dbConf.setProperty("storage.backend", "berkeleyje") 49 | dbConf.setProperty("storage.directory", dir) 50 | dbConf.setProperty("storage.transactions", false) 51 | dbConf.setProperty("storage.db-cache", true) 52 | dbConf.setProperty("storage.db-cache-time", 60000) 53 | 54 | val db = TitanFactory.open(dbConf) 55 | dbTls.set(db) 56 | 57 | db 58 | } 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /src/test/scala/com/ansvia/graph/VertexLabelSpec.scala: -------------------------------------------------------------------------------- 1 | package com.ansvia.graph 2 | 3 | import org.specs2.mutable.Specification 4 | import org.specs2.specification.Scope 5 | import com.thinkaurelius.titan.core.{TitanVertex, TitanGraph} 6 | import com.ansvia.graph.testing.model.{SimpleDbo, Animal} 7 | import com.tinkerpop.blueprints.Vertex 8 | import com.tinkerpop.blueprints.util.wrappers.id.{IdVertex, IdGraph} 9 | 10 | /** 11 | * Author: robin 12 | * Date: 9/1/14 13 | * Time: 3:21 PM 14 | * 15 | * 16 | * Titan only specific label feature test 17 | */ 18 | 19 | 20 | class VertexLabelSpec extends Specification with TitanBackedDb { 21 | 22 | sequential 23 | 24 | val HUMAN = "human" 25 | val ANIMAL = "animal" 26 | 27 | class Ctx extends Scope { 28 | 29 | 30 | implicit val titanDb = db.asInstanceOf[TitanGraph] 31 | 32 | var humanV:Vertex = _ 33 | var animalV:Vertex = _ 34 | var animal2V:Vertex = _ 35 | 36 | val trx = titanDb.getManagementSystem 37 | // titanDb.transact { implicit trx => 38 | trx.makeVertexLabel(HUMAN).make() 39 | trx.makeVertexLabel(ANIMAL).make() 40 | trx.commit() 41 | // } 42 | } 43 | 44 | class Ctx2 extends Scope { 45 | implicit val titanDb = db.asInstanceOf[TitanGraph] 46 | } 47 | class Ctx3 extends Scope { 48 | private val titanDb = db.asInstanceOf[TitanGraph] 49 | val mgmt = titanDb.getManagementSystem 50 | if (!mgmt.containsRelationType(IdGraph.ID)){ 51 | val __id = mgmt.makePropertyKey(IdGraph.ID).dataType(classOf[java.lang.String]).make() 52 | mgmt.buildIndex("IDGraphId", classOf[Vertex]).addKey(__id).unique().buildCompositeIndex() 53 | mgmt.commit() 54 | } 55 | implicit val idGraphTitanDb = new IdGraph(titanDb, true, false) 56 | } 57 | 58 | 59 | "When using Titan backed db we" should { 60 | "be able to create vertex with label" in new Ctx { 61 | import TitanDbWrapper._ 62 | 63 | humanV = SimpleDbo("unyil", "").saveWithLabel(HUMAN) 64 | animalV = Animal("cat").saveWithLabel(ANIMAL) 65 | 66 | // using string 67 | animal2V = Animal("lion").saveWithLabel(ANIMAL) 68 | 69 | val _humanV:TitanVertex = titanDb.getVertex(humanV.getId).asInstanceOf[TitanVertex] 70 | val _animalV:TitanVertex = titanDb.getVertex(animalV.getId).asInstanceOf[TitanVertex] 71 | val _animal2V:TitanVertex = titanDb.getVertex(animal2V.getId).asInstanceOf[TitanVertex] 72 | _humanV.getLabel must_== HUMAN 73 | _animalV.getLabel must_== ANIMAL 74 | _animal2V.getLabel must_== ANIMAL 75 | } 76 | "be able to create vertex with label directly to TitanGraph" in new Ctx2 { 77 | 78 | val v = titanDb.addVertexWithLabel(ANIMAL) 79 | titanDb.commit() 80 | val v2:TitanVertex = titanDb.getVertex(v.getId).asInstanceOf[TitanVertex] 81 | v2.getLabel must_== ANIMAL 82 | } 83 | "be able to working using IdGraph wrapper" in new Ctx3 { 84 | import IdGraphTitanDbWrapper._ 85 | 86 | val v = Animal("bear").saveWithLabel(ANIMAL) 87 | 88 | idGraphTitanDb.commit() 89 | 90 | val v2:TitanVertex = idGraphTitanDb.getVertex(v.getId) 91 | .asInstanceOf[IdVertex].getBaseVertex.asInstanceOf[TitanVertex] 92 | 93 | v2 must_!= null 94 | v2.getLabel must_== ANIMAL 95 | } 96 | "be able to using DbObject" in new Ctx3 { 97 | import IdGraphTitanDbWrapper._ 98 | import com.ansvia.graph.BlueprintsWrapper._ 99 | 100 | val v = Animal("bear").saveWithLabel(ANIMAL) 101 | 102 | idGraphTitanDb.commit() 103 | 104 | v.toCC[Animal] must_!= None 105 | v.toCC[Animal].get.name must_== "bear" 106 | } 107 | "saved vertex has id immediately" in new Ctx3 { 108 | import IdGraphTitanDbWrapper._ 109 | import com.ansvia.graph.BlueprintsWrapper._ 110 | 111 | val v = Animal("tiger").saveWithLabel(ANIMAL) 112 | 113 | v.getId must_!= null 114 | } 115 | "get vertex from dbo after save" in new Ctx3 { 116 | import IdGraphTitanDbWrapper._ 117 | import com.ansvia.graph.BlueprintsWrapper._ 118 | 119 | val tiger = Animal("tiger") 120 | 121 | val v = tiger.saveWithLabel(ANIMAL) 122 | 123 | tiger.getVertex must_== v 124 | tiger.name must_== "tiger" 125 | } 126 | "save with label via transaction" in new Ctx3 { 127 | import IdGraphTitanDbWrapper._ 128 | import com.ansvia.graph.BlueprintsWrapper._ 129 | 130 | val id = 131 | idGraphTitanDb.transact { trx => 132 | val tiger = Animal("tiger") 133 | 134 | val v = tiger.saveWithLabelTx(ANIMAL, trx) 135 | 136 | v.getId 137 | } 138 | 139 | idGraphTitanDb.commit() 140 | 141 | val v = idGraphTitanDb.getVertex(id) 142 | 143 | v must_!= null 144 | 145 | val tiger = v.toCC[Animal].get 146 | 147 | tiger.getVertex must_== v 148 | tiger.name must_== "tiger" 149 | 150 | // can reload using transaction 151 | idGraphTitanDb.transact { trx => 152 | tiger.reload()(trx) must not be throwAn[Exception] 153 | } 154 | 155 | } 156 | "add vertex with label via IdGraph" in new Ctx3 { 157 | import IdGraphTitanDbWrapper._ 158 | import scala.language.reflectiveCalls 159 | 160 | val v = idGraphTitanDb.addVertexWithLabel(ANIMAL) 161 | 162 | v.asInstanceOf[IdVertex].getBaseVertex.asInstanceOf[TitanVertex].getLabel must_== ANIMAL 163 | } 164 | } 165 | } 166 | --------------------------------------------------------------------------------