All Downloads are FREE. Search and download functionalities are using the official Maven repository.

joo.JooClassDeclaration.as Maven / Gradle / Ivy

There is a newer version: 4.1.8
Show newest version
/*
 * Copyright 2009 CoreMedia AG
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 
 *
 * Unless required by applicable law or agreed to in writing, 
 * software distributed under the License is distributed on an "AS
 * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
 * express or implied. See the License for the specific language 
 * governing permissions and limitations under the License.
 */

// JangarooScript runtime support. Author: Frank Wienberg

package joo {

public class JooClassDeclaration extends NativeClassDeclaration {

  public static const STATE_EVENT_AFTER_INIT_MEMBERS:String = 'afterInitMembers';
  private static var STATE_BY_EVENT:Object;
  STATE_BY_EVENT = {
    'afterInitMembers': STATE_MEMBERS_INITIALIZED
  };

  internal var
          package_ : Object,
          type : String = MemberDeclaration.MEMBER_TYPE_CLASS,
          namespace_ : String = MemberDeclaration.NAMESPACE_INTERNAL,
          className : String,
          native_ : Boolean = false,
          extends_ : String = "Object",
          level : int = -1,
          privateStatics : Object,
          memberDeclarations : * /* Function, then Array */,
          memberDeclarationsByQualifiedName : Object,
          staticInitializers : Array/**/,
          publicStaticMethodNames : Array,
          implementingClasses: Array/*Class*/,
          dependencies : Array,
          stateListeners: Object;
  /**
   * The metadata (annotations) associated with this class.
   */
  public var metadata : Object;

  private static const DECLARATION_PATTERN_CLASS:RegExp =
    /^\s*((public|internal|final|dynamic)\s+)*class\s+([a-zA-Z$_0-9]+)(\s+extends\s+([a-zA-Z$_0-9.]+))?(\s+implements\s+([a-zA-Z$_0-9.,\s]+))?\s*$/;
  private static const DECLARATION_PATTERN_INTERFACE:RegExp =
    /^\s*((public|internal)\s+)?interface\s+([a-zA-Z$_0-9]+)(\s+extends\s+([a-zA-Z$_0-9.,\s]+))?\s*$/;
  private static const DECLARATION_PATTERN_OTHER:RegExp =
    /^\s*((public|internal)\s+)?(const|var|function|namespace)\s+([a-zA-Z$_0-9]+)\s*$/;

  public function JooClassDeclaration(packageDef:String, metadata:Object, classDef:String, inheritanceLevel:int, memberDeclarations:Function, publicStaticMethodNames:Array, dependencies:Array) {
    this.stateListeners = {};
    this.metadata = metadata;
    var packageName : String = packageDef.split(/\s+/)[1] || "";
    this.package_ = getOrCreatePackage(packageName);
    var classMatch : Array = classDef.match(DECLARATION_PATTERN_CLASS);
    var interfaces : String;
    if (classMatch) {
      this.className = classMatch[3];
      if (classMatch[5]) {
        this.extends_ = classMatch[5];
      }
      interfaces = classMatch[7];
    } else {
      classMatch = classDef.match(DECLARATION_PATTERN_INTERFACE);
      if (classMatch) {
        this.className = classMatch[3];
        this.type = MemberDeclaration.MEMBER_TYPE_INTERFACE;
        interfaces = classMatch[5];
      } else {
        classMatch = classDef.match(DECLARATION_PATTERN_OTHER);
        if (classMatch) {
          this.className = classMatch[4];
          this.type = classMatch[3];
        }
      }
    }
    if (!classMatch) {
      throw new Error("SyntaxError: \""+classDef+"\" does not match.");
    }
    this.level = inheritanceLevel;
    this.namespace_ = classMatch[2];
    var fullClassName : String = this.className;
    if (packageName) {
      fullClassName = packageName+"."+this.className;
    }
    this.interfaces = interfaces ? interfaces.split(/\s*,\s*/) : [];
    this.memberDeclarations = memberDeclarations;
    this.publicStaticMethodNames = publicStaticMethodNames;
    this.dependencies = dependencies;
    this.privateStatics = {};
    this.publicConstructor = getQualifiedObject(fullClassName);
    if (publicConstructor) {
      this.native_ = true;
    } else if (isClass() || isInterface()) {
      this.package_[this.className] = this.publicConstructor = createInitializingConstructor(this);
      for (var i:int = 0; i < publicStaticMethodNames.length; i++) {
        createInitializingStaticMethod(publicStaticMethodNames[i]);
      }
    } else if (isFunction()) {
      this.package_[this.className] = createInitializingPackageMethod(this);
    } else if (isConst() || isVar()) {
      this.package_[this.className] = typeof this.memberDeclarations === "function" ?
        createInitializingPackageField(this) : this.memberDeclarations;
    }
    this.create(fullClassName, publicConstructor);
    this._processMetadata(); // for early annotation processing like adding dependencies
  }

  public function addStateListener(state:String, listener:Function):void {
    if (this.state >= STATE_BY_EVENT[state]) {
      // when already past this state, call back immediately:
      listener(this);
    } else {
      var stateListeners:Array = this.stateListeners[state];
      if (!stateListeners) {
        this.stateListeners[state] = stateListeners = [];
      }
      stateListeners.push(listener);
    }
  }

  public function removeStateListener(state:String, listener:Function):void {
    var stateListeners:Array = this.stateListeners[state];
    if (stateListeners) {
      var pos:int = stateListeners.indexOf(listener);
      if (pos !== -1) {
        stateListeners.splice(pos, 1);
      }
    }
  }

  public function isClass() : Boolean {
    return this.type === MemberDeclaration.MEMBER_TYPE_CLASS;
  }

  public function isInterface() : Boolean {
    return this.type === MemberDeclaration.MEMBER_TYPE_INTERFACE;
  }

  public function isFunction() : Boolean {
    return this.type === MemberDeclaration.MEMBER_TYPE_FUNCTION;
  }

  public function isConst() : Boolean {
    return this.type === MemberDeclaration.MEMBER_TYPE_CONST;
  }

  public function isVar() : Boolean {
    return this.type === MemberDeclaration.MEMBER_TYPE_VAR;
  }

  internal function addToInterfaces(clazz:Function):void {
    var scd:JooClassDeclaration = superClassDeclaration as JooClassDeclaration;
    if (scd) {
      scd.addToInterfaces(clazz);
    }
    for (var i:int = 0; i < interfaces.length; i++) {
      JooClassDeclaration(interfaces[i]).addImplementingClass(clazz);
    }
  }

  internal function addImplementingClass(clazz:Function):void {
    //trace("#### adding " + clazz + " to interface " + fullClassName + ":");
    var implementingClasses:Array = [];
    //trace("####   before: " + this.implementingClasses.join(", "));
    for (var i:int = 0; i < this.implementingClasses.length; i++) {
      var implementingClass:Function = this.implementingClasses[i];
      // do not add new clazz if it or a superclass is already in the set:
      if (clazz === implementingClass || clazz.prototype instanceof implementingClass) {
        //trace("####   " + implementingClass + " already present, nothing changed.");
        return; // class or superclass already added!
      }
      // remove all subclasses from the set (keep only non-subclasses):
      if (!(implementingClass.prototype instanceof clazz)) {
        implementingClasses.push(implementingClass);
      }
    }
    implementingClasses.push(clazz);
    this.implementingClasses = implementingClasses;
    //trace("####   after: " + this.implementingClasses.join(", "));
    addToInterfaces(clazz);
  }

  override public function isInstance(obj:Object):Boolean {
    return Public ? isInterface() ? implementingClasses.some(function(implementingClass:Function):Boolean {
      return obj instanceof implementingClass;
    }) : obj instanceof Public // cannot invoke super, since BootstrapClassLoader does not support super calls!
    : false; // class not even completed, cannot have instances!
  }

  public function isNamespace() : Boolean {
    return this.type === MemberDeclaration.MEMBER_TYPE_NAMESPACE;
  }

  public function isNative() : Boolean {
    return this.native_;
  }

  internal override function doComplete() : void {
    this.superClassDeclaration = classLoader.getRequiredClassDeclaration(this.extends_);
    this.superClassDeclaration.complete();
    var proto:Object = this.native_ ? publicConstructor.prototype : new (this.superClassDeclaration.Public)();
    this.Public = NativeClassDeclaration.createEmptyConstructor(proto);
  }

  internal function initMembers() : void {
    this.staticInitializers = [];
    var memberDeclarations:Array = this.memberDeclarations(this.privateStatics);
    this.memberDeclarations = [];
    this.memberDeclarationsByQualifiedName = {};
    this.constructor_ = isNative() ? publicConstructor : null;
    var metadata:Object = {};
    for (var i:int = 0; i < memberDeclarations.length; ++i) {
      var item:* = memberDeclarations[i];
      switch (typeof item) {
        case "function":
          this.staticInitializers.push(item);
          break;
        case "string":
          var memberDeclaration:MemberDeclaration = MemberDeclaration.create(item);
          if (memberDeclaration) {
            memberDeclaration.metadata = metadata;
            metadata = {};
            if (!memberDeclaration.isNative()) {
              if (++i >= memberDeclarations.length) {
                throw new Error(this + ": Member expected after modifiers '" + item + "'.");
              }
              var member:* = memberDeclarations[i];
            }
            switch (memberDeclaration.memberType) {
              case MemberDeclaration.MEMBER_TYPE_FUNCTION:
                this.initMethod(memberDeclaration, Function(member));
                break;
              case MemberDeclaration.MEMBER_TYPE_CLASS:
                //noinspection UnnecessaryLocalVariableJS
                var helperInheritanceLevel:int = member;
                var helperMemberDeclarations:Function = memberDeclarations[++i];
                var helperStatics:Array = memberDeclarations[++i];
                var secondaryClass:NativeClassDeclaration = classLoader.prepare("package " + this.fullClassName, item,
                  helperInheritanceLevel, helperMemberDeclarations,
                  helperStatics, [], runtimeApiVersion, compilerVersion).complete();

                // revert that the class has already set itself as a static member, because it considers the primary class its package.
                // otherwise, the static initializer that inits the secondary class may not be set by _storeMember()!
                delete getQualifiedObject(fullClassName)[memberDeclaration.memberName];

                memberDeclaration._static = true;
                memberDeclaration.initSlot(level);
                this._storeMember(memberDeclaration, createSecondaryClassInitializer(secondaryClass.publicConstructor));
                break;
              default:
                for (var memberName:String in member) {
                  this._storeMember(this._createMemberDeclaration(memberDeclaration, {memberName: memberName}), member[memberName]);
                }
            }
          }
          break;
        case "object":
          SystemClassLoader.addToMetadata(metadata, item);
      }
    }
    if (!isInterface() && !native_) {
      if (!superClassDeclaration.constructor_) {
        throw new Error("Class " + fullClassName + " extends " + superClassDeclaration.fullClassName + " whose constructor is not defined!");
      }
      // only add "super$..." for backwards compatibility, and never if we are a JavaScriptObject:
      if (!(Public.prototype instanceof JavaScriptObject)) {
        Public.prototype["super$" + level] = superClassDeclaration.constructor_;
      }
      if (!this.constructor_) {
        // no explicit constructor found
        // generate constructor invoking super() and initialize it from the "collecting" constructor:
        _setConstructor(createSuperConstructor(this));
      }
    }
  }

  private static function createSecondaryClassInitializer(secondaryClass:Function):Function {
    return function():Function {
      // init secondary class together with primary class:
      return classLoader.init(secondaryClass);
    };
  }

  //noinspection JSFieldCanBeLocalInspection
  private static var jooClasstoString:Function;
  jooClasstoString = function():String {
    return "[class " + this.$class.className + "]";
  };

  internal function _setConstructor(constructor_:Function):void {
    // replay all non-private static members collected so far for new constructor_ function:
    for (var i:int = 0; i < memberDeclarations.length; i++) {
      var memberDeclaration:MemberDeclaration = memberDeclarations[i];
      if (memberDeclaration.isStatic() && !memberDeclaration.isPrivate()) {
        memberDeclaration.storeMember(constructor_);
      }
    }
    constructor_['$class'] = this;
    if (superClassDeclaration) {
      constructor_['superclass'] = superClassDeclaration.Public.prototype; // Ext Core compatibility!
    }
    constructor_.prototype = Public.prototype;
    // do not overwrite 'constructor' explicitly if this class is marked as a simple JavaScript object by inheriting from joo.JavaScriptObject!
    if (!(Public.prototype instanceof JavaScriptObject)) {
      Public.prototype['constructor'] = constructor_;
    }
    constructor_.toString = jooClasstoString;
    // replace initializing constructor by the real one:
    package_[className] = this.constructor_ = constructor_;
  }

  private static function createSuperConstructor(classDeclaration:JooClassDeclaration):Function {
    return function generatedConstructor$():void {
      classDeclaration.superClassDeclaration.constructor_.call(this);
    };
  }

  internal function initMethod(memberDeclaration : MemberDeclaration, member : Function) : void {
    if (memberDeclaration.memberName == this.className && !memberDeclaration.isStatic()) {
      if (memberDeclaration.getterOrSetter) {
        throw new Error(this+": Class name cannot be used for getter or setter: "+memberDeclaration);
      }
      if (!native_ && !memberDeclaration.isNative()) {
        _setConstructor(member);
      }
    } else {
      memberDeclaration.initSlot(this.level);
      if (memberDeclaration.isNative()) {
        member = memberDeclaration.getNativeMember(this.publicConstructor);
      }
      if (memberDeclaration.isMethod()) {
        if (this.extends_!="Object") {
          var superMethod : Function = memberDeclaration.retrieveMember(superClassDeclaration.Public.prototype);
        }
        var overrides : Boolean = !!superMethod
          && superMethod!==member
          && superMethod!==Object['prototype'][memberDeclaration.memberName];
        if (overrides !== memberDeclaration.isOverride()) {
          var msg : String = overrides
                  ? "Method overrides without 'override' modifier"
                  : "Method with 'override' modifier does not override";
          throw new Error(this+": "+msg+": "+memberDeclaration);
        }
        if (overrides) {
          // found overriding: store super class' method as private member:
          this._storeMember(this._createMemberDeclaration(memberDeclaration, {_namespace: MemberDeclaration.NAMESPACE_PRIVATE}), superMethod);
        }
      }
      this._storeMember(memberDeclaration, member);
    }
  }

  internal function _createMemberDeclaration(memberDeclaration : MemberDeclaration, changedProperties : Object) : MemberDeclaration {
    var newMemberDeclaration : MemberDeclaration = memberDeclaration.clone(changedProperties);
    newMemberDeclaration.initSlot(this.level);
    return newMemberDeclaration;
  }

  internal function _storeMember(memberDeclaration : MemberDeclaration, value : Object) : void {
    this.memberDeclarations.push(memberDeclaration);
    this.memberDeclarationsByQualifiedName[memberDeclaration.getQualifiedName()] = memberDeclaration;
    memberDeclaration.value = value;
    var _static : Boolean = memberDeclaration.isStatic();
    var _private : Boolean = memberDeclaration.isPrivate();

    if (_static && memberDeclaration.hasInitializer()) {
      this.staticInitializers.push(memberDeclaration);
    }
    _processMetadata(memberDeclaration);
    var target : Object = _static ? _private ? privateStatics : constructor_ : Public.prototype;

    if (target) {
      memberDeclaration.storeMember(target);
    }
    // if constructor_ is not yet set, static non-private members will be added later by _setConstructor().
  }

  internal function _processMetadata(memberDeclaration : MemberDeclaration = null):void {
    var metaPackage:* = getQualifiedObject("joo.meta");
    if (metaPackage) {
      var metadata:Object = memberDeclaration ? memberDeclaration.metadata : this.metadata;
      if (metadata) {
        for (var metaFunctionName:String in metadata) {
          if (metaFunctionName in metaPackage) {
            metaPackage[metaFunctionName](this, memberDeclaration, metadata[metaFunctionName]);
          }
        }
      }
    }
  }

  internal override function doInit() : void {
    if (!isClass() && !isInterface()) {
      return;
    }
    this.superClassDeclaration.init();
    for (var j:int = 0; j < interfaces.length; j++) {
      interfaces[j] = classLoader.getRequiredClassDeclaration(interfaces[j]).init();
    }
    this.initMembers();
    if (isInterface()) {
      implementingClasses = [];
    } else {
      addToInterfaces(constructor_);
    }
    this.state = STATE_MEMBERS_INITIALIZED;
    fireStateEvent(STATE_EVENT_AFTER_INIT_MEMBERS);
    for (var i:int=0; i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy