PageRenderTime 56ms CodeModel.GetById 43ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

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