25 [NanoXlsxQueuePlugIn(PlugInUUID =
"EXTERNAL_LINK_READER", QueueUUID = PlugInUUID.ReaderPrependingQueue, PlugInOrder = 20000)]
26 internal class ExternalLinkReader : IDiscoveryPackageReader
30 private Stream stream;
32 private const string ExternalLinkPathRelationshipType =
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLinkPath";
33 private const string OfficeRelationshipNamespace =
"http://schemas.openxmlformats.org/officeDocument/2006/relationships";
34 private const string AlternateUrlNamespace =
"http://schemas.microsoft.com/office/spreadsheetml/2021/extlinks2021";
42 public IOptions Options {
get;
set; }
46 public Workbook Workbook {
get;
set; }
50 public Action<Stream, Workbook, string, IOptions, int?> InlinePluginHandler {
get;
set; }
55 public string StreamEntryName =>
null;
60 public RelationshipInfo CurrentRelationship {
get;
set; }
65 public string DocumentType {
get {
return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLink"; } }
72 public ExternalLinkReader()
85 public void Init(Stream stream, Workbook workbook, IOptions readerOptions, Action<Stream, Workbook, string, IOptions, int?> inlinePluginHandler)
88 this.Workbook = workbook;
89 this.Options = readerOptions;
90 this.InlinePluginHandler = inlinePluginHandler;
99 Dictionary<int, ExternalWorksheet> worksheets =
new Dictionary<int, ExternalWorksheet>();
100 List<ExternalDefinedName> definedNames =
new List<ExternalDefinedName>();
101 string targetRelationshipId =
null;
102 string absoluteAlternateRelationshipId =
null;
103 string relativeAlternateRelationshipId =
null;
106 using (XmlReader reader = XmlReader.Create(stream, XmlStreamUtils.CreateSettings()))
108 bool isExternalBook =
false;
109 while (reader.Read())
111 if (XmlStreamUtils.IsElement(reader,
"externalBook"))
113 isExternalBook =
true;
114 targetRelationshipId = reader.GetAttribute(
"id", OfficeRelationshipNamespace);
119 if (reader.NamespaceURI == AlternateUrlNamespace && XmlStreamUtils.IsElement(reader,
"absoluteUrl"))
121 absoluteAlternateRelationshipId = reader.GetAttribute(
"id", OfficeRelationshipNamespace);
123 else if (reader.NamespaceURI == AlternateUrlNamespace && XmlStreamUtils.IsElement(reader,
"relativeUrl"))
125 relativeAlternateRelationshipId = reader.GetAttribute(
"id", OfficeRelationshipNamespace);
127 else if (XmlStreamUtils.IsElement(reader,
"sheetNames"))
129 GetSheeetNames(reader.ReadSubtree(), worksheets);
130 if (worksheets.Count == 0)
132 throw new IOException(
"No cached worksheets could be determined");
135 else if (XmlStreamUtils.IsElement(reader,
"sheetDataSet"))
137 GetSheetData(reader.ReadSubtree(), worksheets);
139 else if (XmlStreamUtils.IsElement(reader,
"definedNames"))
141 GetDefinedNames(reader.ReadSubtree(), definedNames);
147 RelationshipCatalog discoveryCatalog = Workbook.AuxiliaryData.GetData<RelationshipCatalog>(PlugInUUID.DiscoveryReader, PlugInUUID.DiscoveryCatalogEntity);
148 if (discoveryCatalog ==
null)
150 throw new IOException(
"The relationship catalog is not available for the external link.");
152 string sourcePartPath = CurrentRelationship.ResolvedTargetPath;
153 RelationshipInfo target = GetExternalLinkPathRelationship(discoveryCatalog, sourcePartPath, targetRelationshipId,
"target",
true);
154 RelationshipInfo absoluteAlternate = GetExternalLinkPathRelationship(discoveryCatalog, sourcePartPath, absoluteAlternateRelationshipId,
"absolute alternate",
false);
155 RelationshipInfo relativeAlternate = GetExternalLinkPathRelationship(discoveryCatalog, sourcePartPath, relativeAlternateRelationshipId,
"relative alternate",
false);
157 link.SetReadUris(target.Target, absoluteAlternate?.Target, relativeAlternate?.Target);
158 foreach (KeyValuePair<int, ExternalWorksheet> worksheet
in worksheets)
160 link.AddWorksheet(worksheet.Value);
164 link.AddDefinedName(definedName);
166 link.WorkbookRId = CurrentRelationship.Id;
168 List<ExternalLink> externalLinks = Workbook.AuxiliaryData.GetDataList<
ExternalLink>(PlugInUUID.CompatibilityInlineProcessor, CompatibilityConstants.EXTERNAL_LINK_OBJECT_ENTITY);
169 int index = externalLinks.Count;
170 Workbook.AuxiliaryData.SetData(PlugInUUID.CompatibilityInlineProcessor, CompatibilityConstants.EXTERNAL_LINK_OBJECT_ENTITY, index, link,
true);
174 throw new IOException(
"The XML entry could not be read from the " + nameof(stream) +
". Please see the inner exception:", ex);
188 private static RelationshipInfo GetExternalLinkPathRelationship(
189 RelationshipCatalog catalog,
190 string sourcePartPath,
191 string relationshipId,
195 if (
string.IsNullOrEmpty(relationshipId))
199 throw new IOException(
"The external-link " + role +
" relationship ID is missing.");
204 RelationshipInfo relationship = catalog.GetBySourceAndId(sourcePartPath, relationshipId);
205 if (relationship ==
null)
207 throw new IOException(
"The external-link " + role +
" relationship '" + relationshipId +
"' could not be resolved.");
209 if (!
string.Equals(relationship.Type, ExternalLinkPathRelationshipType, StringComparison.Ordinal)
210 || relationship.TargetMode != System.IO.Packaging.TargetMode.External)
212 throw new IOException(
"The external-link " + role +
" relationship '" + relationshipId +
"' has an invalid type or target mode.");
222 private static void GetSheeetNames(XmlReader sheetNames, Dictionary<int, ExternalWorksheet> worksheets)
225 while (sheetNames.Read())
227 if (XmlStreamUtils.IsElement(sheetNames,
"sheetName"))
229 string val = sheetNames.GetAttribute(
"val");
231 worksheets[index] = worksheet;
243 private static void GetSheetData(XmlReader sheetDataSet, Dictionary<int, ExternalWorksheet> worksheets)
245 int currentIndex = -1;
247 while (sheetDataSet.Read())
249 if (XmlStreamUtils.IsElement(sheetDataSet,
"sheetData"))
251 string id = sheetDataSet.GetAttribute(
"sheetId");
252 currentIndex = ParserUtils.ParseInt(
id);
253 if (!worksheets.TryGetValue(currentIndex, out currentWorksheet))
255 throw new IOException(
"The cached worksheet with sheet ID '" +
id +
"' is not defined.");
257 string refreshErrors = sheetDataSet.GetAttribute(
"refreshErrors");
258 if (refreshErrors !=
null)
260 int parserdSate = ParserUtils.ParseBinaryBool(refreshErrors);
261 currentWorksheet.RefreshErros = parserdSate == 1 ? true :
false;
265 else if (XmlStreamUtils.IsElement(sheetDataSet,
"row"))
267 GetRowData(sheetDataSet.ReadSubtree(), currentWorksheet);
277 private static void GetDefinedNames(XmlReader definitions, List<ExternalDefinedName> definedNames)
279 while (definitions.Read())
281 if (!XmlStreamUtils.IsElement(definitions,
"definedName"))
286 definitions.GetAttribute(
"name"),
287 definitions.GetAttribute(
"refersTo"),
290 RelationshipId = definitions.GetAttribute(
"sheetId")
292 definedNames.Add(definedName);
305 if (!XmlStreamUtils.IsElement(row,
"cell"))
310 string address = row.GetAttribute(
"r");
311 string type = row.GetAttribute(
"t");
312 string cellMetaData = row.GetAttribute(
"vm");
335 if (!row.IsEmptyElement)
337 using (XmlReader cell = row.ReadSubtree())
341 if (XmlStreamUtils.IsElement(cell,
"v"))
343 value = cell.ReadElementContentAsString();
349 worksheet.
AddCell(address, value, dataType, cellMetaData);