<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author:  Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------

#include "IncrementalParser.h"
#include "ASTNodeEraser.h"

#include "ASTDumper.h"
#include "ChainedConsumer.h"
#include "DeclExtractor.h"
#include "DynamicLookup.h"
#include "ValuePrinterSynthesizer.h"
#include "cling/Interpreter/CIFactory.h"
#include "cling/Interpreter/Interpreter.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclGroup.h"
#include "clang/Basic/FileManager.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Parse/Parser.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Serialization/ASTWriter.h"

#include "llvm/LLVMContext.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_os_ostream.h"

#include <ctime>
#include <iostream>
#include <stdio.h>
#include <sstream>

using namespace clang;

namespace cling {
  IncrementalParser::IncrementalParser(Interpreter* interp,
                                       int argc, const char* const *argv,
                                       const char* llvmdir):
    m_Interpreter(interp),
    m_DynamicLookupEnabled(false),
    m_Consumer(0),
    m_SyntaxOnly(false)
  {
    CompilerInstance* CI
      = CIFactory::createCI(llvm::MemoryBuffer::getMemBuffer("", "CLING"),
                            argc, argv, llvmdir);
    assert(CI && "CompilerInstance is (null)!");
    m_CI.reset(CI);
    m_SyntaxOnly
      = CI->getFrontendOpts().ProgramAction == clang::frontend::ParseSyntaxOnly;

    CreateSLocOffsetGenerator();

    m_Consumer = dyn_cast<ChainedConsumer>(&CI->getASTConsumer());
    assert(m_Consumer && "Expected ChainedConsumer!");
    // Add consumers to the ChainedConsumer, which owns them
    EvaluateTSynthesizer* ES = new EvaluateTSynthesizer(interp);
    m_Consumer->Add(ChainedConsumer::kEvaluateTSynthesizer, ES);

    DeclExtractor* DE = new DeclExtractor();
    m_Consumer->Add(ChainedConsumer::kDeclExtractor, DE);

    ValuePrinterSynthesizer* VPS = new ValuePrinterSynthesizer(interp);
    m_Consumer->Add(ChainedConsumer::kValuePrinterSynthesizer, VPS);
    m_Consumer->Add(ChainedConsumer::kASTDumper, new ASTDumper());
    if (!m_SyntaxOnly) {
      CodeGenerator* CG = CreateLLVMCodeGen(CI->getDiagnostics(),
                                            "cling input",
                                            CI->getCodeGenOpts(),
                                            *m_Interpreter->getLLVMContext()
                                            );
      assert(CG && "No CodeGen?!");
      m_Consumer->Add(ChainedConsumer::kCodeGenerator, CG);
    }
    m_Parser.reset(new Parser(CI->getPreprocessor(), CI->getSema(),
                              false /*skipFuncBodies*/));
    CI->getPreprocessor().EnterMainSourceFile();
    // Initialize the parser after we have entered the main source file.
    m_Parser->Initialize();
    // Perform initialization that occurs after the parser has been initialized
    // but before it parses anything. Initializes the consumers too.
    CI->getSema().Initialize();
  }

  void IncrementalParser::beginTransaction() {
    Transaction* NewCurT = new Transaction();
    Transaction* OldCurT = m_Consumer->getTransaction();
    m_Consumer->setTransaction(NewCurT);
    // If we are in the middle of transaction and we see another begin
    // transaction - it must be nested transaction.
    if (OldCurT && !OldCurT->isCompleted()) {
      OldCurT->addNestedTransaction(NewCurT);
      return;
    }

    m_Transactions.push_back(NewCurT);
  }

  void IncrementalParser::endTransaction() const {
    Transaction* CurT = m_Consumer->getTransaction();
    CurT->setCompleted();
    const DiagnosticsEngine& Diags = getCI()->getSema().getDiagnostics();

    //TODO: Make the enum orable.
    if (Diags.getNumWarnings() > 0)
      CurT->setIssuedDiags(Transaction::kWarnings);

    if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
      CurT->setIssuedDiags(Transaction::kErrors);

    if (CurT->isNestedTransaction()) {
      assert(!CurT->getParent()->isCompleted()
             && "Parent transaction completed!?");
      CurT = m_Consumer->getTransaction()->getParent();
    }
  }

  void IncrementalParser::commitTransaction(const Transaction* T) const {
    assert(T->isCompleted() && "Transaction not ended!?");
    const Transaction* OldT = m_Consumer->getTransaction();
    m_Consumer->setTransaction(T);
    commitCurrentTransaction();
    m_Consumer->setTransaction(OldT);
  }

  void IncrementalParser::commitCurrentTransaction() const {
    Transaction* CurT = m_Consumer->getTransaction();
    assert(CurT->isCompleted() && "Transaction not ended!?");

    // Check for errors...
    if (CurT->getIssuedDiags() == Transaction::kErrors) {
      rollbackTransaction(CurT);
      return;
    }

    // We are sure it's safe to pipe it through
    for (Transaction::const_iterator I = CurT->decls_begin(),
           E = CurT->decls_end(); I != E; ++I) {
      // if an error was seen somewhere in the consumer chain rollback
      if (!m_Consumer->HandleTopLevelDecl(*I)) {
        rollbackTransaction(CurT);
        return;
      }
    }

    // Pull all template instantiations in that came from the consumers.
    getCI()->getSema().PerformPendingInstantiations();

    m_Consumer->HandleTranslationUnit(getCI()->getASTContext());
    CurT->setState(Transaction::kCommitted);
  }

  void IncrementalParser::rollbackTransaction(Transaction* T) const {
    ASTNodeEraser NodeEraser(&getCI()->getSema());

    for (Transaction::const_reverse_iterator I = T->rdecls_begin(),
           E = T->rdecls_end(); I != E; ++I) {
      const DeclGroupRef& DGR = (*I);

      for (DeclGroupRef::const_iterator
             Di = DGR.end() - 1, E = DGR.begin() - 1; Di != E; --Di) {
        DeclContext* DC = (*Di)->getDeclContext();
        assert(DC == (*Di)->getLexicalDeclContext() && \
               "Cannot handle that yet");

        // Get rid of the declaration. If the declaration has name we should
        // heal the lookup tables as well
        bool Successful = NodeEraser.RevertDecl(*Di);
        assert(Successful && "Cannot handle that yet!");
      }
    }

    T->setState(Transaction::kRolledBack);
  }

  // Each input line is contained in separate memory buffer. The SourceManager
  // assigns sort-of invalid FileID for each buffer, i.e there is no FileEntry
  // for the MemoryBuffer's FileID. That in turn is problem because invalid
  // SourceLocations are given to the diagnostics. Thus the diagnostics cannot
  // order the overloads, for example
  //
  // Our work-around is creating a virtual file, which doesn't exist on the disk
  // with enormous size (no allocation is done). That file has valid FileEntry
  // and so on... We use it for generating valid SourceLocations with valid
  // offsets so that it doesn't cause any troubles to the diagnostics.
  //
  // +---------------------+
  // | Main memory buffer  |
  // +---------------------+
  // |  Virtual file SLoc  |
  // |    address space    |<-----------------+
  // |         ...         |<------------+    |
  // |         ...         |             |    |
  // |         ...         |<----+       |    |
  // |         ...         |     |       |    |
  // +~~~~~~~~~~~~~~~~~~~~~+     |       |    |
  // |     input_line_1    | ....+.......+..--+
  // +---------------------+     |       |
  // |     input_line_2    | ....+.....--+
  // +---------------------+     |
  // |          ...        |     |
  // +---------------------+     |
  // |     input_line_N    | ..--+
  // +---------------------+
  //
  void IncrementalParser::CreateSLocOffsetGenerator() {
    SourceManager& SM = getCI()->getSourceManager();
    FileManager& FM = SM.getFileManager();
    const FileEntry* FE
      = FM.getVirtualFile("Interactrive/InputLineIncluder", 1U << 15U, time(0));
    m_VirtualFileID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User);

    assert(!m_VirtualFileID.isInvalid() && "No VirtualFileID created?");
  }

  IncrementalParser::~IncrementalParser() {
     if (GetCodeGenerator()) {
       GetCodeGenerator()->ReleaseModule();
     }
  }

  void IncrementalParser::Initialize() {
    CompilationOptions CO;
    CO.DeclarationExtraction = 0;
    CO.ValuePrinting = 0;
  }

  IncrementalParser::EParseResult
  IncrementalParser::Compile(llvm::StringRef input,
                             const CompilationOptions& Opts) {

    m_Consumer->pushCompilationOpts(Opts);
    EParseResult Result = Compile(input);
    m_Consumer->popCompilationOpts();

    return Result;
  }

  void IncrementalParser::Parse(llvm::StringRef input,
                                llvm::SmallVector<DeclGroupRef, 4>& DGRs) {

    beginTransaction();
    Parse(input);
    endTransaction();
    const Transaction* T = m_Consumer->getTransaction();
    DGRs.append(T->decls_begin(), T->decls_end());
  }

  IncrementalParser::EParseResult
  IncrementalParser::Compile(llvm::StringRef input) {
    // Just in case when Parse is called, we want to complete the transaction
    // coming from parse and then start new one.
    //m_Consumer->HandleTranslationUnit(getCI()->getASTContext());

    // Reset the module builder to clean up global initializers, c'tors, d'tors:
    if (GetCodeGenerator()) {
      GetCodeGenerator()->Initialize(getCI()->getASTContext());
    }

    beginTransaction();
    EParseResult Result = Parse(input);
    endTransaction();

    // Check for errors coming from our custom consumers.
    DiagnosticConsumer& DClient = m_CI->getDiagnosticClient();
    DClient.BeginSourceFile(getCI()->getLangOpts(),
                            &getCI()->getPreprocessor());
    commitCurrentTransaction();

    DClient.EndSourceFile();
    m_CI->getDiagnostics().Reset();

    if (!m_SyntaxOnly) {
      m_Interpreter->runStaticInitializersOnce();
    }

    return Result;
  }

  // Add the input to the memory buffer, parse it, and add it to the AST.
  IncrementalParser::EParseResult
  IncrementalParser::Parse(llvm::StringRef input) {
    if (input.empty()) return IncrementalParser::kSuccess;

    Preprocessor& PP = m_CI->getPreprocessor();
    DiagnosticConsumer& DClient = m_CI->getDiagnosticClient();

    if (!PP.getCurrentLexer()) {
       PP.EnterSourceFile(m_CI->getSourceManager().getMainFileID(),
                          0, SourceLocation());
    }
    PP.enableIncrementalProcessing();

    DClient.BeginSourceFile(m_CI->getLangOpts(), &PP);

    std::ostringstream source_name;
    source_name << "input_line_" << (m_MemoryBuffer.size() + 1);
    llvm::MemoryBuffer* MB
       = llvm::MemoryBuffer::getMemBufferCopy(input, source_name.str());
    m_MemoryBuffer.push_back(MB);
    SourceManager& SM = getCI()->getSourceManager();

    // Create SourceLocation, which will allow clang to order the overload
    // candidates for example
    SourceLocation NewLoc = SM.getLocForStartOfFile(m_VirtualFileID);
    NewLoc = NewLoc.getLocWithOffset(m_MemoryBuffer.size() + 1);

    // Create FileID for the current buffer
    FileID FID = SM.createFileIDForMemBuffer(m_MemoryBuffer.back(),
                                             /*LoadedID*/0,
                                             /*LoadedOffset*/0, NewLoc);

    PP.EnterSourceFile(FID, 0, NewLoc);

    Parser::DeclGroupPtrTy ADecl;

    while (!m_Parser->ParseTopLevelDecl(ADecl)) {
      // If we got a null return and something *was* parsed, ignore it.  This
      // is due to a top-level semicolon, an action override, or a parse error
      // skipping something.
      if (ADecl)
        m_Consumer->HandleTopLevelDecl(ADecl.getAsVal<DeclGroupRef>());
    };

    // Process any TopLevelDecls generated by #pragma weak.
    for (llvm::SmallVector<Decl*,2>::iterator
           I = getCI()->getSema().WeakTopLevelDecls().begin(),
           E = getCI()->getSema().WeakTopLevelDecls().end(); I != E; ++I) {
      m_Consumer->HandleTopLevelDecl(DeclGroupRef(*I));
    }

    getCI()->getSema().PerformPendingInstantiations();

    DClient.EndSourceFile();

    PP.enableIncrementalProcessing(false);

    DiagnosticsEngine& Diag = getCI()->getSema().getDiagnostics();
    if (Diag.hasErrorOccurred())
      return IncrementalParser::kFailed;
    else if (Diag.getNumWarnings())
      return IncrementalParser::kSuccessWithWarnings;

    return IncrementalParser::kSuccess;
  }

  void IncrementalParser::enableDynamicLookup(bool value) {
    m_DynamicLookupEnabled = value;
    Sema& S = m_CI->getSema();
    if (isDynamicLookupEnabled()) {
      assert(!S.ExternalSource && "Already set Sema ExternalSource");
      S.ExternalSource = new DynamicIDHandler(&S);
    }
    else {
      delete S.ExternalSource;
      S.ExternalSource = 0;
    }
  }

  CodeGenerator* IncrementalParser::GetCodeGenerator() const {
    return
      (CodeGenerator*)m_Consumer->getConsumer(ChainedConsumer::kCodeGenerator);
  }

} // namespace cling
<commit_msg>Take care of the leaking transactions in m_Transactions<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author:  Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------

