/razpub/src/razie/assets/AssetKey.scala

http://razpub.googlecode.com/ · Scala · 290 lines · 91 code · 34 blank · 165 comment · 33 complexity · 8ff9d5f17f502d8688f8da66f758f19d MD5 · raw file

  1. /**
  2. * Razvan's public code. Copyright 2008 based on Apache license (share alike) see LICENSE.txt for
  3. * details. No warranty implied nor any liability assumed for this code.
  4. */
  5. package razie.assets
  6. import razie.base.AttrAccess
  7. import scala.collection._
  8. /**
  9. * Each entity/asset has a unique key, which identifies the asset's type, id and location. Borrowed
  10. * from OSS/J's ManagedEntityKey (type, key, location), this is lighter and designed to pass through
  11. * URLs and be easily managed as a string form.
  12. *
  13. * <p>
  14. * asset-URI has this format: <code>"razie.uri:entityType:entityKey@location"</code>
  15. * <p>
  16. * asset-context URI has this format: <code>"razie.puri:entityType:entityKey@location&context"</code>
  17. *
  18. * When the asset support framework is used, this key can have this other form:
  19. * <p>
  20. * REST asset-URI has this format: <code>"http://SERVER:PORT/asset/entityType/entityKey"</code>
  21. * <p>
  22. * REST asset-URI has this format: <code>"http://SERVER:PORT/asset/KEY-ENCODED"</code>
  23. * <p>
  24. * OR: REST asset-URI has this format: <code>"http://SERVER:PORT/asset/entityType/entityKey?context"</code>
  25. *
  26. * Actions can be invoked like so
  27. * <p>
  28. * REST action asset-URI has this format: <code>"http://SERVER:PORT/asset/entityType/entityKey/action?args"</code>
  29. * <p>
  30. * REST asset-URI has this format: <code>"http://SERVER:PORT/asset/KEY-ENCODED/action&args"</code>
  31. * <p>
  32. * OR: REST asset-URI has this format: <code>"http://SERVER:PORT/asset/entityType/entityKey?context"</code>
  33. *
  34. * <ul>
  35. * <li>type is the type of entity, should be unique among all other types. HINT: do not keep
  36. * defining "Movie" etc - always assume someone else did...use "AlBundy.Movie" for instance :D
  37. * <li>key is the unique key of the given entity, unique in this location and for this type. The key
  38. * could be anything that doesn't have an '@' un-escaped. it could contain ':' itself like an
  39. * XCAP/XPATH etc, which is rather cool...?
  40. * <li>location identifies the location of the entity: either URL or folder or a combination.
  41. * A folder implies it's on the localhost. An URL implies it's in that specific server.
  42. * </ul>
  43. *
  44. * <p>
  45. * Keys must have at least type. By convention, if the key is missing, the key refers to all
  46. * entities of the given type.
  47. *
  48. * @author razvanc99
  49. */
  50. class AssetKey (_meta:String, _id:String, _loc:AssetLocation)
  51. extends razie.g.GIDRef (_meta, _id, _loc.toGLoc) {
  52. // TODO optimize - don't need the internal _xxx vars above...
  53. // val meta:String = _meta
  54. // val id:String = if (_id == null) AssetKey.uid() else _id
  55. // TODO LOC - search for TODO LOC
  56. // override val loc:AssetLocation = if(_loc == null) AssetLocation.LOCAL else _loc
  57. val aloc:AssetLocation = if(_loc == null) AssetLocation.LOCAL else _loc
  58. def this (_meta:String, _id:String) = this (_meta, _id, null)
  59. def this (_meta:String) = this (_meta, AssetKey.uid, null)
  60. // these have to be functions rather than vars for existing java code...
  61. def getMeta() = meta
  62. def getId() = id
  63. // TODO LOC - search for TODO LOC
  64. // def getLoc() = loc
  65. def getLoc() = aloc
  66. // TODO 1-1 inline
  67. def getType() = meta
  68. // TODO LOC - search for TODO LOC
  69. def getLocation() = aloc
  70. override def equals(o:Any):Boolean = o match {
  71. case r : AssetKey => meta.equals(r.meta) && id.equals(r.id)
  72. case _ => false
  73. }
  74. override def hashCode() : Int = meta.hashCode() + (if(id != null ) id.hashCode() else 0)
  75. /** short descriptive string */
  76. override def toString =
  77. AssetKey.PREFIX+":" + meta + ":" + (if(id == null ) "" else java.net.URLEncoder.encode(id, "UTF-8")) + (if (loc == null || AssetLocation.LOCAL.equals(loc)) "" else ("@" + loc.toString()))
  78. /**
  79. * Use this method to get a string that is safe to use in a URL. Note that whenever the string
  80. * is encoded when you want to use it it must be decoded with fromUrlEncodedString(String).
  81. */
  82. def toUrlEncodedString : String =
  83. java.net.URLEncoder.encode(toString, "UTF-8")
  84. }
  85. /**
  86. * Each entity/asset has a unique key, which identifies the asset's type, id and location. Borrowed
  87. * from OSS/J's ManagedEntityKey (type, key, location), this is lighter and designed to pass through
  88. * URLs and be easily managed as a string form.
  89. *
  90. * <p>
  91. * asset-URI has this format: <code>"razie.uri:entityType:entityKey@location"</code>
  92. * <p>
  93. * asset-context URI has this format: <code>"razie.puri:entityType:entityKey@location&context"</code>
  94. *
  95. * When the asset support framework is used, this key can have this other form:
  96. * <p>
  97. * REST asset-URI has this format: <code>"http://SERVER:PORT/asset/entityType/entityKey"</code>
  98. * <p>
  99. * REST asset-URI has this format: <code>"http://SERVER:PORT/asset/KEY-ENCODED"</code>
  100. * <p>
  101. * OR: REST asset-URI has this format: <code>"http://SERVER:PORT/asset/entityType/entityKey?context"</code>
  102. *
  103. * Actions can be invoked like so
  104. * <p>
  105. * REST action asset-URI has this format: <code>"http://SERVER:PORT/asset/entityType/entityKey/action?args"</code>
  106. * <p>
  107. * REST asset-URI has this format: <code>"http://SERVER:PORT/asset/KEY-ENCODED/action&args"</code>
  108. * <p>
  109. * OR: REST asset-URI has this format: <code>"http://SERVER:PORT/asset/entityType/entityKey?context"</code>
  110. *
  111. * <ul>
  112. * <li>type is the type of entity, should be unique among all other types. HINT: do not keep
  113. * defining "Movie" etc - always assume someone else did...use "AlBundy.Movie" for instance :D
  114. * <li>key is the unique key of the given entity, unique in this location and for this type. The key
  115. * could be anything that doesn't have an '@' un-escaped. it could contain ':' itself like an
  116. * XCAP/XPATH etc, which is rather cool...?
  117. * <li>location identifies the location of the entity: either URL or folder or a combination.
  118. * A folder implies it's on the localhost. An URL implies it's in that specific server.
  119. * </ul>
  120. *
  121. * <p>
  122. * Keys must have at least type. By convention, if the key is missing, the key refers to all
  123. * entities of the given type.
  124. *
  125. * @author razvanc99
  126. */
  127. ////class AssetKey (_meta:String, _id:String, val loc:AssetLocation) extends razie.GRef (_meta, _id, loc.gloc) {
  128. //class AssetKey (_meta:String, _id:String, val loc:AssetLocation) extends razie.GRef (_meta, _id, loc.gloc) {
  129. //// override val loc = new AssetLocation (super.loc)
  130. //
  131. // def this (_meta:String, _id:String) = this (_meta, _id, null)
  132. // def this (_meta:String) = this (_meta, razie.G.uid, null)
  133. //
  134. // // these have to be functions rather than vars for existing java code...
  135. // def getMeta() = meta
  136. // def getId() = id
  137. // def getLoc() = loc
  138. //
  139. // // TODO 1-1 inline
  140. // def getType() = meta
  141. // def getLocation() = loc
  142. //}
  143. object AssetKey {
  144. def fromRef (x:razie.g.GRef) =
  145. new AssetKey (x.meta, x.asInstanceOf[razie.g.GIDRef].id, AssetLocation.fromGLoc(x.loc))
  146. def PREFIX = "razie.uri"
  147. /** to allocate next UID...this should be done better */
  148. private var seqNum : Int = 1;
  149. /**
  150. * just a simple UID implementation, to fake IDs for objects that don't have them.
  151. */
  152. def uid() = "Uid-" + {seqNum+=1; seqNum} + "-" + String.valueOf(System.currentTimeMillis());
  153. /**
  154. * make up from an entity-URI. see class javadocs for details on URI
  155. *
  156. * TODO it's not efficient - creates too many objects to parse the string
  157. *
  158. * @return the entity-URI
  159. */
  160. def fromString(inurl:String) :AssetKey = {
  161. var url = inurl;
  162. var news:String="";
  163. if (url.startsWith(AssetKey.PREFIX)) {
  164. news = url.replace(AssetKey.PREFIX+":", "");
  165. } else {
  166. val map1 = url.split("://", 2);
  167. // with the following, i support also a missing PREFIX, i.e. a simplified KEY with just
  168. // type:key@loc
  169. news = (if(map1.length > 1 ) map1(1) else (if (map1.length == 1 ) map1(0) else null))
  170. }
  171. if (news != null) {
  172. // i have a class nm
  173. val map2 = news.split(":", 2);
  174. if (map2.length > 1 && map2(1) != null) {
  175. // i have a key/id
  176. val map3 = map2(1).split("@", 2);
  177. if (map3.length > 1) {
  178. // i have an appEnv
  179. if (map3(1).contains ("@@")) {
  180. // i have a context
  181. val map4 = map3(1).split("@@", 2);
  182. return new AssetCtxKey(map2(0), decode(map3(0)), new AssetLocation(
  183. map4(0)), new AssetContext (razie.AA(map4(1))));
  184. } else
  185. return new AssetKey(map2(0), decode(map3(0)), new AssetLocation(
  186. map3(1)));
  187. } else {
  188. // no appEnv
  189. return new AssetKey(map2(0), decode(map3(0)), null);
  190. }
  191. } else {
  192. // no key/id
  193. return new AssetKey(map2(0), null, null);
  194. }
  195. }
  196. return null;
  197. }
  198. val ROLE = "role."
  199. // def ctx (s:String) = {
  200. // val ac = new AssetContext (razie.AA(s))
  201. // for (x <- razie.RJS apply ac.attrs.getPopulatedAttr)
  202. // if (x.startsWith(ROLE))
  203. // ac.env.put(x.replaceFirst(ROLE, ""), fromString(ac.attrs.sa(x)))
  204. //// ac.attrs.foreach ((x,y) => if (x.startsWith(ROLE)) ac.env.put(x.replaceFirst(ROLE, ""), fromString(y)))
  205. // }
  206. def decode (s:String) = java.net.URLDecoder.decode(s, "UTF-8")
  207. // implicit def toac (a : AttrAccess) : AssetContext = new AssetContext (a)
  208. }
  209. /** Context is an important notion. see detailed blurb on our wiki.homecloud.ca
  210. *
  211. * <p>Basically, a reference to an entity can contain the context in which it was made. The same
  212. * entity may do or mean different things depending on its context.
  213. *
  214. * <p>Recommend this be used sparingly.
  215. */
  216. class AssetContext (val name:String, val attrs : AttrAccess) {
  217. // val assocs : List[AssetAssoc]
  218. // TODO 1-1 lazy map
  219. // if (attrs != null)
  220. // (razie.RJS apply attrs.getPopulatedAttr).filter(_.startsWith(AssetKey.ROLE)).foreach (
  221. // x => env.put(x.replaceFirst(AssetKey.ROLE, ""), AssetKey.fromString(AssetKey.decode(attrs.sa(x))))
  222. // )
  223. def this () = this ("", AttrAccess.EMPTY) // maybe EMPTY?
  224. def this (a:AttrAccess) = this ({a.sa ("ctx.name")}, a)
  225. // cleanup
  226. def sa (name:String) : String = attrs.sa(name)
  227. def role(name:String) : AssetKey = attrs.sa (AssetKey.ROLE+name) match {
  228. case s:String => AssetKey.fromString(AssetKey.decode(s))
  229. case null => null
  230. }
  231. def role(name:String, who:AssetKey) : AssetContext = {
  232. attrs.set(AssetKey.ROLE+name, who.toUrlEncodedString)
  233. this
  234. }
  235. override def toString : String = {
  236. attrs.set ("ctx.name", name)
  237. attrs.addToUrl("")
  238. }
  239. override def equals(o:Any):Boolean = o match {
  240. case r : AssetContext => false // TODO 2-1 implement
  241. case _ => false
  242. }
  243. }
  244. // TODO 3-1 complete the asset context key implementation and test
  245. /** this type of key is used when an asset is in context */
  246. class AssetCtxKey (_meta:String, _id:String, _loc:AssetLocation, val ctx:AssetContext) extends AssetKey (_meta, _id, _loc) {
  247. /** short descriptive string */
  248. override def toString =
  249. AssetKey.PREFIX+":" + meta + ":" + (if(id == null ) "" else java.net.URLEncoder.encode(id, "UTF-8")) + (if (loc == null) "" else ("@" + loc.toString())) + ( if (ctx == null) "" else ("@@" + ctx.toString))
  250. // NOTE that for now i ignore the context... it is an interesting question wether the context should be ignored...
  251. override def equals(o:Any):Boolean = o match {
  252. // case r : AssetCtxKey => super.equals(o) && r.ctx .equals(ctx)
  253. case r : AssetKey => super.equals(o)
  254. case _ => false
  255. }
  256. }