summaryrefslogtreecommitdiff
path: root/cpp/src/Slice/JavaUtil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/Slice/JavaUtil.cpp')
-rw-r--r--cpp/src/Slice/JavaUtil.cpp2906
1 files changed, 2906 insertions, 0 deletions
diff --git a/cpp/src/Slice/JavaUtil.cpp b/cpp/src/Slice/JavaUtil.cpp
new file mode 100644
index 00000000000..8d7ab74f49b
--- /dev/null
+++ b/cpp/src/Slice/JavaUtil.cpp
@@ -0,0 +1,2906 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#include <IceUtil/DisableWarnings.h>
+#include <Slice/JavaUtil.h>
+#include <Slice/FileTracker.h>
+#include <Slice/Util.h>
+#include <IceUtil/Functional.h>
+#include <IceUtil/DisableWarnings.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#ifdef _WIN32
+#include <direct.h>
+#endif
+
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+using namespace std;
+using namespace Slice;
+using namespace IceUtil;
+using namespace IceUtilInternal;
+
+Slice::JavaOutput::JavaOutput()
+{
+}
+
+Slice::JavaOutput::JavaOutput(ostream& os) :
+ Output(os)
+{
+}
+
+Slice::JavaOutput::JavaOutput(const char* s) :
+ Output(s)
+{
+}
+
+void
+Slice::JavaOutput::openClass(const string& cls, const string& prefix)
+{
+ string package;
+ string file;
+ string path = prefix;
+
+ string::size_type pos = cls.rfind('.');
+ if(pos != string::npos)
+ {
+ package = cls.substr(0, pos);
+ file = cls.substr(pos + 1);
+ string dir = package;
+
+ //
+ // Create package directories if necessary.
+ //
+ pos = 0;
+ string::size_type start = 0;
+ do
+ {
+ if(!path.empty())
+ {
+ path += "/";
+ }
+ pos = dir.find('.', start);
+ if(pos != string::npos)
+ {
+ path += dir.substr(start, pos - start);
+ start = pos + 1;
+ }
+ else
+ {
+ path += dir.substr(start);
+ }
+
+ struct stat st;
+ int result;
+ result = stat(path.c_str(), &st);
+ if(result == 0)
+ {
+ if(!(st.st_mode & S_IFDIR))
+ {
+ ostringstream os;
+ os << "failed to create package directory `" << path
+ << "': file already exists and is not a directory";
+ throw FileException(__FILE__, __LINE__, os.str());
+ }
+ continue;
+ }
+#ifdef _WIN32
+ result = _mkdir(path.c_str());
+#else
+ result = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
+#endif
+ if(result != 0)
+ {
+ ostringstream os;
+ os << "cannot create directory `" << path << "': " << strerror(errno);
+ throw FileException(__FILE__, __LINE__, os.str());
+ }
+ FileTracker::instance()->addDirectory(path);
+ }
+ while(pos != string::npos);
+ }
+ else
+ {
+ file = cls;
+ }
+ file += ".java";
+
+ //
+ // Open class file.
+ //
+ if(!path.empty())
+ {
+ path += "/";
+ }
+ path += file;
+
+ open(path.c_str());
+ if(isOpen())
+ {
+ FileTracker::instance()->addFile(path);
+ printHeader();
+ printGeneratedHeader(*this, file);
+ if(!package.empty())
+ {
+ separator();
+ print("package ");
+ print(package.c_str());
+ print(";");
+ }
+ }
+ else
+ {
+ ostringstream os;
+ os << "cannot open file `" << path << "': " << strerror(errno);
+ throw FileException(__FILE__, __LINE__, os.str());
+ }
+}
+
+void
+Slice::JavaOutput::printHeader()
+{
+ static const char* header =
+"// **********************************************************************\n"
+"//\n"
+"// Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved.\n"
+"//\n"
+"// This copy of Ice is licensed to you under the terms described in the\n"
+"// ICE_LICENSE file included in this distribution.\n"
+"//\n"
+"// **********************************************************************\n"
+ ;
+
+ print(header);
+ print("//\n");
+ print("// Ice version ");
+ print(ICE_STRING_VERSION);
+ print("\n");
+ print("//\n");
+}
+
+const string Slice::JavaGenerator::_getSetMetaData = "java:getset";
+
+Slice::JavaGenerator::JavaGenerator(const string& dir) :
+ _dir(dir),
+ _out(0)
+{
+}
+
+Slice::JavaGenerator::~JavaGenerator()
+{
+ // If open throws an exception other generators could be left open
+ // during the stack unwind.
+ if(_out != 0)
+ {
+ close();
+ }
+ assert(_out == 0);
+}
+
+void
+Slice::JavaGenerator::open(const string& absolute, const string& file)
+{
+ assert(_out == 0);
+
+ JavaOutput* out = createOutput();
+ try
+ {
+ out->openClass(absolute, _dir);
+ }
+ catch(const FileException&)
+ {
+ delete out;
+ throw;
+ }
+ _out = out;
+}
+
+void
+Slice::JavaGenerator::close()
+{
+ assert(_out != 0);
+ *_out << nl;
+ delete _out;
+ _out = 0;
+}
+
+Output&
+Slice::JavaGenerator::output() const
+{
+ assert(_out != 0);
+ return *_out;
+}
+
+static string
+lookupKwd(const string& name)
+{
+ //
+ // Keyword list. *Must* be kept in alphabetical order. Note that checkedCast and uncheckedCast
+ // are not Java keywords, but are in this list to prevent illegal code being generated if
+ // someone defines Slice operations with that name.
+ //
+ // NOTE: Any changes made to this list must also be made in BasicStream.java.
+ //
+ static const string keywordList[] =
+ {
+ "abstract", "assert", "boolean", "break", "byte", "case", "catch",
+ "char", "checkedCast", "class", "clone", "const", "continue", "default", "do",
+ "double", "else", "enum", "equals", "extends", "false", "final", "finalize",
+ "finally", "float", "for", "getClass", "goto", "hashCode", "if",
+ "implements", "import", "instanceof", "int", "interface", "long",
+ "native", "new", "notify", "notifyAll", "null", "package", "private",
+ "protected", "public", "return", "short", "static", "strictfp", "super", "switch",
+ "synchronized", "this", "throw", "throws", "toString", "transient",
+ "true", "try", "uncheckedCast", "void", "volatile", "wait", "while"
+ };
+ bool found = binary_search(&keywordList[0],
+ &keywordList[sizeof(keywordList) / sizeof(*keywordList)],
+ name);
+ return found ? "_" + name : name;
+}
+
+//
+// Split a scoped name into its components and return the components as a list of (unscoped) identifiers.
+//
+static StringList
+splitScopedName(const string& scoped)
+{
+ assert(scoped[0] == ':');
+ StringList ids;
+ string::size_type next = 0;
+ string::size_type pos;
+ while((pos = scoped.find("::", next)) != string::npos)
+ {
+ pos += 2;
+ if(pos != scoped.size())
+ {
+ string::size_type endpos = scoped.find("::", pos);
+ if(endpos != string::npos)
+ {
+ ids.push_back(scoped.substr(pos, endpos - pos));
+ }
+ }
+ next = pos;
+ }
+ if(next != scoped.size())
+ {
+ ids.push_back(scoped.substr(next));
+ }
+ else
+ {
+ ids.push_back("");
+ }
+
+ return ids;
+}
+
+//
+// If the passed name is a scoped name, return the identical scoped name,
+// but with all components that are Java keywords replaced by
+// their "_"-prefixed version; otherwise, if the passed name is
+// not scoped, but a Java keyword, return the "_"-prefixed name;
+// otherwise, return the name unchanged.
+//
+string
+Slice::JavaGenerator::fixKwd(const string& name) const
+{
+ if(name.empty())
+ {
+ return name;
+ }
+ if(name[0] != ':')
+ {
+ return lookupKwd(name);
+ }
+ StringList ids = splitScopedName(name);
+ transform(ids.begin(), ids.end(), ids.begin(), ptr_fun(lookupKwd));
+ stringstream result;
+ for(StringList::const_iterator i = ids.begin(); i != ids.end(); ++i)
+ {
+ result << "::" + *i;
+ }
+ return result.str();
+}
+
+string
+Slice::JavaGenerator::convertScopedName(const string& scoped, const string& prefix, const string& suffix) const
+{
+ string result;
+ string::size_type start = 0;
+ string fscoped = fixKwd(scoped);
+
+ //
+ // Skip leading "::"
+ //
+ if(fscoped[start] == ':')
+ {
+ assert(fscoped[start + 1] == ':');
+ start += 2;
+ }
+
+ //
+ // Convert all occurrences of "::" to "."
+ //
+ string::size_type pos;
+ do
+ {
+ pos = fscoped.find(':', start);
+ string fix;
+ if(pos == string::npos)
+ {
+ string s = fscoped.substr(start);
+ if(!s.empty())
+ {
+ fix = prefix + fixKwd(s) + suffix;
+ }
+ }
+ else
+ {
+ assert(fscoped[pos + 1] == ':');
+ fix = fixKwd(fscoped.substr(start, pos - start));
+ start = pos + 2;
+ }
+
+ if(!result.empty() && !fix.empty())
+ {
+ result += ".";
+ }
+ result += fix;
+ }
+ while(pos != string::npos);
+
+ return result;
+}
+
+string
+Slice::JavaGenerator::getPackagePrefix(const ContainedPtr& cont) const
+{
+ UnitPtr unit = cont->container()->unit();
+ string file = cont->file();
+ assert(!file.empty());
+
+ map<string, string>::const_iterator p = _filePackagePrefix.find(file);
+ if(p != _filePackagePrefix.end())
+ {
+ return p->second;
+ }
+
+ static const string prefix = "java:package:";
+ DefinitionContextPtr dc = unit->findDefinitionContext(file);
+ assert(dc);
+ string q = dc->findMetaData(prefix);
+ if(!q.empty())
+ {
+ q = q.substr(prefix.size());
+ }
+ _filePackagePrefix[file] = q;
+ return q;
+}
+
+string
+Slice::JavaGenerator::getPackage(const ContainedPtr& cont) const
+{
+ string scope = convertScopedName(cont->scope());
+ string prefix = getPackagePrefix(cont);
+ if(!prefix.empty())
+ {
+ if(!scope.empty())
+ {
+ return prefix + "." + scope;
+ }
+ else
+ {
+ return prefix;
+ }
+ }
+
+ return scope;
+}
+
+string
+Slice::JavaGenerator::getAbsolute(const ContainedPtr& cont,
+ const string& package,
+ const string& prefix,
+ const string& suffix) const
+{
+ string name = cont->name();
+ if(prefix == "" && suffix == "")
+ {
+ name = fixKwd(name);
+ }
+ string contPkg = getPackage(cont);
+ if(contPkg == package)
+ {
+ return prefix + name + suffix;
+ }
+ else if(!contPkg.empty())
+ {
+ return contPkg + "." + prefix + name + suffix;
+ }
+ else
+ {
+ return prefix + name + suffix;
+ }
+}
+
+string
+Slice::JavaGenerator::typeToString(const TypePtr& type,
+ TypeMode mode,
+ const string& package,
+ const StringList& metaData,
+ bool formal) const
+{
+ static const char* builtinTable[] =
+ {
+ "byte",
+ "boolean",
+ "short",
+ "int",
+ "long",
+ "float",
+ "double",
+ "String",
+ "Ice.Object",
+ "Ice.ObjectPrx",
+ "java.lang.Object"
+ };
+ static const char* builtinHolderTable[] =
+ {
+ "Ice.ByteHolder",
+ "Ice.BooleanHolder",
+ "Ice.ShortHolder",
+ "Ice.IntHolder",
+ "Ice.LongHolder",
+ "Ice.FloatHolder",
+ "Ice.DoubleHolder",
+ "Ice.StringHolder",
+ "Ice.ObjectHolder",
+ "Ice.ObjectPrxHolder",
+ "Ice.LocalObjectHolder"
+ };
+
+ if(!type)
+ {
+ assert(mode == TypeModeReturn);
+ return "void";
+ }
+
+ BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
+ if(builtin)
+ {
+ if(mode == TypeModeOut)
+ {
+ return builtinHolderTable[builtin->kind()];
+ }
+ else
+ {
+ return builtinTable[builtin->kind()];
+ }
+ }
+
+ ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type);
+ if(cl)
+ {
+ return getAbsolute(cl, package, "", mode == TypeModeOut ? "Holder" : "");
+ }
+
+ ProxyPtr proxy = ProxyPtr::dynamicCast(type);
+ if(proxy)
+ {
+ return getAbsolute(proxy->_class(), package, "", mode == TypeModeOut ? "PrxHolder" : "Prx");
+ }
+
+ DictionaryPtr dict = DictionaryPtr::dynamicCast(type);
+ if(dict)
+ {
+ if(mode == TypeModeOut)
+ {
+ //
+ // Only use the type's generated holder if the instance and
+ // formal types match.
+ //
+ string instanceType, formalType;
+ getDictionaryTypes(dict, "", metaData, instanceType, formalType);
+ string origInstanceType, origFormalType;
+ getDictionaryTypes(dict, "", StringList(), origInstanceType, origFormalType);
+ if(formalType == origFormalType && instanceType == origInstanceType)
+ {
+ return getAbsolute(dict, package, "", "Holder");
+ }
+
+ //
+ // The custom type may or may not be compatible with the type used
+ // in the generated holder. We use a generic holder that holds a value of the
+ // formal custom type.
+ //
+ return string("Ice.Holder<") + formalType + " >";
+ }
+ else
+ {
+ string instanceType, formalType;
+ getDictionaryTypes(dict, package, metaData, instanceType, formalType);
+ return formal ? formalType : instanceType;
+ }
+ }
+
+ SequencePtr seq = SequencePtr::dynamicCast(type);
+ if(seq)
+ {
+ if(mode == TypeModeOut)
+ {
+ BuiltinPtr builtin = BuiltinPtr::dynamicCast(seq->type());
+ if(builtin && builtin->kind() == Builtin::KindByte)
+ {
+ string prefix = "java:serializable:";
+ string meta;
+ if(seq->findMetaData(prefix, meta))
+ {
+ return string("Ice.Holder<") + meta.substr(prefix.size()) + " >";
+ }
+ prefix = "java:protobuf:";
+ if(seq->findMetaData(prefix, meta))
+ {
+ return string("Ice.Holder<") + meta.substr(prefix.size()) + " >";
+ }
+ }
+
+ //
+ // Only use the type's generated holder if the instance and
+ // formal types match.
+ //
+ string instanceType, formalType;
+ getSequenceTypes(seq, "", metaData, instanceType, formalType);
+ string origInstanceType, origFormalType;
+ getSequenceTypes(seq, "", StringList(), origInstanceType, origFormalType);
+ if(formalType == origFormalType && instanceType == origInstanceType)
+ {
+ return getAbsolute(seq, package, "", "Holder");
+ }
+
+ //
+ // The custom type may or may not be compatible with the type used
+ // in the generated holder. We use a generic holder that holds a value of the
+ // formal custom type.
+ //
+ return string("Ice.Holder<") + formalType + " >";
+ }
+ else
+ {
+ BuiltinPtr builtin = BuiltinPtr::dynamicCast(seq->type());
+ if(builtin && builtin->kind() == Builtin::KindByte)
+ {
+ string prefix = "java:serializable:";
+ string meta;
+ if(seq->findMetaData(prefix, meta))
+ {
+ return meta.substr(prefix.size());
+ }
+ prefix = "java:protobuf:";
+ if(seq->findMetaData(prefix, meta))
+ {
+ return meta.substr(prefix.size());
+ }
+ }
+
+ string instanceType, formalType;
+ getSequenceTypes(seq, package, metaData, instanceType, formalType);
+ return formal ? formalType : instanceType;
+ }
+ }
+
+ ContainedPtr contained = ContainedPtr::dynamicCast(type);
+ if(contained)
+ {
+ if(mode == TypeModeOut)
+ {
+ return getAbsolute(contained, package, "", "Holder");
+ }
+ else
+ {
+ return getAbsolute(contained, package);
+ }
+ }
+
+ return "???";
+}
+
+string
+Slice::JavaGenerator::typeToObjectString(const TypePtr& type,
+ TypeMode mode,
+ const string& package,
+ const StringList& metaData,
+ bool formal) const
+{
+ static const char* builtinTable[] =
+ {
+ "java.lang.Byte",
+ "java.lang.Boolean",
+ "java.lang.Short",
+ "java.lang.Integer",
+ "java.lang.Long",
+ "java.lang.Float",
+ "java.lang.Double",
+ "java.lang.String",
+ "Ice.Object",
+ "Ice.ObjectPrx",
+ "java.lang.Object"
+ };
+
+ BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
+ if(builtin && mode != TypeModeOut)
+ {
+ return builtinTable[builtin->kind()];
+ }
+
+ return typeToString(type, mode, package, metaData, formal);
+}
+
+void
+Slice::JavaGenerator::writeMarshalUnmarshalCode(Output& out,
+ const string& package,
+ const TypePtr& type,
+ const string& param,
+ bool marshal,
+ int& iter,
+ bool holder,
+ const StringList& metaData,
+ const string& patchParams)
+{
+ string stream = marshal ? "__os" : "__is";
+ string v;
+ if(holder)
+ {
+ v = param + ".value";
+ }
+ else
+ {
+ v = param;
+ }
+
+ BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
+ if(builtin)
+ {
+ switch(builtin->kind())
+ {
+ case Builtin::KindByte:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeByte(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readByte();";
+ }
+ break;
+ }
+ case Builtin::KindBool:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeBool(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readBool();";
+ }
+ break;
+ }
+ case Builtin::KindShort:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeShort(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readShort();";
+ }
+ break;
+ }
+ case Builtin::KindInt:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeInt(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readInt();";
+ }
+ break;
+ }
+ case Builtin::KindLong:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeLong(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readLong();";
+ }
+ break;
+ }
+ case Builtin::KindFloat:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeFloat(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readFloat();";
+ }
+ break;
+ }
+ case Builtin::KindDouble:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeDouble(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readDouble();";
+ }
+ break;
+ }
+ case Builtin::KindString:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeString(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readString();";
+ }
+ break;
+ }
+ case Builtin::KindObject:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeObject(" << v << ");";
+ }
+ else
+ {
+ if(holder)
+ {
+ out << nl << stream << ".readObject(" << param << ");";
+ }
+ else
+ {
+ if(patchParams.empty())
+ {
+ out << nl << stream << ".readObject(new Patcher());";
+ }
+ else
+ {
+ out << nl << stream << ".readObject(" << patchParams << ");";
+ }
+ }
+ }
+ break;
+ }
+ case Builtin::KindObjectProxy:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeProxy(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readProxy();";
+ }
+ break;
+ }
+ case Builtin::KindLocalObject:
+ {
+ assert(false);
+ break;
+ }
+ }
+ return;
+ }
+
+ ProxyPtr prx = ProxyPtr::dynamicCast(type);
+ if(prx)
+ {
+ string typeS = typeToString(type, TypeModeIn, package);
+ if(marshal)
+ {
+ out << nl << typeS << "Helper.__write(" << stream << ", " << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << typeS << "Helper.__read(" << stream << ");";
+ }
+ return;
+ }
+
+ ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type);
+ if(cl)
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeObject(" << v << ");";
+ }
+ else
+ {
+ string typeS = typeToString(type, TypeModeIn, package);
+ if(holder)
+ {
+ out << nl << stream << ".readObject(" << param << ");";
+ }
+ else
+ {
+ if(patchParams.empty())
+ {
+ out << nl << stream << ".readObject(new Patcher());";
+ }
+ else
+ {
+ out << nl << stream << ".readObject(" << patchParams << ");";
+ }
+ }
+ }
+ return;
+ }
+
+ StructPtr st = StructPtr::dynamicCast(type);
+ if(st)
+ {
+ if(marshal)
+ {
+ out << nl << v << ".__write(" << stream << ");";
+ }
+ else
+ {
+ string typeS = typeToString(type, TypeModeIn, package);
+ out << nl << v << " = new " << typeS << "();";
+ out << nl << v << ".__read(" << stream << ");";
+ }
+ return;
+ }
+
+ EnumPtr en = EnumPtr::dynamicCast(type);
+ if(en)
+ {
+ if(marshal)
+ {
+ out << nl << v << ".__write(" << stream << ");";
+ }
+ else
+ {
+ string typeS = typeToString(type, TypeModeIn, package);
+ out << nl << v << " = " << typeS << ".__read(" << stream << ");";
+ }
+ return;
+ }
+
+ DictionaryPtr dict = DictionaryPtr::dynamicCast(type);
+ if(dict)
+ {
+ writeDictionaryMarshalUnmarshalCode(out, package, dict, v, marshal, iter, true, metaData);
+ return;
+ }
+
+ SequencePtr seq = SequencePtr::dynamicCast(type);
+ if(seq)
+ {
+ writeSequenceMarshalUnmarshalCode(out, package, seq, v, marshal, iter, true, metaData);
+ return;
+ }
+
+ ConstructedPtr constructed = ConstructedPtr::dynamicCast(type);
+ assert(constructed);
+ string typeS = getAbsolute(constructed, package);
+ if(marshal)
+ {
+ out << nl << typeS << "Helper.write(" << stream << ", " << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << typeS << "Helper.read(" << stream << ");";
+ }
+}
+
+void
+Slice::JavaGenerator::writeDictionaryMarshalUnmarshalCode(Output& out,
+ const string& package,
+ const DictionaryPtr& dict,
+ const string& param,
+ bool marshal,
+ int& iter,
+ bool useHelper,
+ const StringList& metaData)
+{
+ string stream = marshal ? "__os" : "__is";
+ string v = param;
+
+ string instanceType;
+
+ //
+ // We have to determine whether it's possible to use the
+ // type's generated helper class for this marshal/unmarshal
+ // task. Since the user may have specified a custom type in
+ // metadata, it's possible that the helper class is not
+ // compatible and therefore we'll need to generate the code
+ // in-line instead.
+ //
+ // Specifically, there may be "local" metadata (i.e., from
+ // a data member or parameter definition) that overrides the
+ // original type. We'll compare the mapped types with and
+ // without local metadata to determine whether we can use
+ // the helper.
+ //
+ string formalType;
+ getDictionaryTypes(dict, "", metaData, instanceType, formalType);
+ string origInstanceType, origFormalType;
+ getDictionaryTypes(dict, "", StringList(), origInstanceType, origFormalType);
+ if((formalType != origFormalType) || (!marshal && instanceType != origInstanceType))
+ {
+ useHelper = false;
+ }
+
+ //
+ // If we can use the helper, it's easy.
+ //
+ if(useHelper)
+ {
+ string typeS = getAbsolute(dict, package);
+ if(marshal)
+ {
+ out << nl << typeS << "Helper.write(" << stream << ", " << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << typeS << "Helper.read(" << stream << ");";
+ }
+ return;
+ }
+
+ TypePtr key = dict->keyType();
+ TypePtr value = dict->valueType();
+
+ string keyS = typeToString(key, TypeModeIn, package);
+ string valueS = typeToString(value, TypeModeIn, package);
+ int i;
+
+ ostringstream o;
+ o << iter;
+ string iterS = o.str();
+ iter++;
+
+ if(marshal)
+ {
+ out << nl << "if(" << v << " == null)";
+ out << sb;
+ out << nl << "__os.writeSize(0);";
+ out << eb;
+ out << nl << "else";
+ out << sb;
+ out << nl << "__os.writeSize(" << v << ".size());";
+ string keyObjectS = typeToObjectString(key, TypeModeIn, package);
+ string valueObjectS = typeToObjectString(value, TypeModeIn, package);
+ out << nl << "for(java.util.Map.Entry<" << keyObjectS << ", " << valueObjectS << "> __e : " << v
+ << ".entrySet())";
+ out << sb;
+ for(i = 0; i < 2; i++)
+ {
+ string arg;
+ TypePtr type;
+ if(i == 0)
+ {
+ arg = "__e.getKey()";
+ type = key;
+ }
+ else
+ {
+ arg = "__e.getValue()";
+ type = value;
+ }
+ writeMarshalUnmarshalCode(out, package, type, arg, true, iter, false);
+ }
+ out << eb;
+ out << eb;
+ }
+ else
+ {
+ out << nl << v << " = new " << instanceType << "();";
+ out << nl << "int __sz" << iterS << " = __is.readSize();";
+ out << nl << "for(int __i" << iterS << " = 0; __i" << iterS << " < __sz" << iterS << "; __i" << iterS << "++)";
+ out << sb;
+ for(i = 0; i < 2; i++)
+ {
+ string arg;
+ TypePtr type;
+ string typeS;
+ if(i == 0)
+ {
+ arg = "__key";
+ type = key;
+ typeS = keyS;
+ }
+ else
+ {
+ arg = "__value";
+ type = value;
+ typeS = valueS;
+ }
+
+ BuiltinPtr b = BuiltinPtr::dynamicCast(type);
+ if(ClassDeclPtr::dynamicCast(type) || (b && b->kind() == Builtin::KindObject))
+ {
+ string keyTypeStr = typeToObjectString(key, TypeModeIn, package);
+ string valueTypeStr = typeToObjectString(value, TypeModeIn, package);
+ writeMarshalUnmarshalCode(out, package, type, arg, false, iter, false, StringList(),
+ "new IceInternal.DictionaryPatcher<" + keyTypeStr + ", " + valueTypeStr +
+ ">(" + v + ", " + typeS + ".class, \"" + type->typeId() + "\", __key)");
+ }
+ else
+ {
+ out << nl << typeS << ' ' << arg << ';';
+ writeMarshalUnmarshalCode(out, package, type, arg, false, iter, false);
+ }
+ }
+ BuiltinPtr builtin = BuiltinPtr::dynamicCast(value);
+ if(!(builtin && builtin->kind() == Builtin::KindObject) && !ClassDeclPtr::dynamicCast(value))
+ {
+ out << nl << "" << v << ".put(__key, __value);";
+ }
+ out << eb;
+ }
+}
+
+void
+Slice::JavaGenerator::writeSequenceMarshalUnmarshalCode(Output& out,
+ const string& package,
+ const SequencePtr& seq,
+ const string& param,
+ bool marshal,
+ int& iter,
+ bool useHelper,
+ const StringList& metaData)
+{
+ string stream = marshal ? "__os" : "__is";
+ string v = param;
+
+ //
+ // If the sequence is a byte sequence, check if there's the serializable or protobuf metadata to
+ // get rid of these two easy cases first.
+ //
+ BuiltinPtr builtin = BuiltinPtr::dynamicCast(seq->type());
+ if(builtin && builtin->kind() == Builtin::KindByte)
+ {
+ string meta;
+ static const string protobuf = "java:protobuf:";
+ static const string serializable = "java:serializable:";
+ if(seq->findMetaData(serializable, meta))
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeSerializable(" << v << ");";
+ }
+ else
+ {
+ string type = typeToString(seq, TypeModeIn, package);
+ out << nl << v << " = (" << type << ")" << stream << ".readSerializable();";
+ }
+ return;
+ }
+ else if(seq->findMetaData(protobuf, meta))
+ {
+ if(marshal)
+ {
+ out << nl << "if(!" << v << ".isInitialized())";
+ out << sb;
+ out << nl << "throw new Ice.MarshalException(\"type not fully initialized\");";
+ out << eb;
+ out << nl << stream << ".writeByteSeq(" << v << ".toByteArray());";
+ }
+ else
+ {
+ string type = typeToString(seq, TypeModeIn, package);
+ out << nl << "try";
+ out << sb;
+ out << nl << v << " = " << type << ".parseFrom(" << stream << ".readByteSeq());";
+ out << eb;
+ out << nl << "catch(com.google.protobuf.InvalidProtocolBufferException __ex)";
+ out << sb;
+ out << nl << "Ice.MarshalException __mex = new Ice.MarshalException();";
+ out << nl << "__mex.initCause(__ex);";
+ out << nl << "throw __mex;";
+ out << eb;
+ }
+ return;
+ }
+ }
+
+ bool customType = false;
+ string instanceType;
+
+ //
+ // We have to determine whether it's possible to use the
+ // type's generated helper class for this marshal/unmarshal
+ // task. Since the user may have specified a custom type in
+ // metadata, it's possible that the helper class is not
+ // compatible and therefore we'll need to generate the code
+ // in-line instead.
+ //
+ // Specifically, there may be "local" metadata (i.e., from
+ // a data member or parameter definition) that overrides the
+ // original type. We'll compare the mapped types with and
+ // without local metadata to determine whether we can use
+ // the helper.
+ //
+ string formalType;
+ customType = getSequenceTypes(seq, "", metaData, instanceType, formalType);
+ string origInstanceType, origFormalType;
+ getSequenceTypes(seq, "", StringList(), origInstanceType, origFormalType);
+ if((formalType != origFormalType) || (!marshal && instanceType != origInstanceType))
+ {
+ useHelper = false;
+ }
+
+ //
+ // If we can use the helper, it's easy.
+ //
+ if(useHelper)
+ {
+ string typeS = getAbsolute(seq, package);
+ if(marshal)
+ {
+ out << nl << typeS << "Helper.write(" << stream << ", " << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << typeS << "Helper.read(" << stream << ");";
+ }
+ return;
+ }
+
+ //
+ // Determine sequence depth.
+ //
+ int depth = 0;
+ TypePtr origContent = seq->type();
+ SequencePtr s = SequencePtr::dynamicCast(origContent);
+ while(s)
+ {
+ //
+ // Stop if the inner sequence type has a custom, serializable or protobuf type.
+ //
+ if(hasTypeMetaData(s))
+ {
+ break;
+ }
+ depth++;
+ origContent = s->type();
+ s = SequencePtr::dynamicCast(origContent);
+ }
+ string origContentS = typeToString(origContent, TypeModeIn, package);
+
+ TypePtr type = seq->type();
+
+ if(customType)
+ {
+ //
+ // Marshal/unmarshal a custom sequence type.
+ //
+ BuiltinPtr b = BuiltinPtr::dynamicCast(type);
+ string typeS = getAbsolute(seq, package);
+ ostringstream o;
+ o << origContentS;
+ int d = depth;
+ while(d--)
+ {
+ o << "[]";
+ }
+ string cont = o.str();
+ if(marshal)
+ {
+ out << nl << "if(" << v << " == null)";
+ out << sb;
+ out << nl << stream << ".writeSize(0);";
+ out << eb;
+ out << nl << "else";
+ out << sb;
+ out << nl << stream << ".writeSize(" << v << ".size());";
+ string typeS = typeToString(type, TypeModeIn, package);
+ out << nl << "for(" << typeS << " __elem : " << v << ')';
+ out << sb;
+ writeMarshalUnmarshalCode(out, package, type, "__elem", true, iter, false);
+ out << eb;
+ out << eb; // else
+ }
+ else
+ {
+ bool isObject = false;
+ ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type);
+ if((b && b->kind() == Builtin::KindObject) || cl)
+ {
+ isObject = true;
+ }
+ out << nl << v << " = new " << instanceType << "();";
+ out << nl << "final int __len" << iter << " = " << stream << ".readAndCheckSeqSize(" << type->minWireSize()
+ << ");";
+ if(isObject)
+ {
+ if(b)
+ {
+ out << nl << "final String __type" << iter << " = Ice.ObjectImpl.ice_staticId();";
+ }
+ else
+ {
+ assert(cl);
+ if(cl->isInterface())
+ {
+ out << nl << "final String __type" << iter << " = "
+ << getAbsolute(cl, package, "_", "Disp") << ".ice_staticId();";
+ }
+ else
+ {
+ out << nl << "final String __type" << iter << " = " << origContentS << ".ice_staticId();";
+ }
+ }
+ }
+ out << nl << "for(int __i" << iter << " = 0; __i" << iter << " < __len" << iter << "; __i" << iter
+ << "++)";
+ out << sb;
+ if(isObject)
+ {
+ //
+ // Add a null value to the list as a placeholder for the element.
+ //
+ out << nl << v << ".add(null);";
+ ostringstream patchParams;
+ patchParams << "new IceInternal.ListPatcher<" << origContentS << ">(" << v << ", " << origContentS
+ << ".class, __type" << iter << ", __i" << iter << ')';
+ writeMarshalUnmarshalCode(out, package, type, "__elem", false, iter, false, StringList(),
+ patchParams.str());
+ }
+ else
+ {
+ out << nl << cont << " __elem;";
+ writeMarshalUnmarshalCode(out, package, type, "__elem", false, iter, false);
+ }
+ if(!isObject)
+ {
+ out << nl << v << ".add(__elem);";
+ }
+ out << eb;
+ iter++;
+ }
+ }
+ else
+ {
+ BuiltinPtr b = BuiltinPtr::dynamicCast(type);
+ if(b && b->kind() != Builtin::KindObject && b->kind() != Builtin::KindObjectProxy)
+ {
+ switch(b->kind())
+ {
+ case Builtin::KindByte:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeByteSeq(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readByteSeq();";
+ }
+ break;
+ }
+ case Builtin::KindBool:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeBoolSeq(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readBoolSeq();";
+ }
+ break;
+ }
+ case Builtin::KindShort:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeShortSeq(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readShortSeq();";
+ }
+ break;
+ }
+ case Builtin::KindInt:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeIntSeq(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readIntSeq();";
+ }
+ break;
+ }
+ case Builtin::KindLong:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeLongSeq(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readLongSeq();";
+ }
+ break;
+ }
+ case Builtin::KindFloat:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeFloatSeq(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readFloatSeq();";
+ }
+ break;
+ }
+ case Builtin::KindDouble:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeDoubleSeq(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readDoubleSeq();";
+ }
+ break;
+ }
+ case Builtin::KindString:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeStringSeq(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readStringSeq();";
+ }
+ break;
+ }
+ case Builtin::KindObject:
+ case Builtin::KindObjectProxy:
+ case Builtin::KindLocalObject:
+ {
+ assert(false);
+ break;
+ }
+ }
+ }
+ else
+ {
+ if(marshal)
+ {
+ out << nl << "if(" << v << " == null)";
+ out << sb;
+ out << nl << stream << ".writeSize(0);";
+ out << eb;
+ out << nl << "else";
+ out << sb;
+ out << nl << stream << ".writeSize(" << v << ".length);";
+ out << nl << "for(int __i" << iter << " = 0; __i" << iter << " < " << v << ".length; __i" << iter
+ << "++)";
+ out << sb;
+ ostringstream o;
+ o << v << "[__i" << iter << "]";
+ iter++;
+ writeMarshalUnmarshalCode(out, package, type, o.str(), true, iter, false);
+ out << eb;
+ out << eb;
+ }
+ else
+ {
+ bool isObject = false;
+ ClassDeclPtr cl = ClassDeclPtr::dynamicCast(origContent);
+ if((b && b->kind() == Builtin::KindObject) || cl)
+ {
+ isObject = true;
+ }
+ out << nl << "final int __len" << iter << " = " << stream << ".readAndCheckSeqSize("
+ << type->minWireSize() << ");";
+ if(isObject)
+ {
+ if(b)
+ {
+ out << nl << "final String __type" << iter << " = Ice.ObjectImpl.ice_staticId();";
+ }
+ else
+ {
+ assert(cl);
+ if(cl->isInterface())
+ {
+ out << nl << "final String __type" << iter << " = "
+ << getAbsolute(cl, package, "_", "Disp") << ".ice_staticId();";
+ }
+ else
+ {
+ out << nl << "final String __type" << iter << " = " << origContentS << ".ice_staticId();";
+ }
+ }
+ }
+ //
+ // We cannot allocate an array of a generic type, such as
+ //
+ // arr = new Map<String, String>[sz];
+ //
+ // Attempting to compile this code results in a "generic array creation" error
+ // message. This problem can occur when the sequence's element type is a
+ // dictionary, or when the element type is a nested sequence that uses a custom
+ // mapping.
+ //
+ // The solution is to rewrite the code as follows:
+ //
+ // arr = (Map<String, String>[])new Map[sz];
+ //
+ // Unfortunately, this produces an unchecked warning during compilation.
+ //
+ // A simple test is to look for a "<" character in the content type, which
+ // indicates the use of a generic type.
+ //
+ string::size_type pos = origContentS.find('<');
+ if(pos != string::npos)
+ {
+ string nonGenericType = origContentS.substr(0, pos);
+ out << nl << v << " = (" << origContentS << "[]";
+ int d = depth;
+ while(d--)
+ {
+ out << "[]";
+ }
+ out << ")new " << nonGenericType << "[__len" << iter << "]";
+ }
+ else
+ {
+ out << nl << v << " = new " << origContentS << "[__len" << iter << "]";
+ }
+ int d = depth;
+ while(d--)
+ {
+ out << "[]";
+ }
+ out << ';';
+ out << nl << "for(int __i" << iter << " = 0; __i" << iter << " < __len" << iter << "; __i" << iter
+ << "++)";
+ out << sb;
+ ostringstream o;
+ o << v << "[__i" << iter << "]";
+ ostringstream patchParams;
+ if(isObject)
+ {
+ patchParams << "new IceInternal.SequencePatcher(" << v << ", " << origContentS
+ << ".class, __type" << iter << ", __i" << iter << ')';
+ writeMarshalUnmarshalCode(out, package, type, o.str(), false, iter, false, StringList(),
+ patchParams.str());
+ }
+ else
+ {
+ writeMarshalUnmarshalCode(out, package, type, o.str(), false, iter, false);
+ }
+ out << eb;
+ iter++;
+ }
+ }
+ }
+}
+
+void
+Slice::JavaGenerator::writeStreamMarshalUnmarshalCode(Output& out,
+ const string& package,
+ const TypePtr& type,
+ const string& param,
+ bool marshal,
+ int& iter,
+ bool holder,
+ const StringList& metaData,
+ const string& patchParams)
+{
+ string stream = marshal ? "__outS" : "__inS";
+ string v;
+ if(holder)
+ {
+ v = param + ".value";
+ }
+ else
+ {
+ v = param;
+ }
+
+ BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
+ if(builtin)
+ {
+ switch(builtin->kind())
+ {
+ case Builtin::KindByte:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeByte(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readByte();";
+ }
+ break;
+ }
+ case Builtin::KindBool:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeBool(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readBool();";
+ }
+ break;
+ }
+ case Builtin::KindShort:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeShort(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readShort();";
+ }
+ break;
+ }
+ case Builtin::KindInt:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeInt(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readInt();";
+ }
+ break;
+ }
+ case Builtin::KindLong:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeLong(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readLong();";
+ }
+ break;
+ }
+ case Builtin::KindFloat:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeFloat(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readFloat();";
+ }
+ break;
+ }
+ case Builtin::KindDouble:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeDouble(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readDouble();";
+ }
+ break;
+ }
+ case Builtin::KindString:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeString(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readString();";
+ }
+ break;
+ }
+ case Builtin::KindObject:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeObject(" << v << ");";
+ }
+ else
+ {
+ if(holder)
+ {
+ out << nl << stream << ".readObject(" << param << ");";
+ }
+ else
+ {
+ if(patchParams.empty())
+ {
+ out << nl << stream << ".readObject(new Patcher());";
+ }
+ else
+ {
+ out << nl << stream << ".readObject(" << patchParams << ");";
+ }
+ }
+ }
+ break;
+ }
+ case Builtin::KindObjectProxy:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeProxy(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readProxy();";
+ }
+ break;
+ }
+ case Builtin::KindLocalObject:
+ {
+ assert(false);
+ break;
+ }
+ }
+ return;
+ }
+
+ ProxyPtr prx = ProxyPtr::dynamicCast(type);
+ if(prx)
+ {
+ string typeS = typeToString(type, TypeModeIn, package);
+ if(marshal)
+ {
+ out << nl << typeS << "Helper.write(" << stream << ", " << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << typeS << "Helper.read(" << stream << ");";
+ }
+ return;
+ }
+
+ ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type);
+ if(cl)
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeObject(" << v << ");";
+ }
+ else
+ {
+ string typeS = typeToString(type, TypeModeIn, package);
+ if(holder)
+ {
+ out << nl << stream << ".readObject(" << param << ");";
+ }
+ else
+ {
+ if(patchParams.empty())
+ {
+ out << nl << stream << ".readObject(new Patcher());";
+ }
+ else
+ {
+ out << nl << stream << ".readObject(" << patchParams << ");";
+ }
+ }
+ }
+ return;
+ }
+
+ StructPtr st = StructPtr::dynamicCast(type);
+ if(st)
+ {
+ if(marshal)
+ {
+ out << nl << v << ".ice_write(" << stream << ");";
+ }
+ else
+ {
+ string typeS = typeToString(type, TypeModeIn, package);
+ out << nl << v << " = new " << typeS << "();";
+ out << nl << v << ".ice_read(" << stream << ");";
+ }
+ return;
+ }
+
+ EnumPtr en = EnumPtr::dynamicCast(type);
+ if(en)
+ {
+ if(marshal)
+ {
+ out << nl << v << ".ice_write(" << stream << ");";
+ }
+ else
+ {
+ string typeS = typeToString(type, TypeModeIn, package);
+ out << nl << v << " = " << typeS << ".ice_read(" << stream << ");";
+ }
+ return;
+ }
+
+ DictionaryPtr dict = DictionaryPtr::dynamicCast(type);
+ if(dict)
+ {
+ writeStreamDictionaryMarshalUnmarshalCode(out, package, dict, v, marshal, iter, true, metaData);
+ return;
+ }
+
+ SequencePtr seq = SequencePtr::dynamicCast(type);
+ if(seq)
+ {
+ writeStreamSequenceMarshalUnmarshalCode(out, package, seq, v, marshal, iter, true, metaData);
+ return;
+ }
+
+ ConstructedPtr constructed = ConstructedPtr::dynamicCast(type);
+ assert(constructed);
+ string typeS = getAbsolute(constructed, package);
+ if(marshal)
+ {
+ out << nl << typeS << "Helper.write(" << stream << ", " << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << typeS << "Helper.read(" << stream << ");";
+ }
+}
+
+void
+Slice::JavaGenerator::writeStreamDictionaryMarshalUnmarshalCode(Output& out,
+ const string& package,
+ const DictionaryPtr& dict,
+ const string& param,
+ bool marshal,
+ int& iter,
+ bool useHelper,
+ const StringList& metaData)
+{
+ string stream = marshal ? "__outS" : "__inS";
+ string v = param;
+
+ //
+ // We have to determine whether it's possible to use the
+ // type's generated helper class for this marshal/unmarshal
+ // task. Since the user may have specified a custom type in
+ // metadata, it's possible that the helper class is not
+ // compatible and therefore we'll need to generate the code
+ // in-line instead.
+ //
+ // Specifically, there may be "local" metadata (i.e., from
+ // a data member or parameter definition) that overrides the
+ // original type. We'll compare the mapped types with and
+ // without local metadata to determine whether we can use
+ // the helper.
+ //
+ string instanceType, formalType;
+ getDictionaryTypes(dict, "", metaData, instanceType, formalType);
+ string origInstanceType, origFormalType;
+ getDictionaryTypes(dict, "", StringList(), origInstanceType, origFormalType);
+ if((formalType != origFormalType) || (!marshal && instanceType != origInstanceType))
+ {
+ useHelper = false;
+ }
+
+ //
+ // If we can use the helper, it's easy.
+ //
+ if(useHelper)
+ {
+ string typeS = getAbsolute(dict, package);
+ if(marshal)
+ {
+ out << nl << typeS << "Helper.write(" << stream << ", " << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << typeS << "Helper.read(" << stream << ");";
+ }
+ return;
+ }
+
+ TypePtr key = dict->keyType();
+ TypePtr value = dict->valueType();
+
+ string keyS = typeToString(key, TypeModeIn, package);
+ string valueS = typeToString(value, TypeModeIn, package);
+ int i;
+
+ ostringstream o;
+ o << iter;
+ string iterS = o.str();
+ iter++;
+
+ if(marshal)
+ {
+ out << nl << "if(" << v << " == null)";
+ out << sb;
+ out << nl << "__outS.writeSize(0);";
+ out << eb;
+ out << nl << "else";
+ out << sb;
+ out << nl << "__outS.writeSize(" << v << ".size());";
+ string keyObjectS = typeToObjectString(key, TypeModeIn, package);
+ string valueObjectS = typeToObjectString(value, TypeModeIn, package);
+ out << nl << "for(java.util.Map.Entry<" << keyObjectS << ", " << valueObjectS << "> __e : " << v
+ << ".entrySet())";
+ out << sb;
+ for(i = 0; i < 2; i++)
+ {
+ string arg;
+ TypePtr type;
+ if(i == 0)
+ {
+ arg = "__e.getKey()";
+ type = key;
+ }
+ else
+ {
+ arg = "__e.getValue()";
+ type = value;
+ }
+ writeStreamMarshalUnmarshalCode(out, package, type, arg, true, iter, false);
+ }
+ out << eb;
+ out << eb;
+ }
+ else
+ {
+ out << nl << v << " = new " << instanceType << "();";
+ out << nl << "int __sz" << iterS << " = __inS.readSize();";
+ out << nl << "for(int __i" << iterS << " = 0; __i" << iterS << " < __sz" << iterS << "; __i" << iterS << "++)";
+ out << sb;
+ for(i = 0; i < 2; i++)
+ {
+ string arg;
+ TypePtr type;
+ string typeS;
+ if(i == 0)
+ {
+ arg = "__key";
+ type = key;
+ typeS = keyS;
+ }
+ else
+ {
+ arg = "__value";
+ type = value;
+ typeS = valueS;
+ }
+
+ BuiltinPtr b = BuiltinPtr::dynamicCast(type);
+ string s = typeToString(type, TypeModeIn, package);
+ if(ClassDeclPtr::dynamicCast(type) || (b && b->kind() == Builtin::KindObject))
+ {
+ string keyTypeStr = typeToObjectString(key, TypeModeIn, package);
+ string valueTypeStr = typeToObjectString(value, TypeModeIn, package);
+ writeStreamMarshalUnmarshalCode(out, package, type, arg, false, iter, false, StringList(),
+ "new IceInternal.DictionaryPatcher<" + keyTypeStr + ", " +
+ valueTypeStr + ">(" + v + ", " + s + ".class, \"" +
+ type->typeId() + "\", __key)");
+ }
+ else
+ {
+ out << nl << s << ' ' << arg << ';';
+ writeStreamMarshalUnmarshalCode(out, package, type, arg, false, iter, false);
+ }
+ }
+ BuiltinPtr builtin = BuiltinPtr::dynamicCast(value);
+ if(!(builtin && builtin->kind() == Builtin::KindObject) && !ClassDeclPtr::dynamicCast(value))
+ {
+ out << nl << "" << v << ".put(__key, __value);";
+ }
+ out << eb;
+ }
+}
+
+void
+Slice::JavaGenerator::writeStreamSequenceMarshalUnmarshalCode(Output& out,
+ const string& package,
+ const SequencePtr& seq,
+ const string& param,
+ bool marshal,
+ int& iter,
+ bool useHelper,
+ const StringList& metaData)
+{
+ string stream = marshal ? "__outS" : "__inS";
+ string v = param;
+
+ //
+ // If the sequence is a byte sequence, check if there's the serializable or protobuf metadata to
+ // get rid of these two easy cases first.
+ //
+ BuiltinPtr builtin = BuiltinPtr::dynamicCast(seq->type());
+ if(builtin && builtin->kind() == Builtin::KindByte)
+ {
+ string meta;
+ static const string protobuf = "java:protobuf:";
+ static const string serializable = "java:serializable:";
+ if(seq->findMetaData(serializable, meta))
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeSerializable(" << v << ");";
+ }
+ else
+ {
+ string type = typeToString(seq, TypeModeIn, package);
+ out << nl << v << " = (" << type << ")" << stream << ".readSerializable();";
+ }
+ return;
+ }
+ else if(seq->findMetaData(protobuf, meta))
+ {
+ if(marshal)
+ {
+ out << nl << "if(!" << v << ".isInitialized())";
+ out << sb;
+ out << nl << "throw new Ice.MarshalException(\"type not fully initialized\");";
+ out << eb;
+ out << nl << stream << ".writeByteSeq(" << v << ".toByteArray());";
+ }
+ else
+ {
+ string type = meta.substr(protobuf.size());
+ out << nl << "try";
+ out << sb;
+ out << nl << v << " = " << type << ".parseFrom(" << stream << ".readByteSeq());";
+ out << eb;
+ out << nl << "catch(com.google.protobuf.InvalidProtocolBufferException __ex)";
+ out << sb;
+ out << nl << "Ice.MarshalException __mex = new Ice.MarshalException();";
+ out << nl << "__mex.initCause(__ex);";
+ out << nl << "throw __mex;";
+ out << eb;
+ }
+ return;
+ }
+ }
+
+ //
+ // We have to determine whether it's possible to use the
+ // type's generated helper class for this marshal/unmarshal
+ // task. Since the user may have specified a custom type in
+ // metadata, it's possible that the helper class is not
+ // compatible and therefore we'll need to generate the code
+ // in-line instead.
+ //
+ // Specifically, there may be "local" metadata (i.e., from
+ // a data member or parameter definition) that overrides the
+ // original type. We'll compare the mapped types with and
+ // without local metadata to determine whether we can use
+ // the helper.
+ //
+ string instanceType, formalType;
+ bool customType = getSequenceTypes(seq, "", metaData, instanceType, formalType);
+ string origInstanceType, origFormalType;
+ getSequenceTypes(seq, "", StringList(), origInstanceType, origFormalType);
+ if((formalType != origFormalType) || (!marshal && instanceType != origInstanceType))
+ {
+ useHelper = false;
+ }
+
+ //
+ // If we can use the helper, it's easy.
+ //
+ if(useHelper)
+ {
+ string typeS = getAbsolute(seq, package);
+ if(marshal)
+ {
+ out << nl << typeS << "Helper.write(" << stream << ", " << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << typeS << "Helper.read(" << stream << ");";
+ }
+ return;
+ }
+
+ //
+ // Determine sequence depth
+ //
+ int depth = 0;
+ TypePtr origContent = seq->type();
+ SequencePtr s = SequencePtr::dynamicCast(origContent);
+ while(s)
+ {
+ //
+ // Stop if the inner sequence type has a custom, serializable or protobuf type.
+ //
+ if(hasTypeMetaData(s))
+ {
+ break;
+ }
+ depth++;
+ origContent = s->type();
+ s = SequencePtr::dynamicCast(origContent);
+ }
+ string origContentS = typeToString(origContent, TypeModeIn, package);
+
+ TypePtr type = seq->type();
+
+ if(customType)
+ {
+ //
+ // Marshal/unmarshal a custom sequence type.
+ //
+ BuiltinPtr b = BuiltinPtr::dynamicCast(type);
+ string typeS = getAbsolute(seq, package);
+ ostringstream o;
+ o << origContentS;
+ int d = depth;
+ while(d--)
+ {
+ o << "[]";
+ }
+ string cont = o.str();
+ if(marshal)
+ {
+ out << nl << "if(" << v << " == null)";
+ out << sb;
+ out << nl << stream << ".writeSize(0);";
+ out << eb;
+ out << nl << "else";
+ out << sb;
+ out << nl << stream << ".writeSize(" << v << ".size());";
+ string typeS = typeToString(type, TypeModeIn, package);
+ out << nl << "for(" << typeS << " __elem : " << v << ')';
+ out << sb;
+ writeStreamMarshalUnmarshalCode(out, package, type, "__elem", true, iter, false);
+ out << eb;
+ out << eb; // else
+ }
+ else
+ {
+ bool isObject = false;
+ ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type);
+ if((b && b->kind() == Builtin::KindObject) || cl)
+ {
+ isObject = true;
+ }
+ out << nl << v << " = new " << instanceType << "();";
+ out << nl << "final int __len" << iter << " = " << stream << ".readAndCheckSeqSize(" << type->minWireSize()
+ << ");";
+ if(isObject)
+ {
+ if(b)
+ {
+ out << nl << "final String __type" << iter << " = Ice.ObjectImpl.ice_staticId();";
+ }
+ else
+ {
+ assert(cl);
+ if(cl->isInterface())
+ {
+ out << nl << "final String __type" << iter << " = "
+ << getAbsolute(cl, package, "_", "Disp") << ".ice_staticId();";
+ }
+ else
+ {
+ out << nl << "final String __type" << iter << " = " << origContentS << ".ice_staticId();";
+ }
+ }
+ }
+ out << nl << "for(int __i" << iter << " = 0; __i" << iter << " < __len" << iter << "; __i" << iter
+ << "++)";
+ out << sb;
+ if(isObject)
+ {
+ //
+ // Add a null value to the list as a placeholder for the element.
+ //
+ out << nl << v << ".add(null);";
+ ostringstream patchParams;
+ patchParams << "new IceInternal.ListPatcher<" << origContentS << ">(" << v << ", " << origContentS
+ << ".class, __type" << iter << ", __i" << iter << ')';
+ writeStreamMarshalUnmarshalCode(out, package, type, "__elem", false, iter, false,
+ StringList(), patchParams.str());
+ }
+ else
+ {
+ out << nl << cont << " __elem;";
+ writeStreamMarshalUnmarshalCode(out, package, type, "__elem", false, iter, false);
+ }
+ if(!isObject)
+ {
+ out << nl << v << ".add(__elem);";
+ }
+ out << eb;
+ iter++;
+ }
+ }
+ else
+ {
+ BuiltinPtr b = BuiltinPtr::dynamicCast(type);
+ if(b && b->kind() != Builtin::KindObject && b->kind() != Builtin::KindObjectProxy)
+ {
+ switch(b->kind())
+ {
+ case Builtin::KindByte:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeByteSeq(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readByteSeq();";
+ }
+ break;
+ }
+ case Builtin::KindBool:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeBoolSeq(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readBoolSeq();";
+ }
+ break;
+ }
+ case Builtin::KindShort:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeShortSeq(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readShortSeq();";
+ }
+ break;
+ }
+ case Builtin::KindInt:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeIntSeq(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readIntSeq();";
+ }
+ break;
+ }
+ case Builtin::KindLong:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeLongSeq(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readLongSeq();";
+ }
+ break;
+ }
+ case Builtin::KindFloat:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeFloatSeq(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readFloatSeq();";
+ }
+ break;
+ }
+ case Builtin::KindDouble:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeDoubleSeq(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readDoubleSeq();";
+ }
+ break;
+ }
+ case Builtin::KindString:
+ {
+ if(marshal)
+ {
+ out << nl << stream << ".writeStringSeq(" << v << ");";
+ }
+ else
+ {
+ out << nl << v << " = " << stream << ".readStringSeq();";
+ }
+ break;
+ }
+ case Builtin::KindObject:
+ case Builtin::KindObjectProxy:
+ case Builtin::KindLocalObject:
+ {
+ assert(false);
+ break;
+ }
+ }
+ }
+ else
+ {
+ if(marshal)
+ {
+ out << nl << "if(" << v << " == null)";
+ out << sb;
+ out << nl << stream << ".writeSize(0);";
+ out << eb;
+ out << nl << "else";
+ out << sb;
+ out << nl << stream << ".writeSize(" << v << ".length);";
+ out << nl << "for(int __i" << iter << " = 0; __i" << iter << " < " << v << ".length; __i" << iter
+ << "++)";
+ out << sb;
+ ostringstream o;
+ o << v << "[__i" << iter << "]";
+ iter++;
+ writeStreamMarshalUnmarshalCode(out, package, type, o.str(), true, iter, false);
+ out << eb;
+ out << eb;
+ }
+ else
+ {
+ bool isObject = false;
+ ClassDeclPtr cl = ClassDeclPtr::dynamicCast(origContent);
+ if((b && b->kind() == Builtin::KindObject) || cl)
+ {
+ isObject = true;
+ }
+ out << nl << "final int __len" << iter << " = " << stream << ".readAndCheckSeqSize("
+ << type->minWireSize() << ");";
+ if(isObject)
+ {
+ if(b)
+ {
+ out << nl << "final String __type" << iter << " = Ice.ObjectImpl.ice_staticId();";
+ }
+ else
+ {
+ assert(cl);
+ if(cl->isInterface())
+ {
+ out << nl << "final String __type" << iter << " = "
+ << getAbsolute(cl, package, "_", "Disp") << ".ice_staticId();";
+ }
+ else
+ {
+ out << nl << "final String __type" << iter << " = " << origContentS << ".ice_staticId();";
+ }
+ }
+ }
+ //
+ // We cannot allocate an array of a generic type, such as
+ //
+ // arr = new Map<String, String>[sz];
+ //
+ // Attempting to compile this code results in a "generic array creation" error
+ // message. This problem can occur when the sequence's element type is a
+ // dictionary, or when the element type is a nested sequence that uses a custom
+ // mapping.
+ //
+ // The solution is to rewrite the code as follows:
+ //
+ // arr = (Map<String, String>[])new Map[sz];
+ //
+ // Unfortunately, this produces an unchecked warning during compilation.
+ //
+ // A simple test is to look for a "<" character in the content type, which
+ // indicates the use of a generic type.
+ //
+ string::size_type pos = origContentS.find('<');
+ if(pos != string::npos)
+ {
+ string nonGenericType = origContentS.substr(0, pos);
+ out << nl << v << " = (" << origContentS << "[]";
+ int d = depth;
+ while(d--)
+ {
+ out << "[]";
+ }
+ out << ")new " << nonGenericType << "[__len" << iter << "]";
+ }
+ else
+ {
+ out << nl << v << " = new " << origContentS << "[__len" << iter << "]";
+ }
+ int d = depth;
+ while(d--)
+ {
+ out << "[]";
+ }
+ out << ';';
+ out << nl << "for(int __i" << iter << " = 0; __i" << iter << " < __len" << iter << "; __i" << iter
+ << "++)";
+ out << sb;
+ ostringstream o;
+ o << v << "[__i" << iter << "]";
+ ostringstream patchParams;
+ if(isObject)
+ {
+ patchParams << "new IceInternal.SequencePatcher(" << v << ", " << origContentS
+ << ".class, __type" << iter << ", __i" << iter << ')';
+ writeStreamMarshalUnmarshalCode(out, package, type, o.str(), false, iter, false,
+ StringList(), patchParams.str());
+ }
+ else
+ {
+ writeStreamMarshalUnmarshalCode(out, package, type, o.str(), false, iter, false);
+ }
+ out << eb;
+ iter++;
+ }
+ }
+ }
+}
+
+bool
+Slice::JavaGenerator::getTypeMetaData(const StringList& metaData, string& instanceType, string& formalType)
+{
+ //
+ // Extract the instance type and an optional formal type.
+ // The correct syntax is "java:type:instance-type[:formal-type]".
+ //
+ static const string prefix = "java:type:";
+ for(StringList::const_iterator q = metaData.begin(); q != metaData.end(); ++q)
+ {
+ string str = *q;
+ if(str.find(prefix) == 0)
+ {
+ string::size_type pos = str.find(':', prefix.size());
+ if(pos != string::npos)
+ {
+ instanceType = str.substr(prefix.size(), pos - prefix.size());
+ formalType = str.substr(pos + 1);
+ }
+ else
+ {
+ instanceType = str.substr(prefix.size());
+ formalType.clear();
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+Slice::JavaGenerator::hasTypeMetaData(const TypePtr& type, const StringList& localMetaData)
+{
+ ContainedPtr cont = ContainedPtr::dynamicCast(type);
+ if(cont)
+ {
+ static const string prefix = "java:type:";
+
+ StringList::const_iterator q;
+ for(q = localMetaData.begin(); q != localMetaData.end(); ++q)
+ {
+ string str = *q;
+ if(str.find(prefix) == 0)
+ {
+ return true;
+ }
+ }
+
+ StringList metaData = cont->getMetaData();
+ for(q = metaData.begin(); q != metaData.end(); ++q)
+ {
+ string str = *q;
+ if(str.find(prefix) == 0)
+ {
+ return true;
+ }
+ else if(str.find("java:protobuf:") == 0 || str.find("java:serializable:") == 0)
+ {
+ SequencePtr seq = SequencePtr::dynamicCast(cont);
+ if(seq)
+ {
+ BuiltinPtr builtin = BuiltinPtr::dynamicCast(seq->type());
+ if(builtin && builtin->kind() == Builtin::KindByte)
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool
+Slice::JavaGenerator::getDictionaryTypes(const DictionaryPtr& dict,
+ const string& package,
+ const StringList& metaData,
+ string& instanceType,
+ string& formalType) const
+{
+ bool customType = false;
+
+ //
+ // Collect metadata for a custom type.
+ //
+ string ct, at;
+ customType = getTypeMetaData(metaData, ct, at);
+ if(!customType)
+ {
+ customType = getTypeMetaData(dict->getMetaData(), ct, at);
+ }
+
+ //
+ // Get the types of the key and value.
+ //
+ string keyTypeStr = typeToObjectString(dict->keyType(), TypeModeIn, package);
+ string valueTypeStr = typeToObjectString(dict->valueType(), TypeModeIn, package);
+
+ //
+ // Handle a custom type.
+ //
+ if(customType)
+ {
+ assert(!ct.empty());
+ instanceType = ct;
+ formalType = at;
+ }
+
+ //
+ // Return a default type for the platform.
+ //
+ if(instanceType.empty())
+ {
+ instanceType = "java.util.HashMap<" + keyTypeStr + ", " + valueTypeStr + ">";
+ }
+
+ //
+ // If a formal type is not defined, we use the instance type as the default.
+ // If instead we chose a default formal type, such as Map<K, V>, then we
+ // might inadvertently generate uncompilable code. The Java5 compiler does not
+ // allow polymorphic assignment between generic types if it can weaken the
+ // compile-time type safety rules.
+ //
+ if(formalType.empty())
+ {
+ formalType = "java.util.Map<" + keyTypeStr + ", " + valueTypeStr + ">";
+ }
+
+ return customType;
+}
+
+bool
+Slice::JavaGenerator::getSequenceTypes(const SequencePtr& seq,
+ const string& package,
+ const StringList& metaData,
+ string& instanceType,
+ string& formalType) const
+{
+ bool customType = false;
+
+ //
+ // Collect metadata for a custom type.
+ //
+ string ct, at;
+ customType = getTypeMetaData(metaData, ct, at);
+ if(!customType)
+ {
+ customType = getTypeMetaData(seq->getMetaData(), ct, at);
+ }
+
+ //
+ // Get the inner type.
+ //
+ string typeStr = typeToObjectString(seq->type(), TypeModeIn, package);
+
+ //
+ // Handle a custom type.
+ //
+ if(customType)
+ {
+ assert(!ct.empty());
+ instanceType = ct;
+
+ if(!at.empty())
+ {
+ formalType = at;
+ }
+ else
+ {
+ //
+ // If a formal type is not defined, we use the instance type as the default.
+ // If instead we chose a default formal type, such as List<T>, then we
+ // might inadvertently generate uncompilable code. The Java5 compiler does not
+ // allow polymorphic assignment between generic types if it can weaken the
+ // compile-time type safety rules.
+ //
+ formalType = "java.util.List<" + typeStr + ">";
+ }
+ }
+
+ //
+ // The default mapping is a native array.
+ //
+ if(instanceType.empty())
+ {
+ instanceType = formalType = typeToString(seq->type(), TypeModeIn, package) + "[]";
+ }
+
+ return customType;
+}
+
+JavaOutput*
+Slice::JavaGenerator::createOutput()
+{
+ return new JavaOutput;
+}
+
+void
+Slice::JavaGenerator::validateMetaData(const UnitPtr& u)
+{
+ MetaDataVisitor visitor;
+ u->visit(&visitor, true);
+}
+
+bool
+Slice::JavaGenerator::MetaDataVisitor::visitUnitStart(const UnitPtr& p)
+{
+ static const string prefix = "java:";
+
+ //
+ // Validate global metadata in the top-level file and all included files.
+ //
+ StringList files = p->allFiles();
+
+ for(StringList::iterator q = files.begin(); q != files.end(); ++q)
+ {
+ string file = *q;
+ DefinitionContextPtr dc = p->findDefinitionContext(file);
+ assert(dc);
+ StringList globalMetaData = dc->getMetaData();
+ for(StringList::const_iterator r = globalMetaData.begin(); r != globalMetaData.end(); ++r)
+ {
+ string s = *r;
+ if(_history.count(s) == 0)
+ {
+ if(s.find(prefix) == 0)
+ {
+ bool ok = false;
+
+ static const string packagePrefix = "java:package:";
+ if(s.find(packagePrefix) == 0 && s.size() > packagePrefix.size())
+ {
+ ok = true;
+ }
+
+ if(!ok)
+ {
+ emitWarning(file, "", "ignoring invalid global metadata `" + s + "'");
+ }
+ }
+ _history.insert(s);
+ }
+ }
+ }
+ return true;
+}
+
+bool
+Slice::JavaGenerator::MetaDataVisitor::visitModuleStart(const ModulePtr& p)
+{
+ StringList metaData = getMetaData(p);
+ validateType(p, metaData, p->file(), p->line());
+ validateGetSet(p, metaData, p->file(), p->line());
+ return true;
+}
+
+void
+Slice::JavaGenerator::MetaDataVisitor::visitClassDecl(const ClassDeclPtr& p)
+{
+ StringList metaData = getMetaData(p);
+ validateType(p, metaData, p->file(), p->line());
+ validateGetSet(p, metaData, p->file(), p->line());
+}
+
+bool
+Slice::JavaGenerator::MetaDataVisitor::visitClassDefStart(const ClassDefPtr& p)
+{
+ StringList metaData = getMetaData(p);
+ validateType(p, metaData, p->file(), p->line());
+ validateGetSet(p, metaData, p->file(), p->line());
+ return true;
+}
+
+bool
+Slice::JavaGenerator::MetaDataVisitor::visitExceptionStart(const ExceptionPtr& p)
+{
+ StringList metaData = getMetaData(p);
+ validateType(p, metaData, p->file(), p->line());
+ validateGetSet(p, metaData, p->file(), p->line());
+ return true;
+}
+
+bool
+Slice::JavaGenerator::MetaDataVisitor::visitStructStart(const StructPtr& p)
+{
+ StringList metaData = getMetaData(p);
+ validateType(p, metaData, p->file(), p->line());
+ validateGetSet(p, metaData, p->file(), p->line());
+ return true;
+}
+
+void
+Slice::JavaGenerator::MetaDataVisitor::visitOperation(const OperationPtr& p)
+{
+ if(p->hasMetaData("UserException"))
+ {
+ ClassDefPtr cl = ClassDefPtr::dynamicCast(p->container());
+ if(!cl->isLocal())
+ {
+ ostringstream os;
+ os << "ignoring invalid metadata `UserException': directive applies only to local operations "
+ << "but enclosing " << (cl->isInterface() ? "interface" : "class") << " `" << cl->name()
+ << "' is not local";
+ emitWarning(p->file(), p->line(), os.str());
+ }
+ }
+ StringList metaData = getMetaData(p);
+ TypePtr returnType = p->returnType();
+ if(!metaData.empty())
+ {
+ if(!returnType)
+ {
+ for(StringList::const_iterator q = metaData.begin(); q != metaData.end(); ++q)
+ {
+ if(q->find("java:type:", 0) == 0)
+ {
+ emitWarning(p->file(), p->line(), "ignoring invalid metadata `" + *q +
+ "' for operation with void return type");
+ break;
+ }
+ }
+ }
+ else
+ {
+ validateType(returnType, metaData, p->file(), p->line());
+ }
+ }
+
+ ParamDeclList params = p->parameters();
+ for(ParamDeclList::iterator q = params.begin(); q != params.end(); ++q)
+ {
+ metaData = getMetaData(*q);
+ validateType((*q)->type(), metaData, p->file(), (*q)->line());
+ }
+
+ validateGetSet(p, metaData, p->file(), p->line());
+}
+
+void
+Slice::JavaGenerator::MetaDataVisitor::visitDataMember(const DataMemberPtr& p)
+{
+ StringList metaData = getMetaData(p);
+ validateType(p->type(), metaData, p->file(), p->line());
+ validateGetSet(p, metaData, p->file(), p->line());
+}
+
+void
+Slice::JavaGenerator::MetaDataVisitor::visitSequence(const SequencePtr& p)
+{
+ static const string protobuf = "java:protobuf:";
+ static const string serializable = "java:serializable:";
+ StringList metaData = getMetaData(p);
+ const string file = p->file();
+ const string line = p->line();
+ for(StringList::const_iterator q = metaData.begin(); q != metaData.end(); )
+ {
+ string s = *q++;
+ if(_history.count(s) == 0) // Don't complain about the same metadata more than once.
+ {
+ if(s.find(protobuf) == 0 || s.find(serializable) == 0)
+ {
+ //
+ // Remove from list so validateType does not try to handle as well.
+ //
+ metaData.remove(s);
+
+ BuiltinPtr builtin = BuiltinPtr::dynamicCast(p->type());
+ if(!builtin || builtin->kind() != Builtin::KindByte)
+ {
+ _history.insert(s);
+ emitWarning(file, line, "ignoring invalid metadata `" + s + "': " +
+ "this metadata can only be used with a byte sequence");
+ }
+ }
+ }
+ }
+
+ validateType(p, metaData, file, line);
+ validateGetSet(p, metaData, file, line);
+}
+
+void
+Slice::JavaGenerator::MetaDataVisitor::visitDictionary(const DictionaryPtr& p)
+{
+ StringList metaData = getMetaData(p);
+ validateType(p, metaData, p->file(), p->line());
+ validateGetSet(p, metaData, p->file(), p->line());
+}
+
+void
+Slice::JavaGenerator::MetaDataVisitor::visitEnum(const EnumPtr& p)
+{
+ StringList metaData = getMetaData(p);
+ validateType(p, metaData, p->file(), p->line());
+ validateGetSet(p, metaData, p->file(), p->line());
+}
+
+void
+Slice::JavaGenerator::MetaDataVisitor::visitConst(const ConstPtr& p)
+{
+ StringList metaData = getMetaData(p);
+ validateType(p, metaData, p->file(), p->line());
+ validateGetSet(p, metaData, p->file(), p->line());
+}
+
+StringList
+Slice::JavaGenerator::MetaDataVisitor::getMetaData(const ContainedPtr& cont)
+{
+ static const string prefix = "java:";
+
+ StringList metaData = cont->getMetaData();
+ StringList result;
+
+ for(StringList::const_iterator p = metaData.begin(); p != metaData.end(); ++p)
+ {
+ string s = *p;
+ if(_history.count(s) == 0) // Don't complain about the same metadata more than once.
+ {
+ if(s.find(prefix) == 0)
+ {
+ string::size_type pos = s.find(':', prefix.size());
+ if(pos == string::npos)
+ {
+ if(s.size() > prefix.size())
+ {
+ string rest = s.substr(prefix.size());
+ if(rest == "getset")
+ {
+ result.push_back(s);
+ }
+ continue;
+ }
+ }
+ else if(s.substr(prefix.size(), pos - prefix.size()) == "type")
+ {
+ result.push_back(s);
+ continue;
+ }
+ else if(s.substr(prefix.size(), pos - prefix.size()) == "serializable")
+ {
+ result.push_back(s);
+ continue;
+ }
+ else if(s.substr(prefix.size(), pos - prefix.size()) == "protobuf")
+ {
+ result.push_back(s);
+ continue;
+ }
+
+ emitWarning(cont->file(), cont->line(), "ignoring invalid metadata `" + s + "'");
+ }
+
+ _history.insert(s);
+ }
+ }
+
+ return result;
+}
+
+void
+Slice::JavaGenerator::MetaDataVisitor::validateType(const SyntaxTreeBasePtr& p, const StringList& metaData,
+ const string& file, const string& line)
+{
+ for(StringList::const_iterator i = metaData.begin(); i != metaData.end(); ++i)
+ {
+ //
+ // Type metadata ("java:type:Foo") is only supported by sequences and dictionaries.
+ //
+ if(i->find("java:type:", 0) == 0 && (!SequencePtr::dynamicCast(p) && !DictionaryPtr::dynamicCast(p)))
+ {
+ string str;
+ ContainedPtr cont = ContainedPtr::dynamicCast(p);
+ if(cont)
+ {
+ str = cont->kindOf();
+ }
+ else
+ {
+ BuiltinPtr b = BuiltinPtr::dynamicCast(p);
+ assert(b);
+ str = b->typeId();
+ }
+ emitWarning(file, line, "invalid metadata for " + str);
+ }
+ else if(i->find("java:protobuf:") == 0 || i->find("java:serializable:") == 0)
+ {
+ //
+ // Only valid in sequence defintion which is checked in visitSequence
+ //
+ emitWarning(file, line, "ignoring invalid metadata `" + *i + "'");
+ }
+ }
+}
+
+void
+Slice::JavaGenerator::MetaDataVisitor::validateGetSet(const SyntaxTreeBasePtr& p, const StringList& metaData,
+ const string& file, const string& line)
+{
+ for(StringList::const_iterator i = metaData.begin(); i != metaData.end(); ++i)
+ {
+ //
+ // The "getset" metadata can only be specified on a class, struct, exception or data member.
+ //
+ if((*i) == "java:getset" &&
+ (!ClassDefPtr::dynamicCast(p) && !StructPtr::dynamicCast(p) && !ExceptionPtr::dynamicCast(p) &&
+ !DataMemberPtr::dynamicCast(p)))
+ {
+ string str;
+ ContainedPtr cont = ContainedPtr::dynamicCast(p);
+ if(cont)
+ {
+ str = cont->kindOf();
+ }
+ else
+ {
+ BuiltinPtr b = BuiltinPtr::dynamicCast(p);
+ assert(b);
+ str = b->typeId();
+ }
+ emitWarning(file, line, "invalid metadata for " + str);
+ }
+ }
+}