#include "IncrementalParser.h"
#include "ASTNodeEraser.h"

#include "ASTDumper.h"
#include "ChainedConsumer.h"
#include "DeclExtractor.h"
#include "DynamicLookup.h"
#include "ValuePrinterSynthesizer.h"
#include "cling/Interpreter/CIFactory.h"
#include "cling/Interpreter/Interpreter.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclGroup.h"
#include "clang/Basic/FileManager.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Parse/Parser.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Serialization/ASTWriter.h"

#include "llvm/LLVMContext.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_os_ostream.h"

#include <ctime>
#include <iostream>
#include <stdio.h>
#include <sstream>

using namespace clang;

namespace cling {
  IncrementalParser::IncrementalParser(Interpreter* interp,
                                       int argc, const char* const *argv,
                                       const char* llvmdir):
    m_Interpreter(interp),
    m_DynamicLookupEnabled(false),
    m_Consumer(0),
    m_SyntaxOnly(false)
  {
    CompilerInstance* CI
      = CIFactory::createCI(llvm::MemoryBuffer::getMemBuffer("", "CLING"),
                            argc, argv, llvmdir);
    assert(CI && "CompilerInstance is (null)!");
    m_CI.reset(CI);
    m_SyntaxOnly
      = CI->getFrontendOpts().ProgramAction == clang::frontend::ParseSyntaxOnly;

    CreateSLocOffsetGenerator();

    m_Consumer = dyn_cast<ChainedConsumer>(&CI->getASTConsumer());
    assert(m_Consumer && "Expected ChainedConsumer!");
    // Add consumers to the ChainedConsumer, which owns them
    EvaluateTSynthesizer* ES = new EvaluateTSynthesizer(interp);
    m_Consumer->Add(ChainedConsumer::kEvaluateTSynthesizer, ES);

    DeclExtractor* DE = new DeclExtractor();
    m_Consumer->Add(ChainedConsumer::kDeclExtractor, DE);

    ValuePrinterSynthesizer* VPS = new ValuePrinterSynthesizer(interp);
    m_Consumer->Add(ChainedConsumer::kValuePrinterSynthesizer, VPS);
    m_Consumer->Add(ChainedConsumer::kASTDumper, new ASTDumper());
    if (!m_SyntaxOnly) {
      CodeGenerator* CG = CreateLLVMCodeGen(CI->getDiagnostics(),
                                            "cling input",
                                            CI->getCodeGenOpts(),
                                            *m_Interpreter->getLLVMContext()
                                            );
      assert(CG && "No CodeGen?!");
      m_Consumer->Add(ChainedConsumer::kCodeGenerator, CG);
    }
    m_Parser.reset(new Parser(CI->getPreprocessor(), CI->getSema(),
                              false /*skipFuncBodies*/));
    CI->getPreprocessor().EnterMainSourceFile();
    // Initialize the parser after we have entered the main source file.
    m_Parser->Initialize();
    // Perform initialization that occurs after the parser has been initialized
    // but before it parses anything. Initializes the consumers too.
    CI->getSema().Initialize();
  }

  void IncrementalParser::beginTransaction() {
    Transaction* NewCurT = new Transaction();
    Transaction* OldCurT = m_Consumer->getTransaction();
    m_Consumer->setTransaction(NewCurT);
    // If we are in the middle of transaction and we see another begin
    // transaction - it must be nested transaction.
    if (OldCurT && !OldCurT->isCompleted()) {
      OldCurT->addNestedTransaction(NewCurT);
      return;
    }

    m_Transactions.push_back(NewCurT);
  }

  void IncrementalParser::endTransaction() const {
    Transaction* CurT = m_Consumer->getTransaction();
    CurT->setCompleted();
    const DiagnosticsEngine& Diags = getCI()->getSema().getDiagnostics();

    //TODO: Make the enum orable.
    if (Diags.getNumWarnings() > 0)
      CurT->setIssuedDiags(Transaction::kWarnings);

    if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
      CurT->setIssuedDiags(Transaction::kErrors);

    if (CurT->isNestedTransaction()) {
      assert(!CurT->getParent()->isCompleted()
             && "Parent transaction completed!?");
      CurT = m_Consumer->getTransaction()->getParent();
    }
  }

  void IncrementalParser::commitTransaction(const Transaction* T) const {
    assert(T->isCompleted() && "Transaction not ended!?");
    const Transaction* OldT = m_Consumer->getTransaction();
    m_Consumer->setTransaction(T);
    commitCurrentTransaction();
    m_Consumer->setTransaction(OldT);
  }

  void IncrementalParser::commitCurrentTransaction() const {
    Transaction* CurT = m_Consumer->getTransaction();
    assert(CurT->isCompleted() && "Transaction not ended!?");

    // Check for errors...
    if (CurT->getIssuedDiags() == Transaction::kErrors) {
      rollbackTransaction(CurT);
      return;
    }

    // We are sure it's safe to pipe it through
    for (Transaction::const_iterator I = CurT->decls_begin(),
           E = CurT->decls_end(); I != E; ++I) {
      // if an error was seen somewhere in the consumer chain rollback
      if (!m_Consumer->HandleTopLevelDecl(*I)) {
        rollbackTransaction(CurT);
        return;
      }
    }

    // Pull all template instantiations in that came from the consumers.
    getCI()->getSema().PerformPendingInstantiations();

    m_Consumer->HandleTranslationUnit(getCI()->getASTContext());
    CurT->setState(Transaction::kCommitted);
  }

  void IncrementalParser::rollbackTransaction(Transaction* T) const {
    ASTNodeEraser NodeEraser(&getCI()->getSema());

    for (Transaction::const_reverse_iterator I = T->rdecls_begin(),
           E = T->rdecls_end(); I != E; ++I) {
      const DeclGroupRef& DGR = (*I);

      for (DeclGroupRef::const_iterator
             Di = DGR.end() - 1, E = DGR.begin() - 1; Di != E; --Di) {
        DeclContext* DC = (*Di)->getDeclContext();
        assert(DC == (*Di)->getLexicalDeclContext() && \
               "Cannot handle that yet");

        // Get rid of the declaration. If the declaration has name we should
        // heal the lookup tables as well
        bool Successful = NodeEraser.RevertDecl(*Di);
        assert(Successful && "Cannot handle that yet!");
      }
    }

    T->setState(Transaction::kRolledBack);
  }

  // Each input line is contained in separate memory buffer. The SourceManager
  // assigns sort-of invalid FileID for each buffer, i.e there is no FileEntry
  // for the MemoryBuffer's FileID. That in turn is problem because invalid
  // SourceLocations are given to the diagnostics. Thus the diagnostics cannot
  // order the overloads, for example
  //
  // Our work-around is creating a virtual file, which doesn't exist on the disk
  // with enormous size (no allocation is done). That file has valid FileEntry
  // and so on... We use it for generating valid SourceLocations with valid
  // offsets so that it doesn't cause any troubles to the diagnostics.
  //
  // +---------------------+
  // | Main memory buffer  |
  // +---------------------+
  // |  Virtual file SLoc  |
  // |    address space    |<-----------------+
  // |         ...         |<------------+    |
  // |         ...         |             |    |
  // |         ...         |<----+       |    |
  // |         ...         |     |       |    |
  // +~~~~~~~~~~~~~~~~~~~~~+     |       |    |
  // |     input_line_1    | ....+.......+..--+
  // +---------------------+     |       |
  // |     input_line_2    | ....+.....--+
  // +---------------------+     |
  // |          ...        |     |
  // +---------------------+     |
  // |     input_line_N    | ..--+
  // +---------------------+
  //
  void IncrementalParser::CreateSLocOffsetGenerator() {
    SourceManager& SM = getCI()->getSourceManager();
    FileManager& FM = SM.getFileManager();
    const FileEntry* FE
      = FM.getVirtualFile("Interactrive/InputLineIncluder", 1U << 15U, time(0));
    m_VirtualFileID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User);

    assert(!m_VirtualFileID.isInvalid() && "No VirtualFileID created?");
  }

  IncrementalParser::~IncrementalParser() {
     if (GetCodeGenerator()) {
       GetCodeGenerator()->ReleaseModule();
     }
     for (unsigned i = 0; i < m_Transactions.size(); ++i)
       delete m_Transactions[i];
  }

  void IncrementalParser::Initialize() {
    CompilationOptions CO;
    CO.DeclarationExtraction = 0;
    CO.ValuePrinting = 0;
  }

  IncrementalParser::EParseResult
  IncrementalParser::Compile(llvm::StringRef input,
                             const CompilationOptions& Opts) {

    m_Consumer->pushCompilationOpts(Opts);
    EParseResult Result = Compile(input);
    m_Consumer->popCompilationOpts();

    return Result;
  }

  void IncrementalParser::Parse(llvm::StringRef input,
                                llvm::SmallVector<DeclGroupRef, 4>& DGRs) {

    beginTransaction();
    Parse(input);
    endTransaction();
    const Transaction* T = m_Consumer->getTransaction();
    DGRs.append(T->decls_begin(), T->decls_end());
  }

  IncrementalParser::EParseResult
  IncrementalParser::Compile(llvm::StringRef input) {
    // Just in case when Parse is called, we want to complete the transaction
    // coming from parse and then start new one.
    //m_Consumer->HandleTranslationUnit(getCI()->getASTContext());

    // Reset the module builder to clean up global initializers, c'tors, d'tors:
    if (GetCodeGenerator()) {
      GetCodeGenerator()->Initialize(getCI()->getASTContext());
    }

    beginTransaction();
    EParseResult Result = Parse(input);
    endTransaction();

    // Check for errors coming from our custom consumers.
    DiagnosticConsumer& DClient = m_CI->getDiagnosticClient();
    DClient.BeginSourceFile(getCI()->getLangOpts(),
                            &getCI()->getPreprocessor());
    commitCurrentTransaction();

    DClient.EndSourceFile();
    m_CI->getDiagnostics().Reset();

    if (!m_SyntaxOnly) {
      m_Interpreter->runStaticInitializersOnce();
    }

    return Result;
  }

  // Add the input to the memory buffer, parse it, and add it to the AST.
  IncrementalParser::EParseResult
  IncrementalParser::Parse(llvm::StringRef input) {
    if (input.empty()) return IncrementalParser::kSuccess;

    Preprocessor& PP = m_CI->getPreprocessor();
    DiagnosticConsumer& DClient = m_CI->getDiagnosticClient();

    if (!PP.getCurrentLexer()) {
       PP.EnterSourceFile(m_CI->getSourceManager().getMainFileID(),
                          0, SourceLocation());
    }
    PP.enableIncrementalProcessing();

    DClient.BeginSourceFile(m_CI->getLangOpts(), &PP);

    std::ostringstream source_name;
    source_name << "input_line_" << (m_MemoryBuffer.size() + 1);
    llvm::MemoryBuffer* MB
       = llvm::MemoryBuffer::getMemBufferCopy(input, source_name.str());
    m_MemoryBuffer.push_back(MB);
    SourceManager& SM = getCI()->getSourceManager();

    // Create SourceLocation, which will allow clang to order the overload
    // candidates for example
    SourceLocation NewLoc = SM.getLocForStartOfFile(m_VirtualFileID);
    NewLoc = NewLoc.getLocWithOffset(m_MemoryBuffer.size() + 1);

    // Create FileID for the current buffer
    FileID FID = SM.createFileIDForMemBuffer(m_MemoryBuffer.back(),
                                             /*LoadedID*/0,
                                             /*LoadedOffset*/0, NewLoc);

    PP.EnterSourceFile(FID, 0, NewLoc);

    Parser::DeclGroupPtrTy ADecl;

    while (!m_Parser->ParseTopLevelDecl(ADecl)) {
      // If we got a null return and something *was* parsed, ignore it.  This
      // is due to a top-level semicolon, an action override, or a parse error
      // skipping something.
      if (ADecl)
        m_Consumer->HandleTopLevelDecl(ADecl.getAsVal<DeclGroupRef>());
    };

    // Process any TopLevelDecls generated by #pragma weak.
    for (llvm::SmallVector<Decl*,2>::iterator
           I = getCI()->getSema().WeakTopLevelDecls().begin(),
           E = getCI()->getSema().WeakTopLevelDecls().end(); I != E; ++I) {
      m_Consumer->HandleTopLevelDecl(DeclGroupRef(*I));
    }

    getCI()->getSema().PerformPendingInstantiations();

    DClient.EndSourceFile();

    PP.enableIncrementalProcessing(false);

    DiagnosticsEngine& Diag = getCI()->getSema().getDiagnostics();
    if (Diag.hasErrorOccurred())
      return IncrementalParser::kFailed;
    else if (Diag.getNumWarnings())
      return IncrementalParser::kSuccessWithWarnings;

    return IncrementalParser::kSuccess;
  }

  void IncrementalParser::enableDynamicLookup(bool value) {
    m_DynamicLookupEnabled = value;
    Sema& S = m_CI->getSema();
    if (isDynamicLookupEnabled()) {
      assert(!S.ExternalSource && "Already set Sema ExternalSource");
      S.ExternalSource = new DynamicIDHandler(&S);
    }
    else {
      delete S.ExternalSource;
      S.ExternalSource = 0;
    }
  }

  CodeGenerator* IncrementalParser::GetCodeGenerator() const {
    return
      (CodeGenerator*)m_Consumer->getConsumer(ChainedConsumer::kCodeGenerator);
  }

} // namespace cling
<|endoftext|>


