├── .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 [](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 | [](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 |
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 | *
9 | * - Success, with a value of some type and an output that may serve as the input to subsequent rules.
10 | * - Failure. A failure may result in some alternative rule being applied.
11 | * - Error. No further rules should be attempted.
12 | *
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 ~ = for (a <- this; b <- next orError) yield new ~(a, b)
92 |
93 | def ~- = for (a <- this; b <- next orError) yield a
94 |
95 | def -~ = 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 |
--------------------------------------------------------------------------------