/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Formatter.h" #include #include #include #include #include namespace android { Formatter::Formatter() : mFile(nullptr /* invalid */), mIndentDepth(0), mCurrentPosition(0) {} Formatter::Formatter(FILE* file, size_t spacesPerIndent) : mFile(file == nullptr ? stdout : file), mIndentDepth(0), mSpacesPerIndent(spacesPerIndent), mCurrentPosition(0) {} Formatter::~Formatter() { if (mFile != stdout) { fclose(mFile); } mFile = nullptr; } void Formatter::indent(size_t level) { mIndentDepth += level; } void Formatter::unindent(size_t level) { assert(mIndentDepth >= level); mIndentDepth -= level; } Formatter& Formatter::indent(size_t level, const std::function& func) { this->indent(level); func(); this->unindent(level); return *this; } Formatter& Formatter::indent(const std::function& func) { return this->indent(1, func); } Formatter& Formatter::block(const std::function& func) { (*this) << "{\n"; this->indent(func); return (*this) << "}"; } void Formatter::pushLinePrefix(const std::string& prefix) { mLinePrefix.push_back(prefix); } void Formatter::popLinePrefix() { mLinePrefix.pop_back(); } Formatter &Formatter::endl() { return (*this) << "\n"; } Formatter& Formatter::sIf(const std::string& cond, const std::function& block) { (*this) << "if (" << cond << ") "; return this->block(block); } Formatter& Formatter::sElseIf(const std::string& cond, const std::function& block) { (*this) << " else if (" << cond << ") "; return this->block(block); } Formatter& Formatter::sElse(const std::function& block) { (*this) << " else "; return this->block(block); } Formatter& Formatter::sFor(const std::string& stmts, const std::function& block) { (*this) << "for (" << stmts << ") "; return this->block(block); } Formatter& Formatter::sTry(const std::function& block) { (*this) << "try "; return this->block(block); } Formatter& Formatter::sCatch(const std::string& exception, const std::function& block) { (*this) << " catch (" << exception << ") "; return this->block(block); } Formatter& Formatter::sFinally(const std::function& block) { (*this) << " finally "; return this->block(block); } Formatter& Formatter::sWhile(const std::string& cond, const std::function& block) { (*this) << "while (" << cond << ") "; return this->block(block); } Formatter& Formatter::operator<<(const std::string& out) { const size_t len = out.length(); size_t start = 0; const std::string& prefix = base::Join(mLinePrefix, ""); while (start < len) { size_t pos = out.find('\n', start); if (pos == std::string::npos) { if (mCurrentPosition == 0) { fprintf(mFile, "%*s", (int)(getIndentation()), ""); fprintf(mFile, "%s", prefix.c_str()); mCurrentPosition = getIndentation() + prefix.size(); } std::string sub = out.substr(start); output(sub); mCurrentPosition += sub.size(); break; } if (mCurrentPosition == 0 && (pos > start || !prefix.empty())) { fprintf(mFile, "%*s", (int)(getIndentation()), ""); fprintf(mFile, "%s", prefix.c_str()); mCurrentPosition = getIndentation() + prefix.size(); } if (pos == start) { fprintf(mFile, "\n"); mCurrentPosition = 0; } else if (pos > start) { output(out.substr(start, pos - start + 1)); mCurrentPosition = 0; } start = pos + 1; } return *this; } void Formatter::printBlock(const WrappedOutput::Block& block, size_t lineLength) { size_t prefixSize = 0; for (const std::string& prefix : mLinePrefix) { prefixSize += prefix.size(); } size_t lineStart = mCurrentPosition ?: (getIndentation() + prefixSize); size_t blockSize = block.computeSize(false); if (blockSize + lineStart < lineLength) { block.print(*this, false); return; } // Everything will not fit on this line. Try to fit it on the next line. blockSize = block.computeSize(true); if ((blockSize + getIndentation() + mSpacesPerIndent + prefixSize) < lineLength) { *this << "\n"; indent(); block.print(*this, true); unindent(); return; } if (!block.content.empty()) { // Doesn't have subblocks. This means that the block itself is too big. // Have to print it out. *this << "\n"; indent(); block.print(*this, true); unindent(); return; } // Everything will not fit on this line. Go through all the children for (const WrappedOutput::Block& subBlock : block.blocks) { printBlock(subBlock, lineLength); } } Formatter& Formatter::operator<<(const WrappedOutput& wrappedOutput) { printBlock(wrappedOutput.mRootBlock, wrappedOutput.mLineLength); return *this; } // NOLINT to suppress missing parentheses warning about __type__. #define FORMATTER_INPUT_INTEGER(__type__) \ Formatter& Formatter::operator<<(__type__ n) { /* NOLINT */ \ return (*this) << std::to_string(n); \ } FORMATTER_INPUT_INTEGER(short); FORMATTER_INPUT_INTEGER(unsigned short); FORMATTER_INPUT_INTEGER(int); FORMATTER_INPUT_INTEGER(unsigned int); FORMATTER_INPUT_INTEGER(long); FORMATTER_INPUT_INTEGER(unsigned long); FORMATTER_INPUT_INTEGER(long long); FORMATTER_INPUT_INTEGER(unsigned long long); FORMATTER_INPUT_INTEGER(float); FORMATTER_INPUT_INTEGER(double); FORMATTER_INPUT_INTEGER(long double); #undef FORMATTER_INPUT_INTEGER // NOLINT to suppress missing parentheses warning about __type__. #define FORMATTER_INPUT_CHAR(__type__) \ Formatter& Formatter::operator<<(__type__ c) { /* NOLINT */ \ return (*this) << std::string(1, (char)c); \ } FORMATTER_INPUT_CHAR(char); FORMATTER_INPUT_CHAR(signed char); FORMATTER_INPUT_CHAR(unsigned char); #undef FORMATTER_INPUT_CHAR bool Formatter::isValid() const { return mFile != nullptr; } size_t Formatter::getIndentation() const { return mSpacesPerIndent * mIndentDepth; } void Formatter::output(const std::string &text) const { CHECK(isValid()); fprintf(mFile, "%s", text.c_str()); } WrappedOutput::Block::Block(const std::string& content, Block* const parent) : content(content), parent(parent) {} size_t WrappedOutput::Block::computeSize(bool wrapped) const { CHECK(content.empty() || blocks.empty()); // There is a wrap, so the block would not be printed if (printUnlessWrapped && wrapped) return 0; size_t size = content.size(); for (auto block = blocks.begin(); block != blocks.end(); ++block) { if (block == blocks.begin()) { // Only the first one can be wrapped (since content.empty()) size += block->computeSize(wrapped); } else { size += block->computeSize(false); } } return size; } void WrappedOutput::Block::print(Formatter& out, bool wrapped) const { CHECK(content.empty() || blocks.empty()); // There is a wrap, so the block should not be printed if (printUnlessWrapped && wrapped) return; out << content; for (auto block = blocks.begin(); block != blocks.end(); ++block) { if (block == blocks.begin()) { // Only the first one can be wrapped (since content.empty()) block->print(out, wrapped); } else { block->print(out, false); } } } WrappedOutput::WrappedOutput(size_t lineLength) : mLineLength(lineLength), mRootBlock(Block("", nullptr)) { mCurrentBlock = &mRootBlock; } WrappedOutput& WrappedOutput::operator<<(const std::string& str) { std::vector& blockVec = mCurrentBlock->blocks; if (!blockVec.empty()) { Block& last = blockVec.back(); if (!last.populated && last.blocks.empty()) { last.content += str; return *this; } } blockVec.emplace_back(str, mCurrentBlock); return *this; } WrappedOutput& WrappedOutput::printUnlessWrapped(const std::string& str) { std::vector& blockVec = mCurrentBlock->blocks; if (!blockVec.empty()) { blockVec.back().populated = true; } blockVec.emplace_back(str, mCurrentBlock); blockVec.back().populated = true; blockVec.back().printUnlessWrapped = true; return *this; } void WrappedOutput::group(const std::function& block) { std::vector& blockVec = mCurrentBlock->blocks; if (!blockVec.empty()) { blockVec.back().populated = true; } blockVec.emplace_back("", mCurrentBlock); mCurrentBlock = &blockVec.back(); block(); mCurrentBlock->populated = true; mCurrentBlock = mCurrentBlock->parent; } } // namespace android