<commit_before>/* * This file is part of meego-keyboard *
 *
 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved.
 * Contact: Nokia Corporation (directui@nokia.com)
 *
 * If you have questions regarding the use of this file, please contact
 * Nokia at directui@nokia.com.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation
 * and appearing in the file LICENSE.LGPL included in the packaging
 * of this file.
 */

#include "mimtoolbar.h"
#include "layoutsmanager.h"
#include "mhardwarekeyboard.h"
#include "mvirtualkeyboardstyle.h"
#include "mtoolbarbutton.h"
#include "mtoolbarlabel.h"

#include <mtoolbardata.h>
#include <mtoolbaritem.h>
#include <mtoolbarrow.h>
#include <mtoolbarlayout.h>

#include <MNamespace>
#include <MButton>
#include <QKeySequence>
#include <QGraphicsLinearLayout>
#include <QDebug>
#include <MSceneManager>
#include <mplainwindow.h>
#include <mreactionmap.h>
#include <MScalableImage>

namespace
{
    const QString ObjectNameToolbar("MImToolbar");
    const QString ObjectNameToolbarLeft("VirtualKeyboardToolbarLeft");
    const QString ObjectNameToolbarRight("VirtualKeyboardToolbarRight");
    const QString NameToolbarCopyPasteButton("VirtualKeyboardCopyPasteButton");
};

MImToolbar::MImToolbar(QGraphicsWidget *parent)
    : MStylableWidget(parent),
      textSelected(false),
      copyPasteItem(new MToolbarItem(NameToolbarCopyPasteButton, MInputMethod::ItemButton)),
      leftBar(true, this),
      rightBar(true, this),
      shiftState(ModifierClearState),
      fnState(ModifierClearState)
{
    leftBar.setObjectName(ObjectNameToolbarLeft);
    rightBar.setObjectName(ObjectNameToolbarRight);
    setObjectName(ObjectNameToolbar);

    setupLayout();

    connect(this, SIGNAL(visibleChanged()), this, SLOT(updateVisibility()));
    connect(MTheme::instance(), SIGNAL(themeChangeCompleted()),
            this, SLOT(updateFromStyle()));
}

MImToolbar::~MImToolbar()
{
}

void MImToolbar::setupLayout()
{
    QGraphicsLinearLayout *mainLayout = new QGraphicsLinearLayout(Qt::Vertical, this);
    mainLayout->setContentsMargins(0, 0, 0, 0);

    QGraphicsLinearLayout *rowLayout = new QGraphicsLinearLayout(Qt::Horizontal, mainLayout);

    setupRowLayout(rowLayout, &leftBar, &rightBar);
    mainLayout->insertItem(0, rowLayout);

    resize(geometry().width(), layout()->preferredHeight());
}

QRegion MImToolbar::region() const
{
    QRegion region;

    if (isVisible()) {
        if (currentToolbar && currentToolbar->isCustom()) {
            region = QRegion(mapRectToScene(rect()).toRect());
        } else if (rightBar.isVisible()) {
            region = QRegion(mapRectToScene(rightBar.geometry()).toRect());
        }
    }
    return region;
}

void MImToolbar::handleButtonClick(MToolbarItem *item)
{
    if (!item || item->type() != MInputMethod::ItemButton)
        return;

    foreach(QSharedPointer<const MToolbarItemAction> action, item->actions()) {
        switch (action->type()) {
        case MInputMethod::ActionSendKeySequence:
            sendKeySequence(action->keys());
            break;
        case MInputMethod::ActionSendString:
            sendStringRequest(action->text());
            break;
        case MInputMethod::ActionSendCommand:
            //TODO:not support yet
            break;
        case MInputMethod::ActionCopy:
            emit copyPasteRequest(InputMethodCopy);
            break;
        case MInputMethod::ActionPaste:
            emit copyPasteRequest(InputMethodPaste);
            break;
        case MInputMethod::ActionShowGroup:
            showGroup(action->group());
            break;
        case MInputMethod::ActionHideGroup:
            hideGroup(action->group());
            break;
        case MInputMethod::ActionClose:
            emit closeKeyboardRequest();
            break;
        case MInputMethod::ActionUndefined:
        case MInputMethod::ActionCopyPaste:
            break;
        }
    }
}

void MImToolbar::setSelectionStatus(bool selection)
{
    if (textSelected != selection) {
        textSelected = selection;
        if (isVisible())
            updateVisibility();
    }
}

void MImToolbar::updateVisibility()
{
    if (currentToolbar) {
        foreach (const QSharedPointer<MToolbarItem> item, currentToolbar->items())
        {
            if ((item->showOn() == MInputMethod::VisibleAlways)
                    || (textSelected && item->showOn() == MInputMethod::VisibleWhenSelectingText)
                    || (!textSelected && item->hideOn() == MInputMethod::VisibleWhenSelectingText)) {
                item->setVisible(true);
            }

            if ((!textSelected && item->showOn() == MInputMethod::VisibleWhenSelectingText)
                    || (textSelected && item->hideOn() == MInputMethod::VisibleWhenSelectingText)) {
                item->setVisible(false);
            }
        }
    }
    arrangeWidgets();
}

void MImToolbar::updateFromStyle()
{
    prepareGeometryChange(); // notify scene about changes in bounding rect
}

void MImToolbar::loadCustomWidgets()
{
    if (!currentToolbar) {
        return;
    }

    const M::Orientation orientation = MPlainWindow::instance()->sceneManager()->orientation();
    QSharedPointer<const MToolbarLayout> layout = currentToolbar->layout(orientation);
    QGraphicsLinearLayout *mainLayout = static_cast<QGraphicsLinearLayout*>(this->layout());

    if (!mainLayout) {
        qCritical() << __PRETTY_FUNCTION__ << "Layout does not exist";
    }

    QList<QSharedPointer<const MToolbarRow> > rows = layout->rows();

    //create additional rows if necessary
    for (int n = 0; n < rows.count() - 1; ++n) {
        QSharedPointer<const MToolbarRow> row = rows[n];
        QGraphicsLinearLayout *rowLayout = new QGraphicsLinearLayout(Qt::Horizontal, mainLayout);
        WidgetBar *leftWidget  = new WidgetBar(true, this);
        WidgetBar *rightWidget = new WidgetBar(true, this);

        leftWidget->setObjectName(ObjectNameToolbarLeft);
        rightWidget->setObjectName(ObjectNameToolbarRight);
        setupRowLayout(rowLayout, leftWidget, rightWidget);
        mainLayout->insertItem(n, rowLayout);

        foreach (QSharedPointer<MToolbarItem> item , row->items()) {
            createAndAppendWidget(item, leftWidget, rightWidget);
        }
    }

    //add custom items to bottom row
    QSharedPointer<const MToolbarRow> row = rows.last();
    foreach (QSharedPointer<MToolbarItem> item, row->items()) {
        createAndAppendWidget(item, &leftBar, &rightBar);
    }

    mainLayout->invalidate();
}

void MImToolbar::createAndAppendWidget(QSharedPointer<MToolbarItem> item,
                                       WidgetBar *leftWidget,
                                       WidgetBar *rightWidget)
{
    MWidget *widget = 0;
    WidgetBar *sidebar = 0;

    if (item->alignment() == Qt::AlignLeft) {
        sidebar = leftWidget;
    } else {
        sidebar = rightWidget;
    }
    if (item->type() == MInputMethod::ItemButton) {
        widget = new MToolbarButton(item, sidebar);

        connect(widget, SIGNAL(clicked(MToolbarItem*)),
                this, SLOT(handleButtonClick(MToolbarItem*)));
    } else {
        widget = new MToolbarLabel(item, sidebar);
    }
    customWidgets.append(widget);
    sidebar->append(widget, item->isVisible());
    if (sidebar->count() == 1) {
        sidebar->show();
    }
}

void MImToolbar::setupRowLayout(QGraphicsLinearLayout *rowLayout,
                                WidgetBar *leftWidget,
                                WidgetBar *rightWidget)
{
    rowLayout->setContentsMargins(0, 0, 0, 0);

    // Empty button bars are hidden.
    leftWidget->hide();
    rightWidget->hide();
    // Add the left and right side WidgetBar widgets with a stretch item in between.
    rowLayout->addItem(leftWidget);
    rowLayout->addStretch();
    rowLayout->addItem(rightWidget);

    rowLayout->setAlignment(leftWidget, Qt::AlignBottom);
    rowLayout->setAlignment(rightWidget, Qt::AlignBottom);
}

void MImToolbar::unloadCustomWidgets()
{
    QGraphicsLinearLayout *mainLayout = static_cast<QGraphicsLinearLayout*>(layout());
    QList<QGraphicsLinearLayout*> rows;

    if (!mainLayout) {
        qCritical() << __PRETTY_FUNCTION__ << "Layout does not exist";
    }

    //delete all dynamically created rows
    for (int n = 0; n < mainLayout->count() - 1; ++n) {
        QGraphicsLinearLayout *rowLayout = dynamic_cast<QGraphicsLinearLayout*>(mainLayout->itemAt(n));

        if (rowLayout) {
            WidgetBar *leftBar  = dynamic_cast<WidgetBar*>(rowLayout->itemAt(0));
            WidgetBar *rightBar = dynamic_cast<WidgetBar*>(rowLayout->itemAt(1));

            delete leftBar;
            delete rightBar;
            rows << rowLayout;
        }
    }

    qDeleteAll(rows);
    qDeleteAll(customWidgets);
    customWidgets.clear();
    leftBar.cleanup();
    rightBar.cleanup();
}

