41 private: |
41 private: |
42 std::ostream& output; |
42 std::ostream& output; |
43 wstring_convert<codecvt_utf8<wchar_t>> convertor; // XML output will be always in UTF-8 |
43 wstring_convert<codecvt_utf8<wchar_t>> convertor; // XML output will be always in UTF-8 |
44 std::vector<TypeId> attributeTypes; |
44 std::vector<TypeId> attributeTypes; |
45 std::vector<string_t> attributeTypeCodes; |
45 std::vector<string_t> attributeTypeCodes; |
46 std::vector<string_t> attributeNames; |
46 std::vector<string_t> attributeNamesIn; |
|
47 std::vector<string_t> attributeNamesOut; |
47 integer_t valueCount = 0; |
48 integer_t valueCount = 0; |
48 integer_t attributeCount = 0; |
49 integer_t attributeCount = 0; |
49 integer_t relationCount = 0; |
50 integer_t relationCount = 0; |
50 |
51 |
51 void writeRelationName(const string_t& name) { |
52 void writeRelationName(const string_t& name) { |
60 case TypeId::STRING: return ""; |
61 case TypeId::STRING: return ""; |
61 default: throw RelpipeReaderException(L"Unsupported type – unable to convert to a Recfile type"); |
62 default: throw RelpipeReaderException(L"Unsupported type – unable to convert to a Recfile type"); |
62 } |
63 } |
63 } |
64 } |
64 |
65 |
65 void writeAttributeMetadata(const handlers::AttributeMetadata& attribute) { |
66 void writeAttributeMetadata(size_t i) { |
66 // FIXME: escaping/filtering |
67 std::string recfileType = toRecfileType(attributeTypes[i]); |
67 std::string recfileType = toRecfileType(attribute.getTypeId()); |
68 if (recfileType.size()) output << "%type: " << convertor.to_bytes(attributeNamesOut[i]) << " " << recfileType << std::endl; |
68 if (recfileType.size()) output << "%type: " << convertor.to_bytes(attribute.getAttributeName()) << " " << recfileType << std::endl; |
|
69 } |
69 } |
70 |
70 |
71 void writeSeparator() { |
71 void writeSeparator() { |
72 output << std::endl; |
72 output << std::endl; |
73 } |
73 } |
88 || between(ch, L'A', L'Z') |
88 || between(ch, L'A', L'Z') |
89 || between(ch, L'0', L'9'); |
89 || between(ch, L'0', L'9'); |
90 } |
90 } |
91 } |
91 } |
92 |
92 |
93 void writeAttribute(const string_t& name, const TypeId& type, const string_t& value) { |
93 const string_t escapeAttributeName(const string_t& name) { |
|
94 std::wstringstream escaped; |
|
95 |
94 // TODO: multiple escapting mode - including one that is not lossless |
96 // TODO: multiple escapting mode - including one that is not lossless |
95 // but allows writing a single '_' inside the name |
97 // but allows writing a single '_' inside the name |
96 for (size_t i = 0, limit = name.size(); i < limit; i++) { |
98 for (size_t i = 0, limit = name.size(); i < limit; i++) { |
97 wchar_t ch = name[i]; |
99 wchar_t ch = name[i]; |
98 bool valid = isValidNameCharacter(ch, i == 0); |
100 bool valid = isValidNameCharacter(ch, i == 0); |
99 |
101 |
100 // Not a lossless round-trip |
102 // Not a lossless round-trip |
101 // (maybe we could sacrifice some reserved prefix): |
103 // (maybe we could sacrifice some reserved prefix): |
102 if (i == 0 && !valid) output << 'x'; |
104 if (i == 0 && !valid) escaped << 'x'; |
103 |
105 |
104 if (ch == '_') output << "__"; |
106 if (ch == '_') escaped << "__"; |
105 else if (valid) output << convertor.to_bytes(ch); |
107 else if (valid) escaped << ch; |
106 else output << '_' << ((uint32_t) ch) << '_'; |
108 else escaped << '_' << ((uint32_t) ch) << '_'; |
107 } |
109 } |
108 output << ": "; |
110 |
|
111 return escaped.str(); |
|
112 } |
|
113 |
|
114 void writeAttribute(const string_t& escapedName, const TypeId& type, const string_t& value) { |
|
115 output << convertor.to_bytes(escapedName) << ": "; |
109 |
116 |
110 for (char ch : convertor.to_bytes(value)) { |
117 for (char ch : convertor.to_bytes(value)) { |
111 output << ch; |
118 output << ch; |
112 if (ch == '\n') output << "+ "; |
119 if (ch == '\n') output << "+ "; |
113 } |
120 } |
138 writeRelationName(name); |
145 writeRelationName(name); |
139 |
146 |
140 attributeCount = attributes.size(); |
147 attributeCount = attributes.size(); |
141 attributeTypes.resize(attributeCount); |
148 attributeTypes.resize(attributeCount); |
142 attributeTypeCodes.resize(attributeCount); |
149 attributeTypeCodes.resize(attributeCount); |
143 attributeNames.resize(attributeCount); |
150 attributeNamesIn.resize(attributeCount); |
|
151 attributeNamesOut.resize(attributeCount); |
144 for (int i = 0; i < attributes.size(); i++) { |
152 for (int i = 0; i < attributes.size(); i++) { |
145 attributeNames[i] = attributes[i].getAttributeName(); |
153 attributeNamesIn[i] = attributes[i].getAttributeName(); |
|
154 attributeNamesOut[i] = escapeAttributeName(attributeNamesIn[i]); |
146 attributeTypes[i] = attributes[i].getTypeId(); |
155 attributeTypes[i] = attributes[i].getTypeId(); |
147 attributeTypeCodes[i] = attributes[i].getTypeName(); |
156 attributeTypeCodes[i] = attributes[i].getTypeName(); |
148 writeAttributeMetadata(attributes[i]); |
157 writeAttributeMetadata(i); |
149 } |
158 } |
150 } |
159 } |
151 |
160 |
152 void attribute(const string_t& value) override { |
161 void attribute(const string_t& value) override { |
153 integer_t i = valueCount % attributeCount; |
162 integer_t i = valueCount % attributeCount; |
154 if (i == 0) writeSeparator(); |
163 if (i == 0) writeSeparator(); |
155 valueCount++; |
164 valueCount++; |
156 writeAttribute(attributeNames[i], attributeTypes[i], value); |
165 writeAttribute(attributeNamesOut[i], attributeTypes[i], value); |
157 } |
166 } |
158 |
167 |
159 void endOfPipe() { |
168 void endOfPipe() { |
160 writeRecordCount(); |
169 writeRecordCount(); |
161 if (valueCount) writeSeparator(); |
170 if (valueCount) writeSeparator(); |