blob: 42e651f7fd534393aafebd0fbd4fc5b14aadb509 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 "../base_types.hpp"
#include "../InputParserFactory.hpp"
#include "../CodeGeneratorFactory.hpp"
#include "../CodeGenerator.hpp"
#include "Log.hpp"
#include "Helper.hpp"
#include <iostream>
/**
* @namespace apache::geode::client::pdx_auto_serializer The namespace containing
* the
* auto-serializer classes.
*/
using namespace apache::geode::client::pdx_auto_serializer;
/** The name used for invocation of the program on command-line. */
std::string progName;
/** The parser factory object. */
InputParserFactory parserFactory;
/** The code generator factory object. */
CodeGeneratorFactory generatorFactory;
/** The parser returned by the parser factory. */
InputParser* parser = NULL;
/** The list of classes given by the parser. */
ASClassVector classes;
/** The <code>CodeGenerator</code> object returned by the factory. */
CodeGenerator* codeGenerator = NULL;
/**
* The options with their usage for the tool with current parser and
* code generator.
*/
OptionMap toolOptions;
/** Name of this module. */
static std::string ModuleName = "PdxAutoSerializer";
/**
* The option used to specify the language of the code to be auto-serialized.
*/
static const std::string LanguageOption = "language";
/**
* The option used to specify the language in which the auto-serialized code
* is to be generated.
*/
static const std::string GeneratorOption = "generator";
/** The option name for class name. */
static const std::string ClassNameOption = "className";
/** The option name for class name String */
static const std::string getClassNameOption = "classNameStr";
/** The option used to obtain usage information. */
static const std::string UsageOption = "usage";
/** The option for help -- gives same output as usage for now. */
static const std::string HelpOption = "help";
/** the option for directory to generate auto-generated files */
static const std::string outDirectoryOption = "outDir";
/** Fill in the default top-level options required for the tool. */
void addMainOptions();
/**
* Parse the command-line and get the resource names and the properties.
*
* @param argc The number of command-line arguments.
* @param argv The string array containing the command-line arguments.
* @param resources The names of the resources.
* @param properties Output parameter containing the properties as a map.
*/
void parseCommandLine(int argc, char** argv, StringVector& resources,
PropertyMap& properties);
/**
* Show the program usage. If any language/generator has been selected then
* its options are also shown.
*/
void showUsage();
/** Cleanly destroy all the global objects. */
void cleanupObjects();
/** Cleanup any files or other artifacts in case of abnormal exit. */
void cleanup();
/**
* The main entry point of the tool.
*
* @param argc The number of command-line arguments.
* @param argv The string array containing the command-line arguments.
*/
static void Tokenize(const std::string& str, std::vector<std::string>& tokens,
const std::string& delimiters = " ") {
std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
std::string::size_type pos = str.find_first_of(delimiters, lastPos);
while (std::string::npos != pos || std::string::npos != lastPos) {
tokens.push_back(str.substr(lastPos, pos - lastPos));
lastPos = str.find_first_not_of(delimiters, pos);
pos = str.find_first_of(delimiters, lastPos);
}
}
int main(int argc, char** argv) {
bool success = false;
struct CleanUp {
private:
bool& m_success;
public:
explicit CleanUp(bool& success) : m_success(success) {}
~CleanUp() {
if (m_success) {
cleanupObjects();
} else {
cleanup();
}
}
} cleanOnExit(success);
try {
StringVector resources;
PropertyMap properties;
const std::string languageString = "C++";
const std::string generatorString = "C++";
StringVector classNames;
progName = argv[0];
std::string::size_type baseIndex = progName.find_last_of("/\\");
if (baseIndex != std::string::npos) {
progName = progName.substr(baseIndex + 1);
}
addMainOptions();
parseCommandLine(argc, argv, resources, properties);
std::map<std::string, std::string> classNameStringMap;
try {
if (languageString.length() > 0 &&
(parser = parserFactory.getInstance(languageString)) == NULL) {
throw std::invalid_argument("No such language: " + languageString);
}
if (generatorString.length() > 0 &&
(codeGenerator = generatorFactory.getInstance(generatorString)) ==
NULL) {
throw std::invalid_argument("No such code generator: " +
generatorString);
}
if (parser != NULL) {
parser->getOptions(toolOptions);
}
if (codeGenerator != NULL) {
codeGenerator->getOptions(toolOptions);
}
std::string usageString;
if (Helper::getSingleProperty(properties, UsageOption, usageString) ||
Helper::getSingleProperty(properties, HelpOption, usageString)) {
showUsage();
return 0;
}
if (parser == NULL) {
throw std::invalid_argument("No input language specified.");
}
if (codeGenerator == NULL) {
throw std::invalid_argument("No output language specified.");
}
if (resources.size() == 0) {
throw std::invalid_argument("No input resources specified.");
}
if (!Helper::getMultiProperty(properties, ClassNameOption, classNames) ||
classNames.size() == 0) {
Log::warn(ModuleName,
"No class name specified; "
"will serialize all classes.");
}
std::string classNameString;
std::vector<std::string> classNameStringVector;
Helper::getMultiProperty(properties, getClassNameOption,
classNameStringVector);
for (std::vector<std::string>::iterator itr =
classNameStringVector.begin();
itr != classNameStringVector.end(); ++itr) {
std::vector<std::string> eachClassNameVector;
Tokenize(*itr, eachClassNameVector, ":");
if (eachClassNameVector.size() == 2) {
classNameStringMap.insert(std::pair<std::string, std::string>(
eachClassNameVector[0], eachClassNameVector[1]));
} else {
throw std::invalid_argument("No input class name string specified.");
}
}
parser->init(properties);
parser->selectClasses(resources, classNames);
parser->getSelectedClasses(classes);
codeGenerator->init(properties);
} catch (const std::exception& ex) {
std::cerr << Helper::typeName(ex) << ": " << ex.what() << std::endl;
std::cerr << "Use --help or --usage option for help" << std::endl;
return 1;
}
if (properties.size() > 0) {
showUsage();
return 1;
}
Log::info(ModuleName, "Classes being serialized:");
for (ASClassVector::const_iterator classIterator = classes.begin();
classIterator != classes.end(); ++classIterator) {
Log::info(ModuleName, '\t' + (*classIterator)->getName());
}
for (ASClassVector::const_iterator classIterator = classes.begin();
classIterator != classes.end(); ++classIterator) {
ReferenceVector references;
TypeInfo classInfo;
VariableVector members;
std::string varName = "__var";
const ClassInfo* currentClass = *classIterator;
try {
currentClass->getTypeInfo(classInfo);
codeGenerator->initClass(classInfo);
} catch (const std::exception& ex) {
std::cerr << Helper::typeName(ex) << ": " << ex.what() << std::endl;
std::cerr << "Use --help or --usage option for help" << std::endl;
return 1;
}
currentClass->getReferences(references);
currentClass->getMembers(members);
codeGenerator->addFileHeader(argc, argv);
codeGenerator->addReferences(references);
codeGenerator->startClass(members);
std::vector<CodeGenerator::Method::Type> methods;
methods.push_back(CodeGenerator::Method::TODATA);
methods.push_back(CodeGenerator::Method::FROMDATA);
for (size_t index = 0; index < methods.size(); ++index) {
CodeGenerator::Method::Type method = methods[index];
codeGenerator->startMethod(method, varName,
currentClass->getMethodPrefix());
// Adding try block
// Ticket #905 Changes starts here
codeGenerator->addTryBlockStart(method);
// Ticket #905 Changes ends here
for (VariableVectorIterator memberIterator = members.begin();
memberIterator != members.end(); ++memberIterator) {
codeGenerator->genMethod(method, varName, *memberIterator);
}
// Finish try block
// Ticket #905 Changes starts here
codeGenerator->finishTryBlock(method);
// Ticket #905 Changes ends here
codeGenerator->endMethod(method, varName);
}
codeGenerator->genClassNameMethod(classNameStringMap,
currentClass->getMethodPrefix());
codeGenerator->genCreateDeserializable(currentClass->getMethodPrefix());
codeGenerator->genTypeId(currentClass->getMethodPrefix());
codeGenerator->endClass();
}
} catch (const std::invalid_argument& ex) {
std::cerr << "Use --help or --usage option for help" << std::endl;
return 1;
} catch (const std::exception& ex) {
std::cerr << Helper::typeName(ex) << ": " << ex.what() << std::endl;
return 1;
} catch (...) {
std::cerr << "Caught an unknown exception." << std::endl;
return 1;
}
success = true;
return 0;
}
void addMainOptions() {
std::pair<bool, std::string> optionPair;
// std::string languageStr;
// StringVector languages = parserFactory.getParsers();
// assert(languages.size() > 0);
// StringVectorIterator languageIterator = languages.begin();
// languageStr = *languageIterator;
// while (++languageIterator != languages.end()) {
// languageStr += ',' + *languageIterator;
//}
// optionPair.first = true;
///*optionPair.second = "Generate code for the given language "
// "-- one of " + languageStr + " (SINGLE)";
// toolOptions[LanguageOption] = optionPair;*/
// std::string generatorStr;
// StringVector generators = generatorFactory.getGenerators();
// assert(generators.size() > 0);
// StringVectorIterator generatorIterator = generators.begin();
// generatorStr = *generatorIterator;
// while (++generatorIterator != generators.end()) {
// generatorStr += ',' + *generatorIterator;
//}
/*optionPair.second = "Generate code in the given generator "
"-- one of " + generatorStr + " (SINGLE)";
toolOptions[GeneratorOption] = optionPair;*/
optionPair.first = true;
optionPair.second =
"Name of the class for which to generate "
"auto-serialization code (MULTIPLE,OPTIONAL)";
toolOptions[ClassNameOption] = optionPair;
optionPair.first = false;
optionPair.second = (std::string) "\t\tThis usage message.";
toolOptions[UsageOption] = optionPair;
optionPair.first = false;
optionPair.second = (std::string) "\t\tThis help message.";
toolOptions[HelpOption] = optionPair;
optionPair.first = true;
optionPair.second = (std::string) "Name of the class in string representation"
"(MULTIPLE,OPTIONAL)";
toolOptions[getClassNameOption] = optionPair;
optionPair.first = false;
optionPair.second = (std::string) "\tName of the directory where auto-generated"
"files will be written, default is current working directory";
toolOptions[outDirectoryOption] = optionPair;
}
void parseCommandLine(int argc, char** argv, StringVector& resources,
PropertyMap& properties) {
if (argc < 2) {
throw std::invalid_argument("");
}
for (int propertyIndex = 1; propertyIndex < argc; propertyIndex++) {
char* arg = argv[propertyIndex];
if (arg[0] == '-' && arg[1] == '-') {
// Is a property value option.
std::string prop = arg + 2;
std::string propertyName;
std::string propertyValue;
std::string::size_type valPos;
if ((valPos = prop.find('=')) != std::string::npos) {
propertyName = prop.substr(0, valPos);
propertyValue = prop.substr(valPos + 1);
} else {
propertyName = prop;
}
PropertyMap::iterator propertyFind = properties.find(propertyName);
if (propertyFind == properties.end()) {
StringVector propertyValues;
if (propertyValue.length() > 0) {
propertyValues.push_back(propertyValue);
}
properties[propertyName] = propertyValues;
} else if (propertyValue.length() > 0) {
propertyFind->second.push_back(propertyValue);
}
} else {
resources.push_back(arg);
}
}
}
void showUsage() {
std::cout << "Usage: " << progName << " [OPTIONS] <resources e.g. "
"header> ...\n\n";
std::cout << "Resource name should be the path to the header "
"containing the classes to be auto-serialized.\n\n";
std::cout
<< "Options may be one of those given below.\nSINGLE denotes "
"that the option should be specified only once.\nMULTIPLE denotes "
"that the "
"option can be specified more than once.\nOPTIONAL denotes that the "
"option may be skipped in which case the default for that shall be "
"chosen.";
std::cout << '\n' << std::endl;
for (OptionMap::const_iterator optionIterator = toolOptions.begin();
optionIterator != toolOptions.end(); ++optionIterator) {
std::cout << "--" << optionIterator->first;
if (optionIterator->second.first) {
std::cout << "=VALUE";
}
std::cout << '\t' << optionIterator->second.second << '\n';
}
std::cout << std::endl;
std::cout << "Examples:" << std::endl;
std::cout << "\t pdxautoserializer -outDir=<DIR NAME> <RESOURCE>"
<< std::endl;
std::cout
<< "\t pdxautoserializer -outDir=<DIR NAME> --className=<CLASSNAME1> ";
std::cout << "--className=<CLASSNAME2> <RESOURCE>" << std::endl;
std::cout << "\t pdxautoserializer -outDir=<DIR NAME> "
"--classNameStr=<CLASSNAME1:User defined String> ";
std::cout << "--classNameStr=<CLASSNAME:User defined String> <RESOURCE>"
<< std::endl
<< std::endl;
std::cout << "Helper Macros to be defined in Input Header File : "
<< std::endl;
std::cout << "GFINCLUDE\t for including a specific member for serialization"
<< std::endl;
std::cout << "GFEXCLUDE\t for excluding a specific member for serialization"
<< std::endl;
std::cout << "GFID\t\t for considering a member as Identify Field"
<< std::endl;
std::cout << "GFARRAYSIZE\t for specifying a array length member"
<< std::endl;
std::cout << "GFIGNORE\t for ignoring certain keywords" << std::endl;
std::cout << "For more details refer to documentation on this utility."
<< std::endl;
}
void cleanupObjects() {
Helper::deleteASClasses(classes);
if (parser != NULL) {
delete parser;
parser = NULL;
}
if (codeGenerator != NULL) {
delete codeGenerator;
codeGenerator = NULL;
}
}
void cleanup() {
if (codeGenerator != NULL) {
codeGenerator->cleanup();
}
cleanupObjects();
}