void MImToolbar::arrangeWidgets()
{
    if (!layout()) {
        qCritical() << __PRETTY_FUNCTION__ << "Layout does not exist";
    }

    if (isVisible()) {
        for (int n = 0; n < layout()->count(); ++n) {
            QGraphicsLayout *rowLayout = dynamic_cast<QGraphicsLayout*>(layout()->itemAt(n));
            if (rowLayout) {
                rowLayout->invalidate();
                rowLayout->activate();
            }
        }

        layout()->invalidate();
        layout()->activate();
        resize(geometry().width(), layout()->preferredHeight());
    }

    emit regionUpdated();
}

void MImToolbar::showGroup(const QString &group)
{
    bool changed = false;

    if (!currentToolbar) {
        return;
    }

    foreach (const QSharedPointer<MToolbarItem> item, currentToolbar->items())
    {
        if (item->group() == group && !(item->isVisible())) {
            item->setVisible(true);
            changed = true;
        }
    }

    if (changed) {
        arrangeWidgets();
    }
}

void MImToolbar::hideGroup(const QString &group)
{
    bool changed = false;

    if (!currentToolbar) {
        return;
    }

    foreach (const QSharedPointer<MToolbarItem> item, currentToolbar->items())
    {
        if (item->group() == group && item->isVisible()) {
            item->setVisible(false);
            changed = true;
        }
    }

    if (changed) {
        arrangeWidgets();
    }
}

void MImToolbar::sendKeySequence(const QString &keys)
{
    const QKeySequence keysequence(keys);
    //translate the keys string to QKeyEvent by QKeySequence
    if (!keysequence.isEmpty()) {
        for (uint i = 0; i < keysequence.count(); i++) {
            int key = keysequence[i];
            Qt::KeyboardModifiers modify = keyModifiers(key);
            key -= modify;
            QString text;
            if (modify == Qt::NoModifier || modify == Qt::ShiftModifier)
                text = QString(key);
            //send both KeyPress and KeyRelease
            QKeyEvent press(QEvent::KeyPress, key, modify, text);
            emit sendKeyEventRequest(press);
            QKeyEvent release(QEvent::KeyRelease, key, modify, text);
            emit sendKeyEventRequest(release);
        }
    }
}

Qt::KeyboardModifiers MImToolbar::keyModifiers(int key) const
{
    Qt::KeyboardModifiers modify = Qt::NoModifier;
    if (key & Qt::CTRL)
        modify |= Qt::ControlModifier;
    if (key & Qt::ALT)
        modify |= Qt::AltModifier;
    if (key & Qt::SHIFT)
        modify |= Qt::ShiftModifier;
    if (key & Qt::META)
        modify |= Qt::MetaModifier;
    return modify;
}

void MImToolbar::setShapedMode(bool shaped)
{
    if (shaped) {
        style().setModeShapedToolbar();
    } else {
        style().setModeFullToolbar();
    }
    rightBar.setShapedMode(shaped);
}

void MImToolbar::showToolbarWidget(QSharedPointer<const MToolbarData> toolbar)
{
    if (toolbar == currentToolbar) {
        return;
    }
    unloadCustomWidgets();

    const bool oldToolbarCustom(currentToolbar ? currentToolbar->isCustom() : false);
    currentToolbar = toolbar;
    loadCustomWidgets();

    setShapedMode(!toolbar->isCustom());

    if (isVisible())
        updateVisibility();

    if (oldToolbarCustom != toolbar->isCustom()) {
        emit typeChanged(!toolbar->isCustom());
    }
}

void MImToolbar::hideToolbarWidget()
{
    currentToolbar.clear();
    unloadCustomWidgets();
    arrangeWidgets();
    setShapedMode(true);
}

void MImToolbar::removeItem(MWidget *widget)
{
    leftBar.remove(widget);
    rightBar.remove(widget);

    if (leftBar.count() == 0) {
        leftBar.hide();
    }
    if (rightBar.count() == 0) {
        rightBar.hide();
    }

    if (!layout()) {
        qCritical() << __PRETTY_FUNCTION__ << "Layout does not exist";
    }

    layout()->invalidate();
    layout()->activate();
}

void MImToolbar::paintReactionMap(MReactionMap *reactionMap, QGraphicsView *view)
{
    if (!layout()) {
        qCritical() << __PRETTY_FUNCTION__ << "Layout does not exist";
    }

    layout()->activate();

    // Draw the whole toolbar background as inactive when custom toolbar is used.
    const bool paintWholeToolbar = currentToolbar && currentToolbar->isCustom();

    if (paintWholeToolbar) {
        reactionMap->setTransform(this, view);
        reactionMap->setInactiveDrawingValue();
        reactionMap->fillRectangle(boundingRect());
    }

    // Draw all widgets geometries.
    reactionMap->setReactiveDrawingValue();

    for (int n = 0; n < layout()->count(); ++n) {
        QGraphicsLinearLayout *row = dynamic_cast<QGraphicsLinearLayout*>(layout()->itemAt(n));

        if (!row) {
            continue;
        }

        row->activate();

        for (int j = 0; j < row->count(); ++j) {
            WidgetBar *sidebar = dynamic_cast<WidgetBar *>(row->itemAt(j));
            if (!sidebar || !sidebar->isVisible()) {
                continue;
            }

            // Buttons sometimes require this.
            sidebar->layout()->activate();

            if (!paintWholeToolbar) {
                reactionMap->setTransform(sidebar, view);
                reactionMap->setInactiveDrawingValue();
                reactionMap->fillRectangle(sidebar->boundingRect());
            }

            reactionMap->setReactiveDrawingValue();

            for (int i = 0; i < sidebar->count(); ++i) {
                QGraphicsWidget *widget = sidebar->widgetAt(i);

                if (widget && widget->isVisible()
                    && !qobject_cast<MToolbarLabel*>(widget)) {

                    reactionMap->setTransform(widget, view);
                    reactionMap->fillRectangle(widget->boundingRect());
                }
                // Otherwise leave as inactive.
            }
        }
    }
}

void MImToolbar::finalizeOrientationChange()
{
    //use brute force: destroy everything and construct it again
    unloadCustomWidgets();
    loadCustomWidgets();

    if (isVisible()) {
        blockSignals(true);
        arrangeWidgets();
        blockSignals(false);
    }
}

QRectF MImToolbar::boundingRect() const
{
    return QRectF(-style()->marginLeft(), -style()->marginTop(),
                  size().width() + style()->marginLeft() + style()->marginRight(),
                  size().height() + style()->marginTop() + style()->marginBottom());
}

<commit_msg>Changes: coding style fixes<commit_after>/* * This file is part of meego-keyboard *
 *
 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved.
 * Contact: Nokia Corporation (directui@nokia.com)
 *
 * If you have questions regarding the use of this file, please contact
 * Nokia at directui@nokia.com.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation
 * and appearing in the file LICENSE.LGPL included in the packaging
 * of this file.
 */

#include "mimtoolbar.h"
#include "layoutsmanager.h"
#include "mhardwarekeyboard.h"
#include "mvirtualkeyboardstyle.h"
#include "mtoolbarbutton.h"
#include "mtoolbarlabel.h"

#include <mtoolbardata.h>
#include <mtoolbaritem.h>
#include <mtoolbarrow.h>
#include <mtoolbarlayout.h>

#include <MNamespace>
#include <MButton>
#include <QKeySequence>
#include <QGraphicsLinearLayout>
#include <QDebug>
#include <MSceneManager>
#include <mplainwindow.h>
#include <mreactionmap.h>
#include <MScalableImage>

namespace
{
    const QString ObjectNameToolbar("MImToolbar");
    const QString ObjectNameToolbarLeft("VirtualKeyboardToolbarLeft");
    const QString ObjectNameToolbarRight("VirtualKeyboardToolbarRight");
    const QString NameToolbarCopyPasteButton("VirtualKeyboardCopyPasteButton");
};

MImToolbar::MImToolbar(QGraphicsWidget *parent)
    : MStylableWidget(parent),
      textSelected(false),
      copyPasteItem(new MToolbarItem(NameToolbarCopyPasteButton, MInputMethod::ItemButton)),
      leftBar(true, this),
      rightBar(true, this),
      shiftState(ModifierClearState),
      fnState(ModifierClearState)
{
    leftBar.setObjectName(ObjectNameToolbarLeft);
    rightBar.setObjectName(ObjectNameToolbarRight);
    setObjectName(ObjectNameToolbar);

    setupLayout();

    connect(this, SIGNAL(visibleChanged()), this, SLOT(updateVisibility()));
    connect(MTheme::instance(), SIGNAL(themeChangeCompleted()),
            this, SLOT(updateFromStyle()));
}

MImToolbar::~MImToolbar()
{
}

void MImToolbar::setupLayout()
{
    QGraphicsLinearLayout *mainLayout = new QGraphicsLinearLayout(Qt::Vertical, this);
    mainLayout->setContentsMargins(0, 0, 0, 0);

    QGraphicsLinearLayout *rowLayout = new QGraphicsLinearLayout(Qt::Horizontal, mainLayout);

    setupRowLayout(rowLayout, &leftBar, &rightBar);
    mainLayout->insertItem(0, rowLayout);

    resize(geometry().width(), layout()->preferredHeight());
}

QRegion MImToolbar::region() const
{
    QRegion region;

    if (isVisible()) {
        if (currentToolbar && currentToolbar->isCustom()) {
            region = QRegion(mapRectToScene(rect()).toRect());
        } else if (rightBar.isVisible()) {
            region = QRegion(mapRectToScene(rightBar.geometry()).toRect());
        }
    }
    return region;
}

void MImToolbar::handleButtonClick(MToolbarItem *item)
{
    if (!item || item->type() != MInputMethod::ItemButton)
        return;

    foreach(QSharedPointer<const MToolbarItemAction> action, item->actions()) {
        switch (action->type()) {
        case MInputMethod::ActionSendKeySequence:
            sendKeySequence(action->keys());
            break;
        case MInputMethod::ActionSendString:
            sendStringRequest(action->text());
            break;
        case MInputMethod::ActionSendCommand:
            //TODO:not support yet
            break;
        case MInputMethod::ActionCopy:
            emit copyPasteRequest(InputMethodCopy);
            break;
        case MInputMethod::ActionPaste:
            emit copyPasteRequest(InputMethodPaste);
            break;
        case MInputMethod::ActionShowGroup:
            showGroup(action->group());
            break;
        case MInputMethod::ActionHideGroup:
            hideGroup(action->group());
            break;
        case MInputMethod::ActionClose:
            emit closeKeyboardRequest();
            break;
        case MInputMethod::ActionUndefined:
        case MInputMethod::ActionCopyPaste:
            break;
        }
    }
}

void MImToolbar::setSelectionStatus(bool selection)
{
    if (textSelected != selection) {
        textSelected = selection;
        if (isVisible())
            updateVisibility();
    }
}

void MImToolbar::updateVisibility()
{
    if (currentToolbar) {
        foreach (const QSharedPointer<MToolbarItem> item, currentToolbar->items()) {
            if ((item->showOn() == MInputMethod::VisibleAlways)
                || (textSelected && item->showOn() == MInputMethod::VisibleWhenSelectingText)
                || (!textSelected && item->hideOn() == MInputMethod::VisibleWhenSelectingText)) {
                item->setVisible(true);
            }

            if ((!textSelected && item->showOn() == MInputMethod::VisibleWhenSelectingText)
                || (textSelected && item->hideOn() == MInputMethod::VisibleWhenSelectingText)) {
                item->setVisible(false);
            }
        }
    }
    arrangeWidgets();
}

void MImToolbar::updateFromStyle()
{
    prepareGeometryChange(); // notify scene about changes in bounding rect
}

