1 /*
  2  * Filename: Attribute.js
  3  * Created By: Ranando D. King
  4  * License: Apache 2.0
  5  *
  6  * Copyright 2015 Ranando D. King
  7  *
  8  * Licensed under the Apache License, Version 2.0 (the "License");
  9  * you may not use this file except in compliance with the License.
 10  * You may obtain a copy of the License at
 11  *
 12  * 		http://www.apache.org/licenses/LICENSE-2.0
 13  *
 14  * Unless required by applicable law or agreed to in writing, software
 15  * distributed under the License is distributed on an "AS IS" BASIS,
 16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 17  * See the License for the specific language governing permissions and
 18  * limitations under the License.
 19  */
 20 
 21 var Class = require("./Class");
 22 var Private = Class.Private;
 23 var Property = Class.Property;
 24 
 25 var Attribute = (function() {
 26     var ExpandScopeElement = function(dest, scope, key, _this) {
 27         if (dest.hasOwnProperty(key) && scope[key].isBox) {
 28             var prop = scope[key];
 29             var isFn = (prop.value instanceof Function && !prop.value.isClass);
 30             var isFinal = prop.isFinal || isFn;
 31 
 32             //Handle the default case. The privilege level doesn't matter for that.
 33             var propConfig = {
 34                 enumerable: true,
 35                 configurable: !prop.isFinal
 36             };
 37 
 38             if (prop.isProperty) {
 39                 propConfig.configurable = false;
 40 
 41                 if (_this) {
 42                     if ((prop.value.get instanceof Function) && prop.value.get.isFunctor)
 43                         propConfig.get = prop.value.get.rescope(_this);
 44                     else
 45                         propConfig.get = prop.value.get;
 46 
 47                     if ((prop.value.set instanceof Function) && prop.value.set.isFunctor)
 48                         propConfig.set = prop.value.set.rescope(_this);
 49                     else
 50                         propConfig.set = prop.value.set;
 51                 }
 52                 else {
 53                     if (prop.value.get)
 54                         propConfig.get = prop.value.get;
 55 
 56                     if (prop.value.set)
 57                         propConfig.set = prop.value.set;
 58 
 59                     if (prop.value.value) {
 60                         propConfig.writable = prop.value.wrtable;
 61 
 62                         if ((prop.value.value instanceof Function) && prop.value.value.isFunctor)
 63                             propConfig.value = prop.value.value.rescope(_this);
 64                         else
 65                             propConfig.value = prop.value.value;
 66                     }
 67                 }
 68             }
 69             else {
 70                 propConfig.writable = !isFinal;
 71 
 72                 if ((prop.value instanceof Function) && prop.value.isFunctor)
 73                     propConfig.value = prop.value.rescope(_this);
 74                 else
 75                     propConfig.value = prop.value;
 76             }
 77 
 78             delete dest[key];
 79             Object.defineProperty(dest, key, propConfig);
 80         }
 81     };
 82 
 83     var makeRedirect = function(prop, dest, key) {
 84         var isProtected = false;
 85         var propConfig = {
 86             enumerable: true
 87         };
 88 
 89         var getRedirect = function() {
 90             var instance = instances.get(this);
 91             var retval = instance[key];
 92 
 93             //If we're returning a function, make sure it's called against the correct object!
 94             if ((retval instanceof Function) && !retval.isClass && !retval.isFunctor)
 95                 retval = new Functor(instance, retval);
 96 
 97             return retval;
 98         };
 99 
100         var setRedirect = function(val) {
101             var instance = ((prop.isStatic && this.__isClassDomain)?staticScope :
102                             ((this.__isClassDomain)?this:instances.get(this))) || staticScope;
103             instance[key] = val;
104         };
105 
106         if (isProtected || (prop.isPublic && !prop.isStatic)) {
107             propConfig.get = new Functor(null, getRedirect);
108 
109             if (!prop.isFinal)
110                 propConfig.set = new Functor(null, setRedirect);
111         }
112         else {
113             propConfig.get = getRedirect;
114 
115             if (!prop.isFinal)
116                 propConfig.set = setRedirect;
117         }
118 
119         //Put a new property on the destination that references the
120         //redirected object.
121         dest[key] = new Box(prop.isPublic?Privilege.Public: (prop.isProtected?Privilege.Protected:Privilege.Private),
122                             prop.isStatic, false, true, propConfig);
123     };
124 
125     var $$ = function Attribute(name, definition) {
126         if (!name || !name.length)
127             throw new SyntaxError("Attributes must be assigned a name!");
128 
129         if (definition.contains("Constructor"))
130             throw new SyntaxError("Attributes are static objects and cannot contain a non-static constructor!");
131         
132         var scope = new WeakMap();
133         
134         definition["$source"] = Private(Property({ get: function getSource() { return scope.get(this); } }));
135 
136         var Special = { Extends:0, Implements:0, Events:0, Mixins:0, StaticConstructor:0 };
137 
138         //attrib is the Class of the Attribute!
139         var attrib = Class.call(this, name, definition);
140 
141         var _$ = function(obj) {
142             var attr = new attrib();
143             var instance = {};
144             
145             scope.set(attr, obj);
146 
147             for (var member in definition) {
148                 if (definition.hasOwnProperty(member) && definition[member]) {
149                     if (!(member in Special) && definition[member].isBox) {
150                         makeRedirect(definition[member], instance, member);
151                         ExpandScopeElement(instance, instance, member, attr);
152                     }
153                 }
154             }
155             
156             instance.prototype = obj;
157             
158             if (!("$attributes" in instance))
159                 Object.defineProperty(instance, "$attributes", { value: [] });
160             
161             instance["$attributes"].push(name);
162             Object.seal(instance);
163             return instance;
164         };
165 
166         Object.seal(_$);
167         return _$;
168     };
169 
170     Object.seal($$);
171     return $$;
172 })();
173 
174 module.exports = Attribute;
175