void UmlNcRelation::write(FileOut & out) { if ((relationKind() == aDependency) && (parent()->kind() == anUseCase) && (target()->kind() == anUseCase)) { const char * t; const char * r; if (stereotype() == "include") { t = "Include"; r = "addition"; } else if (stereotype() == "extend") { t = "Extend"; r = "extendedCase"; } else { write(out, TRUE); return; } out.indent(); out << "<" << stereotype() << " xmi:type=\"uml:" << t << "\""; out.id(this); out.ref(target(), r); out << ">\n"; out.indent(+1); write_description_properties(out); out.indent(-1); out.indent(); out << "</" << stereotype() << ">\n"; } else write(out, TRUE); }
void UmlArtifact::genpro() { UmlPackage * pack = (UmlPackage *) parent()->parent(); Q3CString path; if (! propertyValue("genpro path", path)) { path = pack->cppSrcDir(); if (path.isEmpty()) path = root_dir(); else if (QDir::isRelativePath(path)) { QDir d(root_dir()); d.cd(path); path = d.absPath(); } } if (stereotype() == "executable") { gen_app(path); } else UmlCom::trace(stereotype() + " : not managed"); }
void UmlRelation::compute_dependency(Q3PtrList<CppRefType> & dependencies, const Q3CString & cl_stereotype, bool all_in_h) { if (cl_stereotype == "enum") return; switch (relationKind()) { case aDependency: if (stereotype() == "friend") break; CppRefType::add(roleType(), dependencies, cppDecl() != "#include in source", roleType()->isCppExternal()); break; case aGeneralisation: case aRealization: CppRefType::add(roleType(), dependencies, TRUE); break; default: Q3CString decl = cppDecl(); int index; if ((index = decl.find("${static}")) != -1) decl.remove((unsigned) index, 9); if ((index = decl.find("${mutable}")) != -1) decl.remove((unsigned) index, 10); if ((index = decl.find("${volatile}")) != -1) decl.remove((unsigned) index, 11); if ((index = decl.find("${const}")) != -1) decl.remove((unsigned) index, 8); if ((index = decl.find("${multiplicity}")) != -1) decl.remove((unsigned) index, 15); if ((index = decl.find("${name}")) != -1) decl.remove((unsigned) index, 7); if ((index = decl.find("${inverse_name}")) != -1) decl.remove((unsigned) index, 15); if ((index = decl.find("${value}")) != -1) decl.remove((unsigned) index, 8); if ((index = decl.find("${h_value}")) != -1) decl.remove((unsigned) index, 10); if ((index = decl.find("${stereotype}")) != -1) decl.replace((unsigned) index, 13, CppSettings::relationAttributeStereotype(stereotype())); if ((index = decl.find("${association}")) != -1) { decl.replace((unsigned) index, 14, association().toString()); } replace_alias(decl); UmlTypeSpec type; type.type = roleType(); UmlClassMember::compute_dependency(dependencies, decl, type, all_in_h); } }
void UmlClass::ref(FileOut & out) { if ((stereotype() == "actor") || ((parent()->kind() != aClassView) && (parent()->kind() != aClass))) out << "<UML:Actor"; else if (stereotype() == "interface") out << "<UML:Interface"; else out << "<UML:Class"; out.idref(this); out << "/>"; }
void UmlNode::write(FileOut & out) { const char * k = (_uml_20) ? "ownedMember" : "packagedElement"; out.indent(); out << "<" << k << " xmi:type=\"" << ((stereotype() == "device\"") ? "uml:Device" : "uml:Node\""); out.id(this); out << " name=\""; out.quote((const char *)name()); //[jasa] ambiguous call out << "\">\n"; out.indent(+1); write_description_properties(out); const QVector<UmlItem*> ch = children(); unsigned n = ch.size(); for (unsigned i = 0; i != n; i += 1) ch[i]->write(out); out.indent(-1); out.indent(); out << "</" << k << ">\n"; unload(); }
void UmlRelation::uml2cpp(bool) { bool composition = FALSE; switch (relationKind()) { case aGeneralisation: case aRealization: set_CppDecl("${type}"); break; case aDependency: if (stereotype() == "friend") set_CppDecl("friend " + CppSettings::classStereotype(roleType()->stereotype()) + " ${type};"); else set_CppDecl("#include in source"); break; case anAggregationByValue: case aDirectionalAggregationByValue: composition = TRUE; // no break default: { Q3CString st = CppSettings::classStereotype(parent()->stereotype()); set_CppDecl(((st == "enum") || (st == "typedef") || (st == "ignored")) ? Q3CString("") : CppSettings::relationDecl(composition, multiplicity())); } } }
void UmlClass::html(QCString pfix, unsigned int rank, unsigned int level) { QCString s = stereotype(); if (flat) { define(); if (s == "stereotype") chapter("Stereotype", pfix, rank, "stereotype", level); else if (s == "metaclass") chapter("Metaclass", pfix, rank, "metaclass", level); else chapter("Class", pfix, rank, "class", level); gen_html(pfix, rank, level); unload(FALSE, FALSE); } else { if (s == "stereotype") fw.write("<table><tr><td><div class=\"element\">Stereotype <b>"); else if (s == "metaclass") fw.write("<table><tr><td><div class=\"element\">Metaclass <b>"); else fw.write("<table><tr><td><div class=\"element\">Class <b>"); write(); fw.write("</b></div></td></tr></table>\n"); } }
void UmlClass::write(QCString target) { if (known) { QCString s = stereotype(); if ((s != "stereotype") && (s != "metaclass")) s = "class"; if (flat) fw.write("<a href=\"index"); else { fw.write("<a href=\""); fw.write(s); fw.write((unsigned) getIdentifier()); } fw.write(".html#ref"); fw.write(s); fw.write((unsigned) getIdentifier()); fw.write("\" target = \""); fw.write(target); fw.write("\"><b>"); writeq(name()); fw.write("</b></a>"); } else writeq(name()); }
void UmlClass::java(Q3Dict<Q3CString> & prop) { if (!scanning) { Q3CString d = (stereotype() == "interface") ? JavaSettings::interfaceDecl() : JavaSettings::classDecl(); Q3CString * v; if ((v = prop.find("Java/Final")) != 0) { if (*v == "TRUE") set_isJavaFinal(TRUE); prop.remove("Java/Final"); } if ((v = prop.find("Java/Strictfp")) != 0) { if (*v == "TRUE") { int index; if ((index = d.find("${public}")) != -1) d.insert((unsigned) index + 9, "strictfp "); else if ((index = d.find("${visibility}")) != -1) d.insert((unsigned) index + 13, "strictfp "); } prop.remove("Java/Strictfp"); } set_JavaDecl(d); } }
Q3CString UmlClass::cpp_stereotype() { Q3CString s = CppSettings::classStereotype(stereotype()); return ((s == "struct") || (s == "union") || (s == "enum") || (s == "typedef")) ? s : Q3CString("class"); }
WrapperStr UmlClass::cpp_stereotype() { WrapperStr s = CppSettings::classStereotype(stereotype()); return ((s == "struct") || (s == "union") || (s == "enum") || (s == "typedef")) ? s : WrapperStr("class"); }
void UmlArtifact::roundtrip_java() { if (! managed) { managed = TRUE; if (stereotype() != "source") return; const WrapperStr srcdef = javaSource(); if (srcdef.isEmpty()) return; const WrapperStr & name = UmlArtifact::name(); UmlPackage * pack = package(); WrapperStr src_path = pack->java_path(name); { WrapperStr s = " <i> " + src_path + "</i>"; UmlCom::message(name); if (verbose()) UmlCom::trace(WrapperStr("<hr><font face=helvetica>roundtrip body from") + s + "</font><br>"); else set_trace_header(WrapperStr("<font face=helvetica>roundtrip body from") + s + "</font><br>"); } UmlOperation::roundtrip(src_path, javaLanguage); } }
void UmlClass::write_actor(FileOut & out) { out.indent(); out << "<UML:Actor name=\"" << name() << '"'; out.id(this); out << " visibility=\"public\" isAbstract=\"" << ((isAbstract()) ? "true" : "false") << "\" isActive=\"false\" >\n"; out.indent(+1); if (stereotype() != "actor") write_stereotype(out); write_description_properties(out); out.indent(-1); out.indent(); out << "</UML:Actor>\n"; const QVector<UmlItem*> ch = children(); unsigned n = ch.size(); for (unsigned i = 0; i != n; i += 1) if (ch[i]->kind() == aRelation) ch[i]->write_if_needed(out); }
void UmlClass::gen_python_decl(QByteArray s, bool descr) { QByteArray st = PythonSettings::classStereotype(stereotype()); if (st == "ignored") return; const char * p = bypass_comment(s); while (*p != 0) { if (!strncmp(p, "${comment}", 10)) p += 10; else if (!strncmp(p, "${description}", 14)) p += 14; else if (!strncmp(p, "${docstring}", 12)) p += 12; else if (!strncmp(p, "${name}", 7)) { p += 7; writeq(name()); } else if (!strncmp(p, "${inherit}", 10)) { p += 10; const QVector<UmlItem*> ch = children(); bool inh = FALSE; for (int i = 0; i != ch.size(); i += 1) { if (ch[i]->kind() == aRelation) { UmlRelation * rel = (UmlRelation *) ch[i]; aRelationKind k = rel->relationKind(); if (((k == aGeneralisation) || (k == aRealization)) && !rel->pythonDecl().isEmpty()) { if (inh) fw.write(", "); else { inh = TRUE; fw.write('('); } rel->roleType()->write(); } } } if (inh) fw.write(')'); else if (isPython_2_2()) fw.write("(object)"); break; } else if (!descr && ((*p == '\r') || (*p == '\n') || (*p == ':'))) break; else if (*p == '@') manage_alias(p); else writeq(*p++); } }
void UmlClass::write() { if (!known) writeq(name()); else { QByteArray s = stereotype(); if ((s != "stereotype") && (s != "metaclass")) s = "class"; if (flat) fw.write("<a href=\"index"); else { fw.write("<a href=\""); fw.write(s); fw.write((unsigned) getIdentifier()); } fw.write(".html#ref"); fw.write(s); fw.write((unsigned) getIdentifier()); fw.write("\"><b>"); writeq(name()); fw.write("</b></a>"); } }
bool UmlArtifact::set_roundtrip_expected() { if ((stereotype() != "source") || cppSource().isEmpty()) return TRUE; const Q3PtrVector<UmlClass> & cls = associatedClasses(); if (cls.isEmpty()) { if ((name() == "main") && cppHeader().isEmpty()) main_art = this; else return TRUE; } has_roundtrip_expected = TRUE; roundtrip_expected = TRUE; useless = TRUE; fully_updated = TRUE; ((UmlPackage *) parent()->parent())->get_package()->own(this); UmlClass ** v = cls.data(); UmlClass ** vsup = v + cls.size(); bool result = isWritable(); for (; v != vsup; v += 1) result &= (*v)->set_roundtrip_expected(); return result; }
void UmlClass::cplusplus(Q3Dict<Q3CString> & prop) { if (!scanning) { if (stereotype() == "typedef") { Q3CString * bt = prop.find("Cplusplus/ImplementationType"); if (bt != 0) { UmlTypeSpec t; t.explicit_type = *bt; // no quidu set_BaseType(t); } set_CppDecl(CppSettings::typedefDecl()); } else if (stereotype() == "struct") set_CppDecl(CppSettings::structDecl()); else if (stereotype() == "union") set_CppDecl(CppSettings::unionDecl()); else if (stereotype() == "enum") set_CppDecl(CppSettings::enumDecl()); else set_CppDecl(CppSettings::classDecl()); prop.remove("Cplusplus/ImplementationType"); } Q3CString * v; if ((v = prop.find("Cplusplus/BodySourceFile")) != 0) { _body_file = *v; prop.remove("Cplusplus/BodySourceFile"); } else if ((v = prop.find("Traversal/BodyFile")) != 0) { _body_file = *v; prop.remove("Traversal/BodyFile"); } if ((v = prop.find("Cplusplus/HeaderSourceFile")) != 0) { _file = *v; prop.remove("Cplusplus/HeaderSourceFile"); } else if ((v = prop.find("Traversal/CodeFile")) != 0) { _file = *v; prop.remove("Traversal/CodeFile"); } }
void UmlClass::html() { QCString s; UmlCom::message(name()); if (stereotype() == "stereotype") start_file("stereotype" + s.setNum((unsigned) getIdentifier()), "Stereotype " + name(), TRUE); else if (stereotype() == "metaclass") start_file("metaclass" + s.setNum((unsigned) getIdentifier()), "Metaclass " + name(), TRUE); else start_file("class" + s.setNum((unsigned) getIdentifier()), "Class " + name(), TRUE); define(); gen_html("", 0, 0); end_file(); unload(FALSE, FALSE); }
void UmlArtifact::solveManifestation(WrapperStr s, WrapperStr idref) { QMap<WrapperStr, UmlItem *>::Iterator it = All.find(idref); if (it == All.end()) { if (!FileIn::isBypassedId(idref)) UmlCom::trace("manifestation : unknown utilized element reference '" + idref + "'<br>"); return; } UmlItem * target = *it; if (!FromBouml || (s != "dependency")) { switch (target->kind()) { case aClass: if (s != "source") break; else if (stereotype().isEmpty()) set_Stereotype("source"); else if (stereotype() != "source") break; addAssociatedClass((UmlClass *) target); return; case anArtifact: if (!FromBouml) break; addAssociatedArtifact((UmlArtifact *) target); return; default: break; } } UmlNcRelation * rel = UmlNcRelation::create(aDependency, this, target); if (rel == 0) UmlCom::trace("cannot create manifestation from '" + name() + "' to '" + target->name() + "'"); else rel->set_Stereotype("manifest"); }
void UmlPackage::html(Q3CString pfix, unsigned int rank, unsigned int level) { define(); if (stereotype() == "profile") chapter("Profile", pfix, rank, "profile", level); else chapter("Package", pfix, rank, "package", level); Q3CString s = description(); if (!s.isEmpty()) { fw.write("<p>"); writeq(s); fw.write("<br /></p>"); } bool ul = FALSE; s = cppNamespace(); if (!s.isEmpty()) { fw.write("<p></p><ul>\n"); ul = TRUE; fw.write("<li>C++ namespace : "); writeq(s); fw.write("</li>\n"); } s = javaPackage(); if (!s.isEmpty()) { if (! ul) fw.write("<p></p><ul>"); ul = TRUE; fw.write("<li>Java package : "); writeq(s); fw.write("</li>\n"); } if (ul) fw.write("</ul>\n"); write_dependencies(); UmlDiagram * d = associatedDiagram(); if (d != 0) { fw.write("<p>Diagram : "); d->write(); fw.write("</p>\n"); } write_properties(); write_children(pfix, rank, level); unload(FALSE, FALSE); }
QCString UmlClass::java_stereotype() { QCString s = JavaSettings::classStereotype(stereotype()); return ((s == "ignored") || (s == "union") || (s == "enum") || (s == "interface") || (s == "@interface") || (s == "enum_pattern")) ? s : QCString("class"); }
void UmlOperation::change(Context & ctx) { if (ctx.onOperation() && ctx.match_stereotype(stereotype())) { if (ctx.cpp()) { const Q3CString & c = cppDecl(); if (!c.isEmpty() && ctx.match(c)) { if (!set_CppDecl(ctx.replace(c))) ctx.err(); } const Q3CString & f = cppDef(); if (!f.isEmpty() && ctx.match(f)) { if (!set_CppDef(ctx.replace(f))) ctx.err(); } } if (ctx.java()) { const Q3CString & c = javaDecl(); if (!c.isEmpty() && ctx.match(c)) { if (!set_JavaDecl(ctx.replace(c))) ctx.err(); } } if (ctx.php()) { const Q3CString & c = phpDecl(); if (!c.isEmpty() && ctx.match(c)) { if (!set_PhpDecl(ctx.replace(c))) ctx.err(); } } if (ctx.python()) { const Q3CString & c = pythonDecl(); if (!c.isEmpty() && ctx.match(c)) { if (!set_PythonDecl(ctx.replace(c))) ctx.err(); } } if (ctx.idl()) { const Q3CString & c = idlDecl(); if (!c.isEmpty() && ctx.match(c)) { if (!set_IdlDecl(ctx.replace(c))) ctx.err(); } } } }
void UmlItem::write_description_properties(FileOut & out) { if (! description().isEmpty()) { static int rank = 0; out.indent(); out << "<ownedComment xmi:type=\"uml:Comment\" xmi:id=\"COMMENT_" << ++rank << "\" body=\""; out.quote((const char *)description()); //[jasa] ambiguous call out << "\"/>\n"; } WrapperStr ste = stereotype(); if (_gen_extension) { const Q3Dict<WrapperStr> up = properties(); Q3DictIterator<WrapperStr> it(up); if (it.current()) { out.indent(); out << "<xmi:Extension extender=\"Bouml\">\n"; if (! ste.isEmpty()) { out.indent(); out << "\t<stereotype name=\""; out.quote((const char *)ste); //[jasa] ambiguous call out << "\"/>\n"; } do { out.indent(); out << "\t<taggedValue tag=\""; out.quote((const char *)it.currentKey()); //[jasa] ambiguous call out << "\" value=\""; out.quote((const char *) * (it.current())); //[jasa] ambiguous call out << "\"/>\n"; ++it; } while (it.current()); out.indent(); out << "</xmi:Extension>\n"; } else if (! ste.isEmpty()) { out.indent(); out << "<xmi:Extension extender=\"Bouml\"><stereotype name=\""; out.quote((const char *)ste); //[jasa] ambiguous call out << "\"/></xmi:Extension>\n"; } } if (ste.operator QString().contains(':') == 1) // probably a stereotype part of profile _stereotypes[ste].append(this); }
void UmlArtifact::write(FileOut & out) { const char * k = (_uml_20) ? "ownedMember" : "packagedElement"; out.indent(); out << "<" << k << " xmi:type=\"uml:Artifact\""; out.id(this); out << " name=\""; out.quote((const char *)name()); //[jasa] ambiguous call out << "\">\n"; out.indent(+1); write_description_properties(out); const Q3PtrVector<UmlItem> ch = children(); unsigned i; unsigned n = ch.size(); unsigned rank = 0; for (i = 0; i != n; i += 1) { UmlItem * x = ch[i]; if ((x->kind() == aNcRelation) && (x->stereotype() == "manifest") && (((UmlNcRelation *) x)->relationKind() == aDependency)) write_manifest(out, ((UmlNcRelation *) x)->target(), "dependency", ++rank); else ch[i]->write(out); } if (stereotype() == "source") { const Q3PtrVector<UmlClass> & cls = associatedClasses(); n = cls.size(); for (i = 0; i != n; i += 1) write_manifest(out, cls[i], "source", ++rank); } else { const Q3PtrVector<UmlArtifact> & arts = associatedArtifacts(); n = arts.size(); for (i = 0; i != n; i += 1) write_manifest(out, arts[i], 0, ++rank); } out.indent(-1); out.indent(); out << "</" << k << ">\n"; unload(); }
void UmlItem::write_stereotype(FileOut & out) { if (! stereotype().isEmpty()) { out.indent(); out << "<UML:ModelElement.stereotype>\n"; out.indent(); out << "\t<UML:Stereotype name=\""; out.quote(stereotype()); out << "\"/>\n"; out.indent(); out << "</UML:ModelElement.stereotype>\n"; switch (_taggedvalue_mode) { case 1: out.indent(); out << "<UML:ModelElement.taggedValue>\n"; out.indent(); out << "\t<UML:TaggedValue tag=\"stereotype\" value=\""; out.quote(stereotype()); out << "\"/>\n"; out.indent(); out << "</UML:ModelElement.taggedValue>\n"; break; case 2: out.indent(); out << "<UML:ModelElement.taggedValue>\n"; out.indent(); out << "\t<UML:TaggedValue.tag>stereotype</UML:TaggedValue.tag>\n"; out.indent(); out << "\t<UML:TaggedValue.value>"; out.quote(stereotype()); out << "</UML:TaggedValue.value>\n"; out.indent(); out << "</UML:ModelElement.taggedValue>\n"; } } }
void UmlActivityObject::write(FileOut & out) { const char * k = (parent()->kind() == anActivity) ? "node" : "containedNode"; out.indent(); out << '<' << k << " xmi:type=\"uml:"; WrapperStr st = stereotype(); if (st == "datastore") out << "DataStoreNode"; else if (st == "centralBuffer") out << "CentralBufferNode"; else out << "ObjectNode"; out << "\" name=\""; out.quote(name()); out << '"'; out.id(this); if (isControlType()) out << " isControlType=\"true\""; write_ordering(out); write_selection(out); write_in_state(out); out << ">\n"; out.indent(+1); write_description_properties(out); write_multiplicity(out, multiplicity(), this); UmlItem::write_type(out, type()); const QVector<UmlItem*> ch = children(); unsigned n = ch.size(); for (unsigned i = 0; i != n; i += 1) ch[i]->write(out); write_incoming_flows(out); out.indent(-1); out.indent(); out << "</" << k << ">\n"; unload(); }
void UmlClass::uml2java(bool rec) { if (isJavaExternal()) set_JavaDecl(JavaSettings::externalClassDecl()); else { QCString st = JavaSettings::classStereotype(stereotype()); UmlItem * pack = parent()->parent(); while (pack->kind() != aPackage) pack = pack->parent(); if ((st == "stereotype") || (st == "metaclass") || (pack->stereotype() == "profile")) { set_CppDecl(""); return; } if (st == "enum_pattern") set_JavaDecl(JavaSettings::enumPatternDecl()); else if (st == "enum") set_JavaDecl(JavaSettings::enumDecl()); else if (st == "interface") set_JavaDecl(JavaSettings::interfaceDecl()); else if (st == "@interface") { QCString s = JavaSettings::interfaceDecl(); int index = s.find("interface"); if (index != -1) s.insert(index, '@'); set_JavaDecl(s); } else if (st == "ignored") { set_JavaDecl(""); return; } else set_JavaDecl(JavaSettings::classDecl()); if (rec) { const QVector<UmlItem> ch = children(); unsigned n = ch.size(); for (unsigned i = 0; i != n; i += 1) ch[i]->uml2java(rec); } if (parent()->kind() == aClassView) // not nested artifact()->set_JavaSource(JavaSettings::sourceContent()); } }
bool UmlArtifact::set_roundtrip_expected_for_class() { if (roundtrip_expected) return TRUE; if ((stereotype() != "source") || cppSource().isEmpty()) return FALSE; has_roundtrip_expected = TRUE; roundtrip_expected = TRUE; useless = TRUE; ((UmlPackage *) parent()->parent())->get_package()->own(this); return TRUE; }
void UmlRelation::change(Context & ctx) { if (ctx.onRelation() && ctx.match_stereotype(stereotype())) { if (ctx.cpp()) { const QByteArray & c = cppDecl(); if (!c.isEmpty() && ctx.match(c)) { if (!set_CppDecl(ctx.replace(c))) ctx.err(); } } if (ctx.java()) { const QByteArray & c = javaDecl(); if (!c.isEmpty() && ctx.match(c)) { if (!set_JavaDecl(ctx.replace(c))) ctx.err(); } } if (ctx.php()) { const QByteArray & c = phpDecl(); if (!c.isEmpty() && ctx.match(c)) { if (!set_PhpDecl(ctx.replace(c))) ctx.err(); } } if (ctx.python()) { const QByteArray & c = pythonDecl(); if (!c.isEmpty() && ctx.match(c)) { if (!set_PythonDecl(ctx.replace(c))) ctx.err(); } } if (ctx.idl()) { const QByteArray & c = idlDecl(); if (!c.isEmpty() && ctx.match(c)) { if (!set_IdlDecl(ctx.replace(c))) ctx.err(); } } } }
void UmlClass::uml2cpp(bool rec) { if (isCppExternal()) set_CppDecl(CppSettings::externalClassDecl()); else { QCString st = CppSettings::classStereotype(stereotype()); UmlItem * pack = parent()->parent(); while (pack->kind() != aPackage) pack = pack->parent(); if ((st == "stereotype") || (st == "metaclass") || (pack->stereotype() == "profile")) { set_CppDecl(""); return; } if (st == "enum") set_CppDecl(CppSettings::enumDecl()); else if (st == "union") set_CppDecl(CppSettings::unionDecl()); else if (st == "struct") set_CppDecl(CppSettings::structDecl()); else if (st == "typedef") set_CppDecl(CppSettings::typedefDecl()); else if (st == "ignored") { set_CppDecl(""); return; } else set_CppDecl(CppSettings::classDecl()); if (rec) { const QVector<UmlItem> ch = children(); unsigned n = ch.size(); for (unsigned i = 0; i != n; i += 1) ch[i]->uml2cpp(rec); } if (parent()->kind() == aClassView) { // not nested UmlArtifact * art = artifact(); art->set_CppSource(CppSettings::sourceContent()); art->set_CppHeader(CppSettings::headerContent()); } } }