void MImToolbar::loadCustomWidgets()
{
    if (!currentToolbar) {
        return;
    }

    const M::Orientation orientation = MPlainWindow::instance()->sceneManager()->orientation();
    QSharedPointer<const MToolbarLayout> layout = currentToolbar->layout(orientation);
    QGraphicsLinearLayout *mainLayout = static_cast<QGraphicsLinearLayout*>(this->layout());

    if (!mainLayout) {
        qCritical() << __PRETTY_FUNCTION__ << "Layout does not exist";
    }

    QList<QSharedPointer<const MToolbarRow> > rows = layout->rows();

    //create additional rows if necessary
    for (int n = 0; n < rows.count() - 1; ++n) {
        QSharedPointer<const MToolbarRow> row = rows[n];
        QGraphicsLinearLayout *rowLayout = new QGraphicsLinearLayout(Qt::Horizontal, mainLayout);
        WidgetBar *leftWidget  = new WidgetBar(true, this);
        WidgetBar *rightWidget = new WidgetBar(true, this);

        leftWidget->setObjectName(ObjectNameToolbarLeft);
        rightWidget->setObjectName(ObjectNameToolbarRight);
        setupRowLayout(rowLayout, leftWidget, rightWidget);
        mainLayout->insertItem(n, rowLayout);

        foreach (QSharedPointer<MToolbarItem> item , row->items()) {
            createAndAppendWidget(item, leftWidget, rightWidget);
        }
    }

    //add custom items to bottom row
    QSharedPointer<const MToolbarRow> row = rows.last();
    foreach (QSharedPointer<MToolbarItem> item, row->items()) {
        createAndAppendWidget(item, &leftBar, &rightBar);
    }

    mainLayout->invalidate();
}

void MImToolbar::createAndAppendWidget(QSharedPointer<MToolbarItem> item,
                                       WidgetBar *leftWidget,
                                       WidgetBar *rightWidget)
{
    MWidget *widget = 0;
    WidgetBar *sidebar = 0;

    if (item->alignment() == Qt::AlignLeft) {
        sidebar = leftWidget;
    } else {
        sidebar = rightWidget;
    }
    if (item->type() == MInputMethod::ItemButton) {
        widget = new MToolbarButton(item, sidebar);

        connect(widget, SIGNAL(clicked(MToolbarItem*)),
                this, SLOT(handleButtonClick(MToolbarItem*)));
    } else {
        widget = new MToolbarLabel(item, sidebar);
    }
    customWidgets.append(widget);
    sidebar->append(widget, item->isVisible());
    if (sidebar->count() == 1) {
        sidebar->show();
    }
}

void MImToolbar::setupRowLayout(QGraphicsLinearLayout *rowLayout,
                                WidgetBar *leftWidget,
                                WidgetBar *rightWidget)
{
    rowLayout->setContentsMargins(0, 0, 0, 0);

    // Empty button bars are hidden.
    leftWidget->hide();
    rightWidget->hide();
    // Add the left and right side WidgetBar widgets with a stretch item in between.
    rowLayout->addItem(leftWidget);
    rowLayout->addStretch();
    rowLayout->addItem(rightWidget);

    rowLayout->setAlignment(leftWidget, Qt::AlignBottom);
    rowLayout->setAlignment(rightWidget, Qt::AlignBottom);
}

void MImToolbar::unloadCustomWidgets()
{
    QGraphicsLinearLayout *mainLayout = static_cast<QGraphicsLinearLayout*>(layout());
    QList<QGraphicsLinearLayout*> rows;

    if (!mainLayout) {
        qCritical() << __PRETTY_FUNCTION__ << "Layout does not exist";
    }

    //delete all dynamically created rows
    for (int n = 0; n < mainLayout->count() - 1; ++n) {
        QGraphicsLinearLayout *rowLayout = dynamic_cast<QGraphicsLinearLayout*>(mainLayout->itemAt(n));

        if (rowLayout) {
            WidgetBar *leftBar  = dynamic_cast<WidgetBar*>(rowLayout->itemAt(0));
            WidgetBar *rightBar = dynamic_cast<WidgetBar*>(rowLayout->itemAt(1));

            delete leftBar;
            delete rightBar;
            rows << rowLayout;
        }
    }

    qDeleteAll(rows);
    qDeleteAll(customWidgets);
    customWidgets.clear();
    leftBar.cleanup();
    rightBar.cleanup();
}

void MImToolbar::arrangeWidgets()
{
    if (!layout()) {
        qCritical() << __PRETTY_FUNCTION__ << "Layout does not exist";
    }

    if (isVisible()) {
        for (int n = 0; n < layout()->count(); ++n) {
            QGraphicsLayout *rowLayout = dynamic_cast<QGraphicsLayout*>(layout()->itemAt(n));
            if (rowLayout) {
                rowLayout->invalidate();
                rowLayout->activate();
            }
        }

        layout()->invalidate();
        layout()->activate();
        resize(geometry().width(), layout()->preferredHeight());
    }

    emit regionUpdated();
}

void MImToolbar::showGroup(const QString &group)
{
    bool changed = false;

    if (!currentToolbar) {
        return;
    }

    foreach (const QSharedPointer<MToolbarItem> item, currentToolbar->items()) {
        if (item->group() == group && !(item->isVisible())) {
            item->setVisible(true);
            changed = true;
        }
    }

    if (changed) {
        arrangeWidgets();
    }
}

void MImToolbar::hideGroup(const QString &group)
{
    bool changed = false;

    if (!currentToolbar) {
        return;
    }

    foreach (const QSharedPointer<MToolbarItem> item, currentToolbar->items()) {
        if (item->group() == group && item->isVisible()) {
            item->setVisible(false);
            changed = true;
        }
    }

    if (changed) {
        arrangeWidgets();
    }
}

void MImToolbar::sendKeySequence(const QString &keys)
{
    const QKeySequence keysequence(keys);
    //translate the keys string to QKeyEvent by QKeySequence
    if (!keysequence.isEmpty()) {
        for (uint i = 0; i < keysequence.count(); i++) {
            int key = keysequence[i];
            Qt::KeyboardModifiers modify = keyModifiers(key);
            key -= modify;
            QString text;
            if (modify == Qt::NoModifier || modify == Qt::ShiftModifier)
                text = QString(key);
            //send both KeyPress and KeyRelease
            QKeyEvent press(QEvent::KeyPress, key, modify, text);
            emit sendKeyEventRequest(press);
            QKeyEvent release(QEvent::KeyRelease, key, modify, text);
            emit sendKeyEventRequest(release);
        }
    }
}

Qt::KeyboardModifiers MImToolbar::keyModifiers(int key) const
{
    Qt::KeyboardModifiers modify = Qt::NoModifier;
    if (key & Qt::CTRL)
        modify |= Qt::ControlModifier;
    if (key & Qt::ALT)
        modify |= Qt::AltModifier;
    if (key & Qt::SHIFT)
        modify |= Qt::ShiftModifier;
    if (key & Qt::META)
        modify |= Qt::MetaModifier;
    return modify;
}

void MImToolbar::setShapedMode(bool shaped)
{
    if (shaped) {
        style().setModeShapedToolbar();
    } else {
        style().setModeFullToolbar();
    }
    rightBar.setShapedMode(shaped);
}

void MImToolbar::showToolbarWidget(QSharedPointer<const MToolbarData> toolbar)
{
    if (toolbar == currentToolbar) {
        return;
    }
    unloadCustomWidgets();

    const bool oldToolbarCustom(currentToolbar ? currentToolbar->isCustom() : false);
    currentToolbar = toolbar;
    loadCustomWidgets();

    setShapedMode(!toolbar->isCustom());

    if (isVisible())
        updateVisibility();

    if (oldToolbarCustom != toolbar->isCustom()) {
        emit typeChanged(!toolbar->isCustom());
    }
}

void MImToolbar::hideToolbarWidget()
{
    currentToolbar.clear();
    unloadCustomWidgets();
    arrangeWidgets();
    setShapedMode(true);
}

void MImToolbar::removeItem(MWidget *widget)
{
    leftBar.remove(widget);
    rightBar.remove(widget);

    if (leftBar.count() == 0) {
        leftBar.hide();
    }
    if (rightBar.count() == 0) {
        rightBar.hide();
    }

    if (!layout()) {
        qCritical() << __PRETTY_FUNCTION__ << "Layout does not exist";
    }

    layout()->invalidate();
    layout()->activate();
}

void MImToolbar::paintReactionMap(MReactionMap *reactionMap, QGraphicsView *view)
{
    if (!layout()) {
        qCritical() << __PRETTY_FUNCTION__ << "Layout does not exist";
    }

    layout()->activate();

    // Draw the whole toolbar background as inactive when custom toolbar is used.
    const bool paintWholeToolbar = currentToolbar && currentToolbar->isCustom();

    if (paintWholeToolbar) {
        reactionMap->setTransform(this, view);
        reactionMap->setInactiveDrawingValue();
        reactionMap->fillRectangle(boundingRect());
    }

    // Draw all widgets geometries.
    reactionMap->setReactiveDrawingValue();

    for (int n = 0; n < layout()->count(); ++n) {
        QGraphicsLinearLayout *row = dynamic_cast<QGraphicsLinearLayout*>(layout()->itemAt(n));

        if (!row) {
            continue;
        }

        row->activate();

        for (int j = 0; j < row->count(); ++j) {
            WidgetBar *sidebar = dynamic_cast<WidgetBar *>(row->itemAt(j));
            if (!sidebar || !sidebar->isVisible()) {
                continue;
            }

            // Buttons sometimes require this.
            sidebar->layout()->activate();

            if (!paintWholeToolbar) {
                reactionMap->setTransform(sidebar, view);
                reactionMap->setInactiveDrawingValue();
                reactionMap->fillRectangle(sidebar->boundingRect());
            }

            reactionMap->setReactiveDrawingValue();

            for (int i = 0; i < sidebar->count(); ++i) {
                QGraphicsWidget *widget = sidebar->widgetAt(i);

                if (widget && widget->isVisible()
                    && !qobject_cast<MToolbarLabel*>(widget)) {

                    reactionMap->setTransform(widget, view);
                    reactionMap->fillRectangle(widget->boundingRect());
                }
                // Otherwise leave as inactive.
            }
        }
    }
}

void MImToolbar::finalizeOrientationChange()
{
    //use brute force: destroy everything and construct it again
    unloadCustomWidgets();
    loadCustomWidgets();

    if (isVisible()) {
        blockSignals(true);
        arrangeWidgets();
        blockSignals(false);
    }
}

QRectF MImToolbar::boundingRect() const
{
    return QRectF(-style()->marginLeft(), -style()->marginTop(),
                  size().width() + style()->marginLeft() + style()->marginRight(),
                  size().height() + style()->marginTop() + style()->marginBottom());
}

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013-2015 Daniel Nicoletti <dantti12@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB. If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "engine_p.h"

#include "common.h"
#include "request_p.h"
#include "application.h"
#include "response_p.h"
#include "context_p.h"

#include <QUrl>
#include <QSettings>
#include <QDir>
#include <QtCore/QByteArray>
#include <QDebug>

using namespace Cutelyst;

Engine::Engine(const QVariantMap &opts, QObject *parent) :
    QObject(parent),
    d_ptr(new EnginePrivate)
{
    Q_D(Engine);

    // Debug messages should be disabled by default
    QLoggingCategory::setFilterRules(QLatin1String("cutelyst.*.debug=false"));

    // Load application configuration
    if (qEnvironmentVariableIsSet("CUTELYST_CONFIG")) {
        const QByteArray config = qgetenv("CUTELYST_CONFIG");
        qCDebug(CUTELYST_CORE) << "Reading config file:" << config;
        QSettings settings(QString::fromLatin1(config), QSettings::IniFormat);
        Q_FOREACH (const QString &group, settings.childGroups()) {
            settings.beginGroup(group);
            Q_FOREACH (const QString &key, settings.childKeys()) {
                d->config[group].insert(key, settings.value(key));
            }
            settings.endGroup();
        }
        qCDebug(CUTELYST_CORE) << "Configuration:" << d->config;
    }

    d->opts = opts;
}

Engine::~Engine()
{
    delete d_ptr;
}

