posted on Monday, August 09, 2004 1:43 PM by anoras

Declarative JavaScript programming - Source Code

// Enumerations
Modifier={
 private:0x2,
 public:0x1,
 static:0x8,
 toString:function(m) {
  for (var f in Modifier) {
   if (typeof(Modifier[f])=="number") if (Modifier[f]==m) return f.toString();
  }
 },
 isPublic:function(m) {
  return (m&Modifier.public)==Modifier.public;
 },
 isPrivate:function(m) {
  return (m&Modifier.private)==Modifier.private;
 },
 isStatic:function(m) {
  return (m&Modifier.static)==Modifier.static;
 }
}
WellKnownSignature={
 customFunction:1,
 string:2,
 number:3,
 boolean:4,
 object:5,
 array:6,
 date:7,
 enumerator:8,
 _arrayConstructorSignature:new Array().constructor,
 _dateConstructorSignature:new Date().constructor,
 _enumeratorConstructorSignature:new Enumerator(new Array()).constructor,
 toString:function(s) {
  for (var f in WellKnownSignature) {
   if (typeof(WellKnownSignature[f])=="number") if (WellKnownSignature[f]==s) return f.toString();
  }
 }
}
// Reflection class
Reflection={
 // Shared
 getModifier:function(memberName) {
  return (memberName.substr(0,1)=='_'||memberName.substr(0,2)=="m_"||memberName.toLowerCase().indexOf("private")>-1)?Modifier.private:Modifier.public;
 }, 
 // Constructor and class reflection
 getConstructor:function(obj) {
  return obj.constructor;
 },
 identifyObjectSignature:function(obj) {
  switch (typeof(obj)) {
   case "function" : return WellKnownSignature.customFunction;
   case "string" : return WellKnownSignature.string;
   case "boolean" : return WellKnownSignature.boolean;
   case "number" : return WellKnownSignature.number;
   case "object" :
    switch (obj.constructor) {
     case WellKnownSignature._arrayConstructorSignature : return WellKnownSignature.array;
     case WellKnownSignature._dateConstructorSignature : return WellKnownSignature.date;
     case WellKnownSignature._enumeratorConstructorSignature : return WellKnownSignature.enumerator;
     default : return WellKnownSignature.object;
    }
   default :
    void (0); // Throw exception
  }
  
 },
 getClassName:function(obj) {
  return Reflection.getMethodName(Reflection.getConstructor(obj));
 },
 // Method reflection
 getMethodName:function(fn) {
  try {
   var fnName=fn.toString();
   fnName=fnName.substring(fnName.indexOf("function")+8,fnName.indexOf('(')).replace(/ /g,'');
   return fnName;
  } catch (e) {
   return "unknownType";
  }
 },
 getMethods:function(obj,protection) {
  if (protection==null) protection=Modifier.public|Modifier.private;
  var fns=[];
  switch (protection) {
   case Modifier.public|Modifier.private:
    for (var elm in obj) {
     if (typeof(obj[elm])=="function") fns[fns.length]=obj[elm];
    }
   break;
   case Modifier.private:
    for (var elm in obj) {
     if (Reflection.getModifier(elm)==Modifier.private) if (typeof(obj[elm])=="function") fns[fns.length]=obj[elm];
    }
   break;
   case Modifier.public:
    for (var elm in obj) {
     if (Reflection.getModifier(elm)==Modifier.public) if (typeof(obj[elm])=="function") fns[fns.length]=obj[elm];
    }
   break;
   default:
    alert("Unknown protection level: "+protection);
   break;  
  }
  return fns;
 },
 getMethod:function(obj,name) {
  for (var elm in obj) {
   if (elm==name&&typeof(obj[elm])=="function") return obj[elm];
  }
  return null;
 },
 getParameterNames:function(fn) {
  var fnStripped=fn.toString().replace(/\t*| */g,'');
  fnStripped=fnStripped.substr(fnStripped.indexOf("function(")+9);
  fnStripped=fnStripped.substring(0,fnStripped.indexOf(')'));
  return fnStripped.split(',');
 },
 // Field reflection
 getFields:function(obj,protection) {
  if (protection==null) protection=Modifier.public|Modifier.private;
  var flds=[];
  switch (protection) {
   case Modifier.public|Modifier.private:
    for (var elm in obj) if (typeof(obj[elm])!="function") flds[flds.length]=obj[elm];
   break;
   case Modifier.private:
    for (var elm in obj) if (Reflection.getModifier(elm)==Modifier.private) if (typeof(obj[elm])!="function") flds[flds.length]=obj[elm];
   break;
   case Modifier.public:
    for (var elm in obj) if (Reflection.getModifier(elm)==Modifier.public) if (typeof(obj[elm])!="function") flds[flds.length]=obj[elm];
   break;
   default:
    alert("Unknown protection level: "+protection);
   break;  
  }
  return flds;
 },
// Private methods
 _removeWhiteSpace:function(str) {
  return str.replace(/\t|^ +/g,'');
 },
 _getCodeLines:function(fn) {
  return fn.split('\n');
 }
}
Figure 8 - Reflection.js source


