content: // Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
  // for details. All rights reserved. Use of this source code is governed by a
  // BSD-style license that can be found in the LICENSE file.
  
  part of scanner;
  
  class ClassElementParser extends PartialParser {
    ClassElementParser(Listener listener) : super(listener);
  
    Token parseClassBody(Token token) => fullParseClassBody(token);
  }
  
  class PartialClassElement extends ClassElementX {
    final Token beginToken;
    final Token endToken;
    ClassNode cachedNode;
  
    PartialClassElement(String name,
                        Token this.beginToken,
                        Token this.endToken,
                        Element enclosing,
                        int id)
        : super(name, enclosing, id, STATE_NOT_STARTED);
  
    void set supertypeLoadState(int state) {
      assert(state == supertypeLoadState + 1);
      assert(state <= STATE_DONE);
      super.supertypeLoadState = state;
    }
  
    void set resolutionState(int state) {
      assert(state == resolutionState + 1);
      assert(state <= STATE_DONE);
      super.resolutionState = state;
    }
  
    ClassNode parseNode(Compiler compiler) {
      if (cachedNode != null) return cachedNode;
      compiler.withCurrentElement(this, () {
        compiler.parser.measure(() {
          MemberListener listener = new MemberListener(compiler, this);
          Parser parser = new ClassElementParser(listener);
          Token token = parser.parseTopLevelDeclaration(beginToken);
          assert(identical(token, endToken.next));
          cachedNode = listener.popNode();
          assert(listener.nodes.isEmpty);
        });
        compiler.patchParser.measure(() {
          if (isPatched) {
            // TODO(lrn): Perhaps extract functionality so it doesn't
            // need compiler.
            compiler.patchParser.parsePatchClassNode(patch);
          }
        });
      });
      return cachedNode;
    }
  
    Token position() => beginToken;
  
    // TODO(johnniwinther): Ensure that modifiers are always available.
    Modifiers get modifiers =>
        cachedNode != null ? cachedNode.modifiers : Modifiers.EMPTY;
  }
  
  class MemberListener extends NodeListener {
    final ClassElement enclosingElement;
  
    MemberListener(DiagnosticListener listener,
                   Element enclosingElement)
        : this.enclosingElement = enclosingElement,
          super(listener, enclosingElement.getCompilationUnit());
  
    bool isConstructorName(Node nameNode) {
      if (enclosingElement == null ||
          enclosingElement.kind != ElementKind.CLASS) {
        return false;
      }
      String name;
      if (nameNode.asIdentifier() != null) {
        name = nameNode.asIdentifier().source;
      } else {
        Send send = nameNode.asSend();
        name = send.receiver.asIdentifier().source;
      }
      return enclosingElement.name == name;
    }
  
    // TODO(johnniwinther): Remove this method.
    String getMethodNameHack(Node methodName) {
      Send send = methodName.asSend();
      if (send == null) {
        if (isConstructorName(methodName)) return '';
        return methodName.asIdentifier().source;
      }
      Identifier receiver = send.receiver.asIdentifier();
      Identifier selector = send.selector.asIdentifier();
      Operator operator = selector.asOperator();
      if (operator != null) {
        assert(identical(receiver.source, 'operator'));
        // TODO(ahe): It is a hack to compare to ')', but it beats
        // parsing the node.
        bool isUnary = identical(operator.token.next.next.stringValue, ')');
        return Elements.constructOperatorName(operator.source, isUnary);
      } else {
        if (receiver == null || receiver.source != enclosingElement.name) {
          listener.reportError(send.receiver,
                                   MessageKind.INVALID_CONSTRUCTOR_NAME,
                                   {'name': enclosingElement.name});
        }
        return selector.source;
      }
    }
  
    void endMethod(Token getOrSet, Token beginToken, Token endToken) {
      super.endMethod(getOrSet, beginToken, endToken);
      FunctionExpression method = popNode();
      pushNode(null);
      bool isConstructor = isConstructorName(method.name);
      String name = getMethodNameHack(method.name);
      ElementKind kind = ElementKind.FUNCTION;
      if (isConstructor) {
        if (getOrSet != null) {
          recoverableError('illegal modifier', token: getOrSet);
        }
        kind = ElementKind.GENERATIVE_CONSTRUCTOR;
      } else if (getOrSet != null) {
        kind = (identical(getOrSet.stringValue, 'get'))
               ? ElementKind.GETTER : ElementKind.SETTER;
      }
      Element memberElement =
          new PartialFunctionElement(name, beginToken, getOrSet, endToken,
                                     kind, method.modifiers, enclosingElement);
      addMember(memberElement);
    }
  
    void endFactoryMethod(Token beginToken, Token endToken) {
      super.endFactoryMethod(beginToken, endToken);
      FunctionExpression method = popNode();
      pushNode(null);
      String name = getMethodNameHack(method.name);
      Identifier singleIdentifierName = method.name.asIdentifier();
      if (singleIdentifierName != null && singleIdentifierName.source == name) {
        if (name != enclosingElement.name) {
          listener.reportError(singleIdentifierName,
                                   MessageKind.INVALID_UNNAMED_CONSTRUCTOR_NAME,
                                   {'name': enclosingElement.name});
        }
      }
      ElementKind kind = ElementKind.FUNCTION;
      Element memberElement =
          new PartialFunctionElement(name, beginToken, null, endToken,
                                     kind, method.modifiers, enclosingElement);
      addMember(memberElement);
    }
  
    void endFields(int count, Token beginToken, Token endToken) {
      super.endFields(count, beginToken, endToken);
      VariableDefinitions variableDefinitions = popNode();
      Modifiers modifiers = variableDefinitions.modifiers;
      pushNode(null);
      void buildFieldElement(String name, Element fields) {
        Element element =
            new VariableElementX(name, fields, ElementKind.FIELD, null);
        addMember(element);
      }
      buildFieldElements(modifiers, variableDefinitions.definitions,
                         enclosingElement,
                         buildFieldElement, beginToken, endToken);
    }
  
    void endInitializer(Token assignmentOperator) {
      pushNode(null); // Super expects an expression, but
                      // ClassElementParser just skips expressions.
      super.endInitializer(assignmentOperator);
    }
  
    void endInitializers(int count, Token beginToken, Token endToken) {
      pushNode(null);
    }
  
    void addMember(Element memberElement) {
      for (Link link = metadata; !link.isEmpty; link = link.tail) {
        memberElement.addMetadata(link.head);
      }
      metadata = const Link<MetadataAnnotation>();
      enclosingElement.addMember(memberElement, listener);
    }
  
    void endMetadata(Token beginToken, Token periodBeforeName, Token endToken) {
      popNode(); // Discard arguments.
      if (periodBeforeName != null) {
        popNode(); // Discard name.
      }
      popNode(); // Discard node (Send or Identifier).
      pushMetadata(new PartialMetadataAnnotation(beginToken, endToken));
    }
  }
  