void Engine::finalizeCookies(Context *c)
{
    Response *res = c->response();
    Headers &headers = res->headers();
    Q_FOREACH (const QNetworkCookie &cookie, res->cookies()) {
        headers.pushHeader(QStringLiteral("Set-Cookie"), QString::fromLatin1(cookie.toRawForm()));
    }
}

bool Engine::finalizeHeaders(Context *c)
{
    Response *response = c->response();

    // Check if we already finalized headers
    if (response->d_ptr->finalizedHeaders) {
        return true;
    }

    // Fix missing content length
    if (!response->contentLength() && response->hasBody()) {
        QIODevice *body = response->bodyDevice();
        if (body) {
            response->setContentLength(body->size());
        } else {
            response->setContentLength(response->body().size());
        }
    }

    const QString protocol = c->request()->protocol();
    if (protocol == QLatin1String("HTTP/1.1")) {
        if (!response->contentLength()) {
            quint16 status = response->status();
            // if status is not 1xx or 204 NoContent or 304 NotModified
            if (!(status >= 100 && status <= 199) && status != 204 && status != 304) {
                qCDebug(CUTELYST_ENGINE, "Using chunked transfer-encoding to send unknown length body");
                response->setHeader(QStringLiteral("Transfer-Encoding"), QStringLiteral("chunked"));
                response->d_ptr->chunked = true;
            }
        } else if (response->header(QStringLiteral("Transfer-Encoding")) == QLatin1String("chunked")) {
            qCDebug(CUTELYST_ENGINE, "Chunked transfer-encoding set for response");
            response->d_ptr->chunked = true;
        }
    }

    // Handle redirects
    const QUrl &location = response->location();
    if (!location.isEmpty()) {
        qCDebug(CUTELYST_ENGINE, "Redirecting to \"%s\"", location.toEncoded().constData());
        response->headers().setHeader(QStringLiteral("Location"), QString::fromLatin1(location.toEncoded()));
    }

    finalizeCookies(c);

    // Done
    response->d_ptr->finalizedHeaders = true;
    return true;
}

void Engine::finalizeBody(Context *c)
{
    Response *response = c->response();
    void *engineData = c->engineData();

    if (!response->d_ptr->chunked) {
        QIODevice *body = response->bodyDevice();

        if (body) {
            body->seek(0);
            char block[64 * 1024];
            while (!body->atEnd()) {
                qint64 in = body->read(block, sizeof(block));
                if (in <= 0)
                    break;

                if (write(c, block, in, engineData) != in) {
                    qCWarning(CUTELYST_ENGINE) << "Failed to write body";
                    break;
                }
            }
        } else {
            const QByteArray bodyByteArray = response->body();
            if (!bodyByteArray.isNull()) {
                write(c, bodyByteArray.constData(), bodyByteArray.size(), engineData);
            }
        }
    } else if (!response->d_ptr->chunked_done) {
        // Write the final '0' chunk
        doWrite(c, "0\r\n\r\n", 5, engineData);
    }
}

void Engine::finalizeError(Context *c)
{
    Response *res = c->response();

    res->setContentType(QStringLiteral("text/html; charset=utf-8"));

    QByteArray body;

    // Trick IE. Old versions of IE would display their own error page instead
    // of ours if we'd give it less than 512 bytes.
    body.reserve(512);

    body.append(c->errors().join(QLatin1Char('\n')).toUtf8());

    res->body() = body;

    // Return 500
    res->setStatus(Response::InternalServerError);
}

Application *Engine::app() const
{
    Q_D(const Engine);
    Q_ASSERT(d->app);
    return d->app;
}

bool Engine::initApplication(Application *app, bool postFork)
{
    Q_D(Engine);
    d->app = app;

    // To make easier for engines to clean up
    // the app must be a child of it
    app->setParent(this);

    if (!app->setup(this)) {
        qCCritical(CUTELYST_ENGINE) << "Failed to setup application";
        return false;
    }

    if (!init()) {
        qCCritical(CUTELYST_ENGINE) << "Failed to setup engine";
        return false;
    }

    if (postFork) {
        return postForkApplication();
    }

    return true;
}

bool Engine::postForkApplication()
{
    Q_D(Engine);

    if (!d->app) {
        qCCritical(CUTELYST_ENGINE) << "Failed to postForkApplication on a null application";
        return false;
    }
    return d->app->enginePostFork();
}

quint64 Engine::time()
{
    return QDateTime::currentMSecsSinceEpoch();
}

qint64 Engine::write(Context *c, const char *data, qint64 len, void *engineData)
{
    Response *response = c->response();
    if (!response->d_ptr->chunked) {
        return doWrite(c, data, len, engineData);
    } else if (!response->d_ptr->chunked_done) {
        const QByteArray chunkSize = QByteArray::number(len, 16).toUpper();
        QByteArray chunk;
        chunk.reserve(len + chunkSize.size() + 4);
        chunk.append(chunkSize).append("\r\n", 2)
                .append(data, len).append("\r\n", 2);

        qint64 retWrite = doWrite(c, chunk.data(), chunk.size(), engineData);

        // Flag if we wrote an empty chunk
        if (!len) {
            response->d_ptr->chunked_done = true;
        }

        return retWrite == chunk.size() ? len : -1;
    }
    return -1;
}

QByteArray Engine::statusCode(quint16 status)
{
    switch (status) {
    case Response::OK:
        return QByteArrayLiteral("200 OK");
    case Response::Found:
        return QByteArrayLiteral("302 Found");
    case Response::NotFound:
        return QByteArrayLiteral("404 Not Found");
    case Response::InternalServerError:
        return QByteArrayLiteral("500 Internal Server Error");
    case Response::MovedPermanently:
        return QByteArrayLiteral("301 Moved Permanently");
    case Response::NotModified:
        return QByteArrayLiteral("304 Not Modified");
    case Response::SeeOther:
        return QByteArrayLiteral("303 See Other");
    case Response::Forbidden:
        return QByteArrayLiteral("403 Forbidden");
    case Response::TemporaryRedirect:
        return QByteArrayLiteral("307 Temporary Redirect");
    case Response::Unauthorized:
        return QByteArrayLiteral("401 Unauthorized");
    case Response::BadRequest:
        return QByteArrayLiteral("400 Bad Request");
    case Response::MethodNotAllowed:
        return QByteArrayLiteral("405 Method Not Allowed");
    case Response::RequestTimeout:
        return QByteArrayLiteral("408 Request Timeout");
    case Response::Continue:
        return QByteArrayLiteral("100 Continue");
    case Response::SwitchingProtocols:
        return QByteArrayLiteral("101 Switching Protocols");
    case Response::Created:
        return QByteArrayLiteral("201 Created");
    case Response::Accepted:
        return QByteArrayLiteral("202 Accepted");
    case Response::NonAuthoritativeInformation:
        return QByteArrayLiteral("203 Non-Authoritative Information");
    case Response::NoContent:
        return QByteArrayLiteral("204 No Content");
    case Response::ResetContent:
        return QByteArrayLiteral("205 Reset Content");
    case Response::PartialContent:
        return QByteArrayLiteral("206 Partial Content");
    case Response::MultipleChoices:
        return QByteArrayLiteral("300 Multiple Choices");
    case Response::UseProxy:
        return QByteArrayLiteral("305 Use Proxy");
    case Response::PaymentRequired:
        return QByteArrayLiteral("402 Payment Required");
    case Response::NotAcceptable:
        return QByteArrayLiteral("406 Not Acceptable");
    case Response::ProxyAuthenticationRequired:
        return QByteArrayLiteral("407 Proxy Authentication Required");
    case Response::Conflict:
        return QByteArrayLiteral("409 Conflict");
    case Response::Gone:
        return QByteArrayLiteral("410 Gone");
    case Response::LengthRequired:
        return QByteArrayLiteral("411 Length Required");
    case Response::PreconditionFailed:
        return QByteArrayLiteral("412 Precondition Failed");
    case Response::RequestEntityTooLarge:
        return QByteArrayLiteral("413 Request Entity Too Large");
    case Response::RequestURITooLong:
        return QByteArrayLiteral("414 Request-URI Too Long");
    case Response::UnsupportedMediaType:
        return QByteArrayLiteral("415 Unsupported Media Type");
    case Response::RequestedRangeNotSatisfiable:
        return QByteArrayLiteral("416 Requested Range Not Satisfiable");
    case Response::ExpectationFailed:
        return QByteArrayLiteral("417 Expectation Failed");
    case Response::NotImplemented:
        return QByteArrayLiteral("501 Not Implemented");
    case Response::BadGateway:
        return QByteArrayLiteral("502 Bad Gateway");
    case Response::ServiceUnavailable:
        return QByteArrayLiteral("503 Service Unavailable");
    case Response::GatewayTimeout:
        return QByteArrayLiteral("504 Gateway Timeout");
    case Response::HTTPVersionNotSupported:
        return QByteArrayLiteral("505 HTTP Version Not Supported");
    case Response::BandwidthLimitExceeded:
        return QByteArrayLiteral("509 Bandwidth Limit Exceeded");
    default:
        return QByteArray::number(status);
    }
}

QVariantMap Engine::opts() const
{
    Q_D(const Engine);
    return d->opts;
}

QVariantMap Engine::config(const QString &entity) const
{
    Q_D(const Engine);
    return d->config.value(entity);
}

void Engine::finalize(Context *c)
{
    if (c->error()) {
        finalizeError(c);
    }

    if (!c->response()->d_ptr->finalizedHeaders) {
        finalizeHeaders(c);
    }

    finalizeBody(c);
}

static const QByteArray cutelyst_header_order (
        // General headers
        "Cache-Control\n"
        "Connection\n"
        "Date\n"
        "Pragma\n"
        "Trailer\n"
        "Transfer-Encoding\n"
        "Upgrade\n"
        "Via\n"
        "Warning\n"
        // Request headers
        "Accept\n"
        "Accept-Charset\n"
        "Accept-Encoding\n"
        "Accept-Language\n"
        "Authorization\n"
        "Expect\n"
        "From\n"
        "Host\n"
        "If-Match\n"
        "If-Modified-Since\n"
        "If-None-Match\n"
        "If-Range\n"
        "If-Unmodified-Since\n"
        "Max-Forwards\n"
        "Proxy-Authorization\n"
        "Range\n"
        "Referer\n"
        "TE\n"
        "User-Agent\n"
        // Response headers
        "Accept-Ranges\n"
        "Age\n"
        "ETag\n"
        "Location\n"
        "Proxy-Authenticate\n"
        "Retry-After\n"
        "Server\n"
        "Vary\n"
        "WWW-Authenticate\n"
        // Entity headers
        "Allow\n"
        "Content-Encoding\n"
        "Content-Language\n"
        "Content-Length\n"
        "Content-Location\n"
        "Content-MD5\n"
        "Content-Range\n"
        "Content-Type\n"
        "Expires\n"
        "Last-Modified"
        );

bool httpGoodPracticeWeightSort(const HeaderValuePair &pair1, const HeaderValuePair &pair2)
{
    int index1 = pair1.weight;
    int index2 = pair2.weight;

    if (index1 != -1 && index2 != -1) {
        // Both items are in the headerOrder list
        return index1 < index2;
    } else if (index1 == -1 && index2 == -1) {
        // Noone of them are int the headerOrder list
        return false;
    }

    // if the pair1 is in the header list it should go first
    return index1 != -1;
}

QList<HeaderValuePair> Engine::headersForResponse(const Headers &headers)
{
    QList<HeaderValuePair> ret;

    auto it = headers.constBegin();
    while (it != headers.constEnd()) {
        HeaderValuePair pair;
        pair.key = camelCaseHeader(it.key());
        pair.value = it.value();
        pair.weight = cutelyst_header_order.indexOf(pair.key.toLatin1());

        ret.append(pair);
        ++it;
    }

    // Sort base on the "good practices" of HTTP RCF
    qSort(ret.begin(), ret.end(), &httpGoodPracticeWeightSort);

    return ret;
}