// Reflection extensions
Reflection.getAnnotations=function(fn) {
 if (fn) {
  var codeLines=Reflection._getCodeLines(Reflection._removeWhiteSpace(fn.toString()));
  var annotations=[];
  for (var i=0;i<codeLines.length;i++) {
   if (codeLines[i].indexOf("///")==0) {
    try {
     annotations[annotations.length]=Reflection._getAnnotation(codeLines[i]);
    } catch (e) {
     if (e.number&0xFFFF==5007) {
      // throw missing attribute exception
      alert(e.message);
     }
    }
    
   }
  }
  return annotations;
  
 } else {
  // Throw object expected.
 }   
}
Reflection.getNamedAnnotation=function(fn,name) {
 if (fn) {
  var codeLines=Reflection._getCodeLines(Reflection._removeWhiteSpace(fn.toString()));
  var annotations=[];
  for (var i=0;i<codeLines.length;i++) {
   if (codeLines[i].indexOf("///")==0) {
    try {
     if (Reflection._parseAnnotationName(codeLines[i]))return Reflection._getAnnotation(codeLines[i]);
    } catch (e) {
     if (e.number&0xFFFF==5007) {
      // throw missing attribute exception
      alert(e.message);
     }
    }
   }
  }
 }
 return null;
}
Reflection._getAnnotation=function(str) {
 var annotation=eval("new "+Reflection._parseAnnotationName(str)+"()");
 annotation=Reflection._setNameValuePairs(annotation,str);
 return annotation; 
}
Reflection._setNameValuePairs=function(annotationInstance,annotationInfo) {
 var annotationName=Reflection._parseAnnotationName(annotationInfo);
 var pairs=annotationInfo.substr(annotationInfo.indexOf(annotationName)+annotationName.length+1);
 pairs=pairs.substr(0,pairs.indexOf(')')).split(',');
 if (pairs.length==1&&pairs[0].indexOf('=')==-1) {
  annotationInstance=eval("new "+annotationName+'('+pairs[0]+");");
  return annotationInstance;
 } else {
  for (var i=0; i<pairs.length; i++) {
   var nameValue=pairs[i].split('=');
   try {
    annotationInstance._setMember(nameValue[0],nameValue[1]);
   } catch (e) {
    
    alert(e.message + " (" + e.number+")<BR>");
   }
  }
 }
 return annotationInstance;
}
Reflection._parseAnnotationName=function(str) {
 return str.substring(str.indexOf(
'@')+1,str.indexOf('(')).replace(/ /g,'');
}
Annotation=function() {
}
Annotation.prototype._setMember=function(name,value) {
 eval("this."+name+'='+value);
}
Figure 9 - Annotation.js source

 

Comments