void Engine::processRequest(const QString &method,
                            const QString &path,
                            const QByteArray &query,
                            const QString &protocol,
                            bool isSecure,
                            const QString &serverAddress,
                            const QString &remoteAddress,
                            quint16 remotePort,
                            const QString &remoteUser,
                            const Headers &headers,
                            quint64 startOfRequest,
                            QIODevice *body,
                            void *requestPtr)
{
    Q_D(Engine);

    auto prv = new RequestPrivate(this,
                                  method,
                                  path,
                                  query,
                                  protocol,
                                  isSecure,
                                  serverAddress,
                                  remoteAddress,
                                  remotePort,
                                  remoteUser,
                                  headers,
                                  startOfRequest,
                                  body,
                                  requestPtr);

    auto request = new Request(prv);
    d->app->handleRequest(request);
    delete request;
}

#include "moc_engine.cpp"
<commit_msg>Automatically set chunked transfer only for QIODevice bodies of unknown size<commit_after>/*
 * Copyright (C) 2013-2015 Daniel Nicoletti <dantti12@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB. If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "engine_p.h"

#include "common.h"
#include "request_p.h"
#include "application.h"
#include "response_p.h"
#include "context_p.h"

#include <QUrl>
#include <QSettings>
#include <QDir>
#include <QtCore/QByteArray>
#include <QDebug>

using namespace Cutelyst;

Engine::Engine(const QVariantMap &opts, QObject *parent) :
    QObject(parent),
    d_ptr(new EnginePrivate)
{
    Q_D(Engine);

    // Debug messages should be disabled by default
    QLoggingCategory::setFilterRules(QLatin1String("cutelyst.*.debug=false"));

    // Load application configuration
    if (qEnvironmentVariableIsSet("CUTELYST_CONFIG")) {
        const QByteArray config = qgetenv("CUTELYST_CONFIG");
        qCDebug(CUTELYST_CORE) << "Reading config file:" << config;
        QSettings settings(QString::fromLatin1(config), QSettings::IniFormat);
        Q_FOREACH (const QString &group, settings.childGroups()) {
            settings.beginGroup(group);
            Q_FOREACH (const QString &key, settings.childKeys()) {
                d->config[group].insert(key, settings.value(key));
            }
            settings.endGroup();
        }
        qCDebug(CUTELYST_CORE) << "Configuration:" << d->config;
    }

    d->opts = opts;
}

Engine::~Engine()
{
    delete d_ptr;
}

void Engine::finalizeCookies(Context *c)
{
    Response *res = c->response();
    Headers &headers = res->headers();
    Q_FOREACH (const QNetworkCookie &cookie, res->cookies()) {
        headers.pushHeader(QStringLiteral("Set-Cookie"), QString::fromLatin1(cookie.toRawForm()));
    }
}

bool Engine::finalizeHeaders(Context *c)
{
    Response *response = c->response();

    // Check if we already finalized headers
    if (response->d_ptr->finalizedHeaders) {
        return true;
    }

    // Fix missing content length
    if (!response->contentLength() && response->hasBody()) {
        QIODevice *body = response->bodyDevice();
        if (!body) {
            response->setContentLength(response->body().size());
        } else {
            qint64 size = body->size();
            if (size >= 0) {
                response->setContentLength(body->size());
            } else if (c->request()->protocol() == QLatin1String("HTTP/1.1")) {
                quint16 status = response->status();
                // if status is not 1xx or 204 NoContent or 304 NotModified
                if (!(status >= 100 && status <= 199) && status != 204 && status != 304) {
                    qCDebug(CUTELYST_ENGINE, "Using chunked transfer-encoding to send unknown length body");
                    response->setHeader(QStringLiteral("Transfer-Encoding"), QStringLiteral("chunked"));
                    response->d_ptr->chunked = true;
                }
            }
        }
    } else if (response->header(QStringLiteral("Transfer-Encoding")) == QLatin1String("chunked")) {
        qCDebug(CUTELYST_ENGINE, "Chunked transfer-encoding set for response");
        response->d_ptr->chunked = true;
    }

    // Handle redirects
    const QUrl &location = response->location();
    if (!location.isEmpty()) {
        qCDebug(CUTELYST_ENGINE, "Redirecting to \"%s\"", location.toEncoded().constData());
        response->headers().setHeader(QStringLiteral("Location"), QString::fromLatin1(location.toEncoded()));
    }

    finalizeCookies(c);

    // Done
    response->d_ptr->finalizedHeaders = true;
    return true;
}

void Engine::finalizeBody(Context *c)
{
    Response *response = c->response();
    void *engineData = c->engineData();

    if (!response->d_ptr->chunked) {
        QIODevice *body = response->bodyDevice();

        if (body) {
            body->seek(0);
            char block[64 * 1024];
            while (!body->atEnd()) {
                qint64 in = body->read(block, sizeof(block));
                if (in <= 0)
                    break;

                if (write(c, block, in, engineData) != in) {
                    qCWarning(CUTELYST_ENGINE) << "Failed to write body";
                    break;
                }
            }
        } else {
            const QByteArray bodyByteArray = response->body();
            if (!bodyByteArray.isNull()) {
                write(c, bodyByteArray.constData(), bodyByteArray.size(), engineData);
            }
        }
    } else if (!response->d_ptr->chunked_done) {
        // Write the final '0' chunk
        doWrite(c, "0\r\n\r\n", 5, engineData);
    }
}

void Engine::finalizeError(Context *c)
{
    Response *res = c->response();

    res->setContentType(QStringLiteral("text/html; charset=utf-8"));

    QByteArray body;

    // Trick IE. Old versions of IE would display their own error page instead
    // of ours if we'd give it less than 512 bytes.
    body.reserve(512);

    body.append(c->errors().join(QLatin1Char('\n')).toUtf8());

    res->body() = body;

    // Return 500
    res->setStatus(Response::InternalServerError);
}

Application *Engine::app() const
{
    Q_D(const Engine);
    Q_ASSERT(d->app);
    return d->app;
}

bool Engine::initApplication(Application *app, bool postFork)
{
    Q_D(Engine);
    d->app = app;

    // To make easier for engines to clean up
    // the app must be a child of it
    app->setParent(this);

    if (!app->setup(this)) {
        qCCritical(CUTELYST_ENGINE) << "Failed to setup application";
        return false;
    }

    if (!init()) {
        qCCritical(CUTELYST_ENGINE) << "Failed to setup engine";
        return false;
    }

    if (postFork) {
        return postForkApplication();
    }

    return true;
}

bool Engine::postForkApplication()
{
    Q_D(Engine);

    if (!d->app) {
        qCCritical(CUTELYST_ENGINE) << "Failed to postForkApplication on a null application";
        return false;
    }
    return d->app->enginePostFork();
}

quint64 Engine::time()
{
    return QDateTime::currentMSecsSinceEpoch();
}

qint64 Engine::write(Context *c, const char *data, qint64 len, void *engineData)
{
    Response *response = c->response();
    if (!response->d_ptr->chunked) {
        return doWrite(c, data, len, engineData);
    } else if (!response->d_ptr->chunked_done) {
        const QByteArray chunkSize = QByteArray::number(len, 16).toUpper();
        QByteArray chunk;
        chunk.reserve(len + chunkSize.size() + 4);
        chunk.append(chunkSize).append("\r\n", 2)
                .append(data, len).append("\r\n", 2);

        qint64 retWrite = doWrite(c, chunk.data(), chunk.size(), engineData);

        // Flag if we wrote an empty chunk
        if (!len) {
            response->d_ptr->chunked_done = true;
        }

        return retWrite == chunk.size() ? len : -1;
    }
    return -1;
}

QByteArray Engine::statusCode(quint16 status)
{
    switch (status) {
    case Response::OK:
        return QByteArrayLiteral("200 OK");
    case Response::Found:
        return QByteArrayLiteral("302 Found");
    case Response::NotFound:
        return QByteArrayLiteral("404 Not Found");
    case Response::InternalServerError:
        return QByteArrayLiteral("500 Internal Server Error");
    case Response::MovedPermanently:
        return QByteArrayLiteral("301 Moved Permanently");
    case Response::NotModified:
        return QByteArrayLiteral("304 Not Modified");
    case Response::SeeOther:
        return QByteArrayLiteral("303 See Other");
    case Response::Forbidden:
        return QByteArrayLiteral("403 Forbidden");
    case Response::TemporaryRedirect:
        return QByteArrayLiteral("307 Temporary Redirect");
    case Response::Unauthorized:
        return QByteArrayLiteral("401 Unauthorized");
    case Response::BadRequest:
        return QByteArrayLiteral("400 Bad Request");
    case Response::MethodNotAllowed:
        return QByteArrayLiteral("405 Method Not Allowed");
    case Response::RequestTimeout:
        return QByteArrayLiteral("408 Request Timeout");
    case Response::Continue:
        return QByteArrayLiteral("100 Continue");
    case Response::SwitchingProtocols:
        return QByteArrayLiteral("101 Switching Protocols");
    case Response::Created:
        return QByteArrayLiteral("201 Created");
    case Response::Accepted:
        return QByteArrayLiteral("202 Accepted");
    case Response::NonAuthoritativeInformation:
        return QByteArrayLiteral("203 Non-Authoritative Information");
    case Response::NoContent:
        return QByteArrayLiteral("204 No Content");
    case Response::ResetContent:
        return QByteArrayLiteral("205 Reset Content");
    case Response::PartialContent:
        return QByteArrayLiteral("206 Partial Content");
    case Response::MultipleChoices:
        return QByteArrayLiteral("300 Multiple Choices");
    case Response::UseProxy:
        return QByteArrayLiteral("305 Use Proxy");
    case Response::PaymentRequired:
        return QByteArrayLiteral("402 Payment Required");
    case Response::NotAcceptable:
        return QByteArrayLiteral("406 Not Acceptable");
    case Response::ProxyAuthenticationRequired:
        return QByteArrayLiteral("407 Proxy Authentication Required");
    case Response::Conflict:
        return QByteArrayLiteral("409 Conflict");
    case Response::Gone:
        return QByteArrayLiteral("410 Gone");
    case Response::LengthRequired:
        return QByteArrayLiteral("411 Length Required");
    case Response::PreconditionFailed:
        return QByteArrayLiteral("412 Precondition Failed");
    case Response::RequestEntityTooLarge:
        return QByteArrayLiteral("413 Request Entity Too Large");
    case Response::RequestURITooLong:
        return QByteArrayLiteral("414 Request-URI Too Long");
    case Response::UnsupportedMediaType:
        return QByteArrayLiteral("415 Unsupported Media Type");
    case Response::RequestedRangeNotSatisfiable:
        return QByteArrayLiteral("416 Requested Range Not Satisfiable");
    case Response::ExpectationFailed:
        return QByteArrayLiteral("417 Expectation Failed");
    case Response::NotImplemented:
        return QByteArrayLiteral("501 Not Implemented");
    case Response::BadGateway:
        return QByteArrayLiteral("502 Bad Gateway");
    case Response::ServiceUnavailable:
        return QByteArrayLiteral("503 Service Unavailable");
    case Response::GatewayTimeout:
        return QByteArrayLiteral("504 Gateway Timeout");
    case Response::HTTPVersionNotSupported:
        return QByteArrayLiteral("505 HTTP Version Not Supported");
    case Response::BandwidthLimitExceeded:
        return QByteArrayLiteral("509 Bandwidth Limit Exceeded");
    default:
        return QByteArray::number(status);
    }
}

QVariantMap Engine::opts() const
{
    Q_D(const Engine);
    return d->opts;
}

QVariantMap Engine::config(const QString &entity) const
{
    Q_D(const Engine);
    return d->config.value(entity);
}

void Engine::finalize(Context *c)
{
    if (c->error()) {
        finalizeError(c);
    }

    if (!c->response()->d_ptr->finalizedHeaders) {
        finalizeHeaders(c);
    }

    finalizeBody(c);
}

static const QByteArray cutelyst_header_order (
        // General headers
        "Cache-Control\n"
        "Connection\n"
        "Date\n"
        "Pragma\n"
        "Trailer\n"
        "Transfer-Encoding\n"
        "Upgrade\n"
        "Via\n"
        "Warning\n"
        // Request headers
        "Accept\n"
        "Accept-Charset\n"
        "Accept-Encoding\n"
        "Accept-Language\n"
        "Authorization\n"
        "Expect\n"
        "From\n"
        "Host\n"
        "If-Match\n"
        "If-Modified-Since\n"
        "If-None-Match\n"
        "If-Range\n"
        "If-Unmodified-Since\n"
        "Max-Forwards\n"
        "Proxy-Authorization\n"
        "Range\n"
        "Referer\n"
        "TE\n"
        "User-Agent\n"
        // Response headers
        "Accept-Ranges\n"
        "Age\n"
        "ETag\n"
        "Location\n"
        "Proxy-Authenticate\n"
        "Retry-After\n"
        "Server\n"
        "Vary\n"
        "WWW-Authenticate\n"
        // Entity headers
        "Allow\n"
        "Content-Encoding\n"
        "Content-Language\n"
        "Content-Length\n"
        "Content-Location\n"
        "Content-MD5\n"
        "Content-Range\n"
        "Content-Type\n"
        "Expires\n"
        "Last-Modified"
        );

bool httpGoodPracticeWeightSort(const HeaderValuePair &pair1, const HeaderValuePair &pair2)
{
    int index1 = pair1.weight;
    int index2 = pair2.weight;

    if (index1 != -1 && index2 != -1) {
        // Both items are in the headerOrder list
        return index1 < index2;
    } else if (index1 == -1 && index2 == -1) {
        // Noone of them are int the headerOrder list
        return false;
    }

    // if the pair1 is in the header list it should go first
    return index1 != -1;
}

QList<HeaderValuePair> Engine::headersForResponse(const Headers &headers)
{
    QList<HeaderValuePair> ret;

    auto it = headers.constBegin();
    while (it != headers.constEnd()) {
        HeaderValuePair pair;
        pair.key = camelCaseHeader(it.key());
        pair.value = it.value();
        pair.weight = cutelyst_header_order.indexOf(pair.key.toLatin1());

        ret.append(pair);
        ++it;
    }

    // Sort base on the "good practices" of HTTP RCF
    qSort(ret.begin(), ret.end(), &httpGoodPracticeWeightSort);

    return ret;
}

void Engine::processRequest(const QString &method,
                            const QString &path,
                            const QByteArray &query,
                            const QString &protocol,
                            bool isSecure,
                            const QString &serverAddress,
                            const QString &remoteAddress,
                            quint16 remotePort,
                            const QString &remoteUser,
                            const Headers &headers,
                            quint64 startOfRequest,
                            QIODevice *body,
                            void *requestPtr)
{
    Q_D(Engine);

    auto prv = new RequestPrivate(this,
                                  method,
                                  path,
                                  query,
                                  protocol,
                                  isSecure,
                                  serverAddress,
                                  remoteAddress,
                                  remotePort,
                                  remoteUser,
                                  headers,
                                  startOfRequest,
                                  body,
                                  requestPtr);

    auto request = new Request(prv);
    d->app->handleRequest(request);
    delete request;
}

#include "moc_engine.cpp"
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2005, 2006, 2009 Apple Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "config.h"

#ifdef SKIP_STATIC_CONSTRUCTORS_ON_GCC
#define WEBCORE_QUALIFIEDNAME_HIDE_GLOBALS 1
#else
#define QNAME_DEFAULT_CONSTRUCTOR
#endif

#include "HTMLNames.h"
#include "SVGNames.h"
#include "XLinkNames.h"
#include "XMLNSNames.h"
#include "XMLNames.h"
#include "core/dom/QualifiedName.h"
#include "wtf/Assertions.h"
#include "wtf/HashSet.h"
#include "wtf/StaticConstructors.h"

namespace WebCore {

static const int staticQualifiedNamesCount = HTMLNames::HTMLTagsCount + HTMLNames::HTMLAttrsCount
    + SVGNames::SVGTagsCount + SVGNames::SVGAttrsCount
    + XLinkNames::XLinkAttrsCount
    + XMLNSNames::XMLNSAttrsCount
    + XMLNames::XMLAttrsCount;

struct QualifiedNameHashTraits : public HashTraits<QualifiedName::QualifiedNameImpl*> {
    static const int minimumTableSize = WTF::HashTableCapacityForSize<staticQualifiedNamesCount>::value;
};

typedef HashSet<QualifiedName::QualifiedNameImpl*, QualifiedNameHash, QualifiedNameHashTraits> QNameSet;

struct QNameComponentsTranslator {
    static unsigned hash(const QualifiedNameComponents& components)
    {
        return hashComponents(components);
    }
    static bool equal(QualifiedName::QualifiedNameImpl* name, const QualifiedNameComponents& c)
    {
        return c.m_prefix == name->m_prefix.impl() && c.m_localName == name->m_localName.impl() && c.m_namespace == name->m_namespace.impl();
    }
    static void translate(QualifiedName::QualifiedNameImpl*& location, const QualifiedNameComponents& components, unsigned)
    {
        location = QualifiedName::QualifiedNameImpl::create(components.m_prefix, components.m_localName, components.m_namespace).leakRef();
    }
};

static QNameSet* gNameCache;

QualifiedName::QualifiedName(const AtomicString& p, const AtomicString& l, const AtomicString& n)
{
    if (!gNameCache)
        gNameCache = new QNameSet;
    QualifiedNameComponents components = { p.impl(), l.impl(), n.isEmpty() ? nullAtom.impl() : n.impl() };
    QNameSet::AddResult addResult = gNameCache->add<QNameComponentsTranslator>(components);
    m_impl = *addResult.iterator;
    if (!addResult.isNewEntry)
        m_impl->ref();
}

QualifiedName::~QualifiedName()
{
    deref();
}

void QualifiedName::deref()
{
#ifdef QNAME_DEFAULT_CONSTRUCTOR
    if (!m_impl)
        return;
#endif
    ASSERT(!isHashTableDeletedValue());
    m_impl->deref();
}

QualifiedName::QualifiedNameImpl::~QualifiedNameImpl()
{
    gNameCache->remove(this);
}

String QualifiedName::toString() const
{
    String local = localName();
    if (hasPrefix())
        return prefix().string() + ":" + local;
    return local;
}

// Global init routines
DEFINE_GLOBAL(QualifiedName, anyName, nullAtom, starAtom, starAtom)

void QualifiedName::init()
{
    ASSERT(starAtom.impl());
    new ((void*)&anyName) QualifiedName(nullAtom, starAtom, starAtom);
}

const QualifiedName& nullQName()
{
    DEFINE_STATIC_LOCAL(QualifiedName, nullName, (nullAtom, nullAtom, nullAtom));
    return nullName;
}

const AtomicString& QualifiedName::localNameUpper() const
{
    if (!m_impl->m_localNameUpper)
        m_impl->m_localNameUpper = m_impl->m_localName.upper();
    return m_impl->m_localNameUpper;
}

unsigned QualifiedName::QualifiedNameImpl::computeHash() const
{
    QualifiedNameComponents components = { m_prefix.impl(), m_localName.impl(), m_namespace.impl() };
    return hashComponents(components);
}

void createQualifiedName(void* targetAddress, StringImpl* name, const AtomicString& nameNamespace)
{
    new (targetAddress) QualifiedName(nullAtom, AtomicString(name), nameNamespace);
}

void createQualifiedName(void* targetAddress, StringImpl* name)
{
    new (targetAddress) QualifiedName(nullAtom, AtomicString(name), nullAtom);
}

}
<commit_msg>ASSERT that QualifiedNames are only ever created/destroyed on the main thread<commit_after>/*
 * Copyright (C) 2005, 2006, 2009 Apple Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "config.h"

#ifdef SKIP_STATIC_CONSTRUCTORS_ON_GCC
#define WEBCORE_QUALIFIEDNAME_HIDE_GLOBALS 1
#else
#define QNAME_DEFAULT_CONSTRUCTOR
#endif

#include "HTMLNames.h"
#include "SVGNames.h"
#include "XLinkNames.h"
#include "XMLNSNames.h"
#include "XMLNames.h"
#include "core/dom/QualifiedName.h"
#include "wtf/Assertions.h"
#include "wtf/HashSet.h"
#include "wtf/MainThread.h"
#include "wtf/StaticConstructors.h"

namespace WebCore {

static const int staticQualifiedNamesCount = HTMLNames::HTMLTagsCount + HTMLNames::HTMLAttrsCount
    + SVGNames::SVGTagsCount + SVGNames::SVGAttrsCount
    + XLinkNames::XLinkAttrsCount
    + XMLNSNames::XMLNSAttrsCount
    + XMLNames::XMLAttrsCount;

struct QualifiedNameHashTraits : public HashTraits<QualifiedName::QualifiedNameImpl*> {
    static const int minimumTableSize = WTF::HashTableCapacityForSize<staticQualifiedNamesCount>::value;
};

typedef HashSet<QualifiedName::QualifiedNameImpl*, QualifiedNameHash, QualifiedNameHashTraits> QualifiedNameCache;

static QualifiedNameCache& qualifiedNameCache()
{
    // This code is lockless and thus assumes it all runs on one thread!
    ASSERT(isMainThread());
    static QualifiedNameCache* gNameCache = new QualifiedNameCache;
    return *gNameCache;
}

struct QNameComponentsTranslator {
    static unsigned hash(const QualifiedNameComponents& components)
    {
        return hashComponents(components);
    }
    static bool equal(QualifiedName::QualifiedNameImpl* name, const QualifiedNameComponents& c)
    {
        return c.m_prefix == name->m_prefix.impl() && c.m_localName == name->m_localName.impl() && c.m_namespace == name->m_namespace.impl();
    }
    static void translate(QualifiedName::QualifiedNameImpl*& location, const QualifiedNameComponents& components, unsigned)
    {
        location = QualifiedName::QualifiedNameImpl::create(components.m_prefix, components.m_localName, components.m_namespace).leakRef();
    }
};

QualifiedName::QualifiedName(const AtomicString& p, const AtomicString& l, const AtomicString& n)
{
    QualifiedNameComponents components = { p.impl(), l.impl(), n.isEmpty() ? nullAtom.impl() : n.impl() };
    QualifiedNameCache::AddResult addResult = qualifiedNameCache().add<QNameComponentsTranslator>(components);
    m_impl = *addResult.iterator;
    if (!addResult.isNewEntry)
        m_impl->ref();
}

QualifiedName::~QualifiedName()
{
    deref();
}

void QualifiedName::deref()
{
#ifdef QNAME_DEFAULT_CONSTRUCTOR
    if (!m_impl)
        return;
#endif
    ASSERT(!isHashTableDeletedValue());
    m_impl->deref();
}

QualifiedName::QualifiedNameImpl::~QualifiedNameImpl()
{
    qualifiedNameCache().remove(this);
}

String QualifiedName::toString() const
{
    String local = localName();
    if (hasPrefix())
        return prefix().string() + ":" + local;
    return local;
}

// Global init routines
DEFINE_GLOBAL(QualifiedName, anyName, nullAtom, starAtom, starAtom)

void QualifiedName::init()
{
    ASSERT(starAtom.impl());
    new ((void*)&anyName) QualifiedName(nullAtom, starAtom, starAtom);
}

const QualifiedName& nullQName()
{
    DEFINE_STATIC_LOCAL(QualifiedName, nullName, (nullAtom, nullAtom, nullAtom));
    return nullName;
}

const AtomicString& QualifiedName::localNameUpper() const
{
    if (!m_impl->m_localNameUpper)
        m_impl->m_localNameUpper = m_impl->m_localName.upper();
    return m_impl->m_localNameUpper;
}

unsigned QualifiedName::QualifiedNameImpl::computeHash() const
{
    QualifiedNameComponents components = { m_prefix.impl(), m_localName.impl(), m_namespace.impl() };
    return hashComponents(components);
}

void createQualifiedName(void* targetAddress, StringImpl* name, const AtomicString& nameNamespace)
{
    new (targetAddress) QualifiedName(nullAtom, AtomicString(name), nameNamespace);
}

void createQualifiedName(void* targetAddress, StringImpl* name)
{
    new (targetAddress) QualifiedName(nullAtom, AtomicString(name), nullAtom);
}

}
<|endoftext|>