Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBranden Phillips2020-02-19 22:29:30 +0000
committerBranden Phillips2020-02-26 21:49:10 +0000
commitbd5fcfef8d573d82580c01d0f2a2e089e1b11c99 (patch)
treec1267d4bb4bf68055361e2aa31e5456d6f7fbd4e
parent13a6a94782fc9dd522c20537ce50ced0280a4654 (diff)
downloadorg.eclipse.osee-bd5fcfef8d573d82580c01d0f2a2e089e1b11c99.tar.gz
org.eclipse.osee-bd5fcfef8d573d82580c01d0f2a2e089e1b11c99.tar.xz
org.eclipse.osee-bd5fcfef8d573d82580c01d0f2a2e089e1b11c99.zip
feature[TW16540]: Feature-less refactor creating abstract MSWordTemplatePublisher
Change-Id: I03831113bd457557e3a8d0788d1e32f13c9dbe43 Signed-off-by: Branden Phillips <branden.w.phillips@boeing.com>
-rw-r--r--plugins/org.eclipse.osee.define.rest/src/org/eclipse/osee/define/rest/publishing/AbstractMSWordTemplatePublisher.java613
-rw-r--r--plugins/org.eclipse.osee.define.rest/src/org/eclipse/osee/define/rest/publishing/MSWordTemplatePublisher.java115
2 files changed, 728 insertions, 0 deletions
diff --git a/plugins/org.eclipse.osee.define.rest/src/org/eclipse/osee/define/rest/publishing/AbstractMSWordTemplatePublisher.java b/plugins/org.eclipse.osee.define.rest/src/org/eclipse/osee/define/rest/publishing/AbstractMSWordTemplatePublisher.java
new file mode 100644
index 00000000000..ad001acd167
--- /dev/null
+++ b/plugins/org.eclipse.osee.define.rest/src/org/eclipse/osee/define/rest/publishing/AbstractMSWordTemplatePublisher.java
@@ -0,0 +1,613 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Boeing.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Boeing - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osee.define.rest.publishing;
+
+import static org.eclipse.osee.framework.core.enums.CoreAttributeTypes.PlainTextContent;
+import static org.eclipse.osee.framework.core.enums.CoreAttributeTypes.RendererOptions;
+import static org.eclipse.osee.framework.core.enums.CoreAttributeTypes.WholeWordContent;
+import static org.eclipse.osee.framework.core.enums.CoreAttributeTypes.WordTemplateContent;
+import static org.eclipse.osee.framework.core.enums.CoreBranches.COMMON;
+import static org.eclipse.osee.framework.core.enums.PresentationType.PREVIEW;
+import static org.eclipse.osee.framework.core.util.ReportConstants.CONTINUOUS;
+import static org.eclipse.osee.framework.core.util.ReportConstants.FTR;
+import static org.eclipse.osee.framework.core.util.ReportConstants.PAGE_SZ;
+import static org.eclipse.osee.framework.core.util.ReportConstants.PG_SZ;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.eclipse.osee.define.api.ArtifactUrlServer;
+import org.eclipse.osee.define.api.AttributeElement;
+import org.eclipse.osee.define.api.MetadataElement;
+import org.eclipse.osee.define.api.PublishingErrorElement;
+import org.eclipse.osee.define.api.PublishingOptions;
+import org.eclipse.osee.define.api.WordTemplateContentData;
+import org.eclipse.osee.define.rest.internal.wordupdate.WordTemplateContentRendererHandler;
+import org.eclipse.osee.framework.core.data.ArtifactId;
+import org.eclipse.osee.framework.core.data.AttributeTypeId;
+import org.eclipse.osee.framework.core.data.AttributeTypeToken;
+import org.eclipse.osee.framework.core.data.TransactionToken;
+import org.eclipse.osee.framework.core.enums.CoreAttributeTypes;
+import org.eclipse.osee.framework.core.enums.PresentationType;
+import org.eclipse.osee.framework.core.model.type.LinkType;
+import org.eclipse.osee.framework.core.util.WordMLProducer;
+import org.eclipse.osee.framework.jdk.core.type.OseeCoreException;
+import org.eclipse.osee.framework.jdk.core.type.Pair;
+import org.eclipse.osee.framework.jdk.core.util.Strings;
+import org.eclipse.osee.framework.jdk.core.util.xml.Xml;
+import org.eclipse.osee.logger.Log;
+import org.eclipse.osee.orcs.OrcsApi;
+import org.eclipse.osee.orcs.data.ArtifactReadable;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * @author Branden W. Phillips
+ */
+public abstract class AbstractMSWordTemplatePublisher {
+
+ //Constants
+ protected static final String ARTIFACT = "Artifact";
+ private static final Object ARTIFACT_ID = "Artifact Id";
+ private static final String ARTIFACT_TYPE = "Artifact Type";
+ private static final String APPLICABILITY = "Applicability";
+ protected static final String INSERT_ARTIFACT_HERE = "INSERT_ARTIFACT_HERE";
+ protected static final String INSERT_LINK = "INSERT_LINK_HERE";
+ protected static final String PGNUMTYPE_START_1 = "<w:pgNumType [^>]*w:start=\"1\"/>";
+
+ //Patterns
+ protected static final Pattern headElementsPattern =
+ Pattern.compile("(" + INSERT_ARTIFACT_HERE + ")" + "|" + INSERT_LINK,
+ Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE);
+
+ //Template
+ protected String elementType;
+
+ //Outlining Options
+ protected AttributeTypeId headingAttributeType;
+ protected boolean outlining;
+ protected boolean recurseChildren;
+ protected String outlineNumber = "";
+ protected boolean includeEmptyHeaders = false;
+
+ //Data Structures
+ protected final PublishingOptions publishingOptions;
+ protected final List<AttributeElement> attributeElements = new LinkedList<>();
+ protected final List<MetadataElement> metadataElements = new LinkedList<>();
+ protected final List<ArtifactReadable> nonTemplateArtifacts = new LinkedList<>();
+ protected final Set<ArtifactReadable> processedArtifacts = new HashSet<>();
+
+ //Error Variables
+ protected final List<PublishingErrorElement> errorElements = new LinkedList<>();
+ protected Set<String> bookmarkedIds = new HashSet<>();
+ protected HashMap<String, ArtifactReadable> hyperlinkedIds = new HashMap<>();
+
+ protected final OrcsApi orcsApi;
+ protected final Log logger;
+
+ protected AbstractMSWordTemplatePublisher(PublishingOptions publishingOptions, Log logger, OrcsApi orcsApi) {
+ this.publishingOptions = publishingOptions;
+ this.logger = logger;
+ this.orcsApi = orcsApi;
+ }
+
+ /**
+ * Beginning method of the publishing process. Default version takes in the artifact id of the head that the publish
+ * is based off of, and then the artifact id for the template. This method is where the artifact readable is
+ * gathered, and the template options are set up. Other artifact gathering/template set up can be done here. If
+ * everything is valid, move onto the next step for publishing.
+ */
+ public abstract String publish(ArtifactId artifactId, ArtifactId templateArtId);
+
+ /**
+ * Second step of the publishing process. This method is where the WordMLProducer is set up and the word xml starts
+ * to be written. The default version changes some elements of the template first. Then is the start of the template
+ * up until the marking where the artifact content should be. The artifacts/content is then inserted in the middle
+ * via processContent. Finally the rest of the template's word content is placed at the end to finish off the
+ * published document.
+ */
+ protected abstract StringBuilder applyContentToTemplate(ArtifactReadable headArtifact, String templateContent);
+
+ /**
+ * Third step of the publishing process, this is where the processed content of the publish is handled in between the
+ * beginning and end of the render template. In the default implementation, the artifact hierarchy is processed
+ * starting from our head artifact, then any errors are added in their own final section.
+ */
+ protected abstract void processContent(ArtifactReadable headArtifact, WordMLProducer wordMl);
+
+ /**
+ * This method processes each artifact on an individually. The default implementation only handles word template
+ * content, not whole word content or native content. A running list of processed artifacts is kept so no artifact is
+ * processed multiple times. Folder artifacts are not processed either. In the default implementation, artifacts are
+ * processed in hierarchy order so it traces through each artifacts' child recursively if the option is enabled.
+ * Within each artifact, the metadata and attributes are published.
+ */
+ protected abstract void processArtifact(ArtifactReadable artifact, WordMLProducer wordMl);
+
+ //--- Publish Helper Methods ---//
+
+ /**
+ * Grabs artifact readable for the artifact id passed in as the head artifact, default uses the view option that was
+ * set.
+ */
+ protected ArtifactReadable getArtifactHead(ArtifactId artifactId) {
+ ArtifactReadable artifact =
+ orcsApi.getQueryFactory().fromBranch(publishingOptions.branch, publishingOptions.view).andId(
+ artifactId).getArtifact();
+ return artifact;
+ }
+
+ /**
+ * Using the template artifact id, gets the template content and its' options. This default method then will go and
+ * parse the renderOptions attribute.
+ */
+ protected String setUpTemplateWithOptions(ArtifactId templateArtId) {
+ String template = "", options = "";
+ ArtifactReadable templateArtifact;
+ if (templateArtId != ArtifactId.SENTINEL) {
+ templateArtifact = orcsApi.getQueryFactory().fromBranch(COMMON).andId(templateArtId).getArtifact();
+ template = templateArtifact.getSoleAttributeAsString(WholeWordContent, "");
+ options = templateArtifact.getSoleAttributeAsString(RendererOptions, "");
+ parseRenderOptions(options);
+ }
+ return template;
+ }
+
+ /**
+ * This default version of the method goes through each section of the renderOptions, parsing the json and setting
+ * the class variables. In the default publish, if element type is not artifact an error should be thrown because
+ * then the template is not valid
+ */
+ protected void parseRenderOptions(String options) {
+ setElementType(options);
+ if (elementType.equals(ARTIFACT)) {
+ parseAttributeOptions(options);
+ parseMetadataOptions(options);
+ parseOutliningOptions(options);
+ }
+ }
+
+ protected void setElementType(String templateOptions) {
+ try {
+ JSONObject jsonObject = new JSONObject(templateOptions);
+ elementType = jsonObject.getString("ElementType");
+ } catch (JSONException ex) {
+ OseeCoreException.wrapAndThrow(ex);
+ }
+ }
+
+ protected void parseOutliningOptions(String templateOptions) {
+ try {
+ JSONObject jsonObject = new JSONObject(templateOptions);
+ JSONArray optionsArray = jsonObject.getJSONArray("OutliningOptions");
+ JSONObject options = optionsArray.getJSONObject(0);
+
+ outlining = options.getBoolean("Outlining");
+ recurseChildren = options.getBoolean("RecurseChildren");
+ try {
+ includeEmptyHeaders = options.getBoolean("IncludeEmptyHeaders");
+ } catch (JSONException ex) {
+ // The template file json may not have this defined, default is false
+ includeEmptyHeaders = false;
+ }
+ outlineNumber = options.getString("OutlineNumber");
+ String headingAttrType = options.getString("HeadingAttributeType");
+ headingAttributeType = orcsApi.getOrcsTypes().getAttributeTypes().getByName(headingAttrType);
+ } catch (JSONException ex) {
+ OseeCoreException.wrapAndThrow(ex);
+ }
+ }
+
+ protected void parseAttributeOptions(String templateOptions) {
+ try {
+ attributeElements.clear();
+
+ JSONObject jsonObject = new JSONObject(templateOptions);
+ JSONArray attributeOptions = jsonObject.getJSONArray("AttributeOptions");
+ JSONObject options = null;
+
+ for (int i = 0; i < attributeOptions.length(); i++) {
+ options = attributeOptions.getJSONObject(i);
+ String attributeType = options.getString("AttrType");
+ String attributeLabel = options.getString("Label");
+ String formatPre = options.getString("FormatPre");
+ String formatPost = options.getString("FormatPost");
+
+ AttributeElement attrElement = new AttributeElement();
+ boolean typeExists = orcsApi.getOrcsTypes().getAttributeTypes().typeExists(attributeType);
+ if (attributeType.equals("*") || typeExists) {
+ attrElement.setElements(attributeType, attributeLabel, formatPre, formatPost);
+ attributeElements.add(attrElement);
+ }
+ }
+ // Need to check if all attributes will be published. If so set the AllAttributes option.
+ // Assumes that all (*) will not be used when other attributes are specified
+ publishingOptions.allAttributes = false;
+ if (attributeElements.size() == 1) {
+ String attributeName = attributeElements.get(0).getAttributeName();
+ if (attributeName.equals("*")) {
+ publishingOptions.allAttributes = true;
+ }
+ }
+ } catch (JSONException ex) {
+ OseeCoreException.wrapAndThrow(ex);
+ }
+ }
+
+ protected void parseMetadataOptions(String metadataOptions) {
+ try {
+ JSONObject jsonObject = new JSONObject(metadataOptions);
+ JSONObject options = null;
+
+ if (!jsonObject.has("MetadataOptions")) {
+ return;
+ }
+
+ JSONArray optionsArray = jsonObject.getJSONArray("MetadataOptions");
+ for (int i = 0; i < optionsArray.length(); i++) {
+ options = optionsArray.getJSONObject(i);
+
+ String metadataType = options.getString("Type");
+ String metadataFormat = options.getString("Format");
+ String metadataLabel = options.getString("Label");
+
+ MetadataElement metadataElement = new MetadataElement();
+
+ metadataElement.setElements(metadataType, metadataFormat, metadataLabel);
+ metadataElements.add(metadataElement);
+ }
+ } catch (JSONException ex) {
+ OseeCoreException.wrapAndThrow(ex);
+ }
+ }
+
+ //--- ApplyContentToTemplate Helper Methods ---//
+
+ /**
+ * This default version of the method cleans up some pieces of the render template's whole word content, and grabs
+ * the paragraph number if needed.
+ */
+ protected String setUpTemplateContent(WordMLProducer wordMl, ArtifactReadable artifact, String templateContent) {
+ templateContent = templateContent.replaceAll(PGNUMTYPE_START_1, "");
+ if (outlineNumber.equals("")) {
+ outlineNumber = getParagraphNumber(artifact, templateContent);
+ }
+ templateContent = wordMl.setHeadingNumbers(outlineNumber, templateContent, null);
+
+ return templateContent;
+ }
+
+ /**
+ * If the render template has the "insert_artifact_here" marking, the head artifacts paragraph number attribute is
+ * used as the start of the outline number. Otherwise the default is to start at 1
+ */
+ protected String getParagraphNumber(ArtifactReadable artifact, String templateContent) {
+ String startParagraphNumber = "1";
+ Matcher matcher = headElementsPattern.matcher(templateContent);
+
+ if (matcher.find()) {
+ String elementType = matcher.group(0);
+
+ if (elementType != null && elementType.equals(INSERT_ARTIFACT_HERE)) {
+ if (artifact.isAttributeTypeValid(CoreAttributeTypes.ParagraphNumber)) {
+ String paragraphNum = artifact.getSoleAttributeValue(CoreAttributeTypes.ParagraphNumber, "");
+ if (Strings.isValid(paragraphNum)) {
+ startParagraphNumber = paragraphNum;
+ }
+ }
+ }
+ }
+
+ return startParagraphNumber;
+ }
+
+ /**
+ * Adds the beginning section of the template to our wordml builder, the end index of the matcher is returned as the
+ * lastEndIndex for the default implementation
+ */
+ protected int handleStartOfTemplate(WordMLProducer wordMl, String templateContent, Matcher matcher) {
+ wordMl.addWordMl(templateContent.substring(0, matcher.start()));
+ return matcher.end();
+ }
+
+ /**
+ * For the default version, now that the content has been inserted the rest of the render template's whole word
+ * content is appended to the end after the footer is updated
+ */
+ protected void handleEndOfTemplate(WordMLProducer wordMl, String templateContent, int lastEndIndex) {
+ String endOfTemplate = templateContent.substring(lastEndIndex);
+ // Write out the last of the template
+ wordMl.addWordMl(updateFooter(endOfTemplate));
+ }
+
+ /**
+ * Cleans up the final section of the template to fix styling
+ */
+ protected String updateFooter(String endOfTemplate) {
+ // footer cleanup
+ endOfTemplate = endOfTemplate.replaceAll(FTR, "");
+ endOfTemplate = endOfTemplate.replaceFirst(PAGE_SZ, CONTINUOUS + PG_SZ);
+ return endOfTemplate;
+ }
+
+ //--- ProcessContent Helper Methods ---//
+
+ //--- ProcessArtifact Helper Methods ---//
+
+ /**
+ * If outlining is enabled, this default method inserts the heading with the paragraph number for the artifact.
+ */
+ protected void setArtifactOutlining(ArtifactReadable artifact, WordMLProducer wordMl) {
+ AttributeTypeToken attrToken = AttributeTypeToken.valueOf(headingAttributeType.getIdString());
+ String headingText = artifact.getSoleAttributeAsString(attrToken, "");
+ CharSequence paragraphNumber = null;
+
+ paragraphNumber = wordMl.startOutlineSubSection("Times New Roman", headingText, null);
+ if (paragraphNumber == null) {
+ paragraphNumber = wordMl.startOutlineSubSection();
+ }
+ }
+
+ /**
+ * Loops through and processes each metadata item that was parsed from earlier when handling the rendererOptions.
+ */
+ protected void processMetadata(ArtifactReadable artifact, WordMLProducer wordMl) {
+ for (MetadataElement metadataElement : metadataElements) {
+ processMetadata(artifact, wordMl, metadataElement);
+ }
+ }
+
+ /**
+ * Adds the metadata element to the artifact, currently the default implementation ignores applicability
+ */
+ protected void processMetadata(ArtifactReadable artifact, WordMLProducer wordMl, MetadataElement element) {
+ wordMl.startParagraph();
+ String name = element.getType();
+ String format = element.getFormat();
+ String label = element.getLabel();
+ String value = "";
+ if (name.equals(APPLICABILITY)) {
+ //TODO Handle for when the meta data option is for applicability.
+ return;
+ } else if (name.equals(ARTIFACT_TYPE)) {
+ value = artifact.getArtifactType().getName();
+ } else if (name.equals(ARTIFACT_ID)) {
+ value = artifact.getIdString();
+ }
+ if (!format.isEmpty() || !label.isEmpty()) {
+ if (label.contains(">x<")) {
+ wordMl.addWordMl(label.replace(">x<", ">" + Xml.escape(name + ": ").toString() + "<"));
+ }
+ if (format.contains(">x<")) {
+ wordMl.addWordMl(format.replace(">x<", ">" + Xml.escape(value).toString() + "<"));
+ }
+ } else {
+ wordMl.addTextInsideParagraph(name + ": " + value);
+ }
+ wordMl.endParagraph();
+ }
+
+ /**
+ * Loops through each attribute element that is to be printed, if * (all attributes), it loops through every valid
+ * attribute on that artifact. Also makes sure not to print the headingAttributeType if outlining is enabled.
+ * Otherwise it only runs for the specific attribute element. In this default implementation the presentation type is
+ * preview and the footer is empty, assuming that the template will include its' own constant footer.
+ */
+ protected void processAttributes(ArtifactReadable artifact, WordMLProducer wordMl) {
+ for (AttributeElement attributeElement : attributeElements) {
+ String attributeName = attributeElement.getAttributeName();
+ if (publishingOptions.allAttributes || attributeName.equals("*")) {
+ for (AttributeTypeToken attributeType : getOrderedAttributeTypes(artifact.getValidAttributeTypes())) {
+ if (!outlining || attributeType.notEqual(headingAttributeType)) {
+ processAttribute(artifact, wordMl, attributeElement, attributeType, true, PREVIEW, "");
+ }
+ }
+ } else {
+ AttributeTypeToken attributeType = orcsApi.getOrcsTypes().getAttributeTypes().getByName(attributeName);
+ if (artifact.isAttributeTypeValid(attributeType)) {
+ processAttribute(artifact, wordMl, attributeElement, attributeType, false, PREVIEW, "");
+ }
+ }
+ }
+ }
+
+ /**
+ * Orders the attribute and moves any word/plain text content to the end of the attributes.
+ */
+ protected List<AttributeTypeToken> getOrderedAttributeTypes(Collection<AttributeTypeToken> attributeTypes) {
+ ArrayList<AttributeTypeToken> orderedAttributeTypes = new ArrayList<>(attributeTypes.size());
+ AttributeTypeToken contentType = null;
+
+ for (AttributeTypeToken attributeType : attributeTypes) {
+ if (attributeType.matches(WholeWordContent, WordTemplateContent, PlainTextContent)) {
+ contentType = attributeType;
+ } else {
+ orderedAttributeTypes.add(attributeType);
+ }
+ }
+
+ Collections.sort(orderedAttributeTypes);
+ if (contentType != null) {
+ orderedAttributeTypes.add(contentType);
+ }
+ return orderedAttributeTypes;
+ }
+
+ /**
+ * The default implementation does not render word ole data or relation order. This method gets the values for the
+ * attributes and calls renderWordTemplateContent if of type Word Template Content, renderAttribute if any other
+ * valid attribute
+ */
+ protected void processAttribute(ArtifactReadable artifact, WordMLProducer wordMl, AttributeElement attributeElement, AttributeTypeToken attributeType, boolean allAttrs, PresentationType presentationType, String footer) {
+ //Do not publish OleData or RelationOrder
+ if (attributeType.equals(CoreAttributeTypes.WordOleData) || attributeType.equals(
+ CoreAttributeTypes.RelationOrder)) {
+ return;
+ }
+
+ List<Object> attributes = artifact.getAttributeValues(attributeType);
+ if (attributeType.equals(WordTemplateContent)) {
+ renderWordTemplateContent(attributeType, artifact, presentationType, wordMl, attributeElement.getFormat(),
+ attributeElement.getLabel(), footer);
+ } else if (!attributes.isEmpty()) {
+ renderAttribute(attributeType, artifact, presentationType, wordMl, attributeElement.getFormat(),
+ attributeElement.getLabel(), footer);
+ }
+ }
+
+ /**
+ * This method derives from the WordTemplateRenderer on the client, used to render word template content attribute.
+ * Uses WordTemplateContentRendererHandler to render the word ml. Also handles OSEE_Link errors if there are
+ * artifacts that are linking to artifacts that aren't included in the publish.
+ */
+ protected void renderWordTemplateContent(AttributeTypeToken attributeType, ArtifactReadable artifact, PresentationType presentationType, WordMLProducer producer, String format, String label, String footer) {
+ WordMLProducer wordMl = producer;
+ String data = null;
+
+ LinkType linkType = publishingOptions.linkType;
+
+ if (label.length() > 0) {
+ wordMl.addParagraph(label);
+ }
+
+ TransactionToken txId = null;
+ if (artifact.isHistorical()) {
+ txId = orcsApi.getTransactionFactory().getTx(artifact.getTransaction());
+ } else {
+ txId = TransactionToken.SENTINEL;
+ }
+
+ WordTemplateContentData wtcData = new WordTemplateContentData();
+ wtcData.setArtId(artifact.getUuid());
+ wtcData.setBranch(artifact.getBranch());
+ wtcData.setFooter(footer);
+ wtcData.setIsEdit(presentationType == PresentationType.SPECIALIZED_EDIT);
+ wtcData.setLinkType(linkType != null ? linkType.toString() : null);
+ wtcData.setTxId(txId);
+ wtcData.setPresentationType(presentationType);
+ wtcData.setViewId(publishingOptions.view);
+ wtcData.setPermanentLinkUrl(new ArtifactUrlServer(orcsApi).getSelectedPermanentLinkUrl());
+
+ Pair<String, Set<String>> content = null;
+ try {
+ WordTemplateContentRendererHandler rendererHandler = new WordTemplateContentRendererHandler(orcsApi, logger);
+ content = rendererHandler.renderWordML(wtcData);
+ } catch (Exception ex) {
+ errorElements.add(new PublishingErrorElement(artifact.getId(), artifact.getName(), artifact.getArtifactType(),
+ ex.toString()));
+ }
+
+ if (content != null) {
+ data = content.getFirst();
+ processLinkErrors(artifact, data, content.getSecond());
+ }
+
+ if (data != null) {
+ wordMl.addWordMl(data);
+ } else if (footer != null) {
+ wordMl.addWordMl(footer);
+ }
+ wordMl.resetListValue();
+
+ }
+
+ /**
+ * For non word template content attributes, this method appends the attribute to the WordMLProducer.
+ */
+ protected void renderAttribute(AttributeTypeToken attributeType, ArtifactReadable artifact, PresentationType presentationType, WordMLProducer producer, String format, String label, String footer) {
+ WordMLProducer wordMl = producer;
+
+ wordMl.startParagraph();
+
+ if (publishingOptions.allAttributes) {
+ if (!attributeType.matches(CoreAttributeTypes.PlainTextContent)) {
+ wordMl.addWordMl("<w:r><w:t> " + Xml.escape(attributeType.getName()) + ": </w:t></w:r>");
+ } else {
+ wordMl.addWordMl("<w:r><w:t> </w:t></w:r>");
+ }
+ } else {
+ // assumption: the label is of the form <w:r><w:t> text </w:t></w:r>
+ wordMl.addWordMl(label);
+ }
+
+ String valueList = artifact.getAttributeValuesAsString(attributeType);
+ if (format.contains(">x<")) {
+ wordMl.addWordMl(format.replace(">x<", ">" + Xml.escape(valueList).toString() + "<"));
+ } else {
+ wordMl.addTextInsideParagraph(valueList);
+ }
+ wordMl.endParagraph();
+
+ }
+
+ //--- Error Handling Methods ---//
+ /**
+ * Once all of the content has been processed, any errors that have been logged are now appended to the wordml in
+ * their own end section.
+ */
+ protected void addErrorLogToWordMl(WordMLProducer wordMl) {
+ if (!errorElements.isEmpty()) {
+ wordMl.startErrorLog();
+ for (PublishingErrorElement error : errorElements) {
+ wordMl.addErrorRow(error.getArtId().toString(), error.getArtName(), error.getArtType().getName(),
+ error.getErrorDescription());
+ }
+ wordMl.endErrorLog();
+ }
+ }
+
+ /**
+ * When rendering word template content, this method keeps track of OSEE links in artifacts and the artifacts that
+ * are linked to. After processing all artifacts, hyperlinkedIds will contain any artifact that has an OSEE link to
+ * an artifact that was not published in the document.
+ */
+ protected void processLinkErrors(ArtifactReadable artifact, String data, Set<String> unknownIds) {
+ Pattern bookmarkHyperlinkPattern = Pattern.compile(
+ "(^<aml:annotation[^<>]+w:name=\"OSEE\\.[^\"]*\"[^<>]+w:type=\"Word\\.Bookmark\\.Start\\\"/>)|" + "(<w:instrText>\\s+HYPERLINK[^<>]+\"OSEE\\.[^\"]*\"\\s+</w:instrText>)");
+ Pattern oseeLinkPattern = Pattern.compile("\"OSEE\\.[^\"]*\"");
+ Matcher match = bookmarkHyperlinkPattern.matcher(data);
+ Matcher linkMatch = null;
+ String id = "";
+
+ if (!unknownIds.isEmpty()) {
+ String description = "Contains the following unknown GUIDs: " + unknownIds;
+ errorElements.add(
+ new PublishingErrorElement(artifact.getId(), artifact.getName(), artifact.getArtifactType(), description));
+ }
+
+ while (match.find()) {
+ String foundMatch = match.group(0);
+ if (Strings.isValid(foundMatch)) {
+ linkMatch = oseeLinkPattern.matcher(foundMatch);
+ if (linkMatch.find()) {
+ id = foundMatch.substring(linkMatch.start() + 6, linkMatch.end() - 1);
+ if (foundMatch.contains("Bookmark")) {
+ bookmarkedIds.add(id);
+ if (hyperlinkedIds.containsKey(id)) {
+ hyperlinkedIds.remove(id);
+ }
+ } else if (foundMatch.contains("HYPERLINK")) {
+ if (!bookmarkedIds.contains(id) && !hyperlinkedIds.containsKey(id)) {
+ hyperlinkedIds.put(id, artifact);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/org.eclipse.osee.define.rest/src/org/eclipse/osee/define/rest/publishing/MSWordTemplatePublisher.java b/plugins/org.eclipse.osee.define.rest/src/org/eclipse/osee/define/rest/publishing/MSWordTemplatePublisher.java
new file mode 100644
index 00000000000..310207c0223
--- /dev/null
+++ b/plugins/org.eclipse.osee.define.rest/src/org/eclipse/osee/define/rest/publishing/MSWordTemplatePublisher.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Boeing.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Boeing - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osee.define.rest.publishing;
+
+import static org.eclipse.osee.framework.core.enums.CoreArtifactTypes.Folder;
+import static org.eclipse.osee.framework.core.enums.CoreAttributeTypes.NativeContent;
+import static org.eclipse.osee.framework.core.enums.CoreAttributeTypes.WholeWordContent;
+import java.util.regex.Matcher;
+import org.eclipse.osee.define.api.PublishingErrorElement;
+import org.eclipse.osee.define.api.PublishingOptions;
+import org.eclipse.osee.framework.core.data.ArtifactId;
+import org.eclipse.osee.framework.core.util.WordMLProducer;
+import org.eclipse.osee.logger.Log;
+import org.eclipse.osee.orcs.OrcsApi;
+import org.eclipse.osee.orcs.data.ArtifactReadable;
+
+/**
+ * @author Branden W. Phillips
+ */
+public class MSWordTemplatePublisher extends AbstractMSWordTemplatePublisher {
+
+ /**
+ * Basic server publishing process. Using a head artifact to publish a hierarchy on the given template. Includes an
+ * error log to be printed on the end.
+ */
+ public MSWordTemplatePublisher(PublishingOptions publishingOptions, Log logger, OrcsApi orcsApi) {
+ super(publishingOptions, logger, orcsApi);
+ }
+
+ @Override
+ public String publish(ArtifactId artifactId, ArtifactId templateArtId) {
+ ArtifactReadable artifact = getArtifactHead(artifactId);
+ String template = setUpTemplateWithOptions(templateArtId);
+
+ if (!artifact.equals(ArtifactReadable.SENTINEL)) {
+ if (!template.equals("") && elementType.equals(ARTIFACT)) {
+ StringBuilder wordMlOutput = applyContentToTemplate(artifact, template);
+ return wordMlOutput.toString();
+ } else {
+ //TODO handle critical error for invalid template
+ return null;
+ }
+ } else {
+ //TODO handle critical error for invalid artifact head
+ return null;
+ }
+ }
+
+ @Override
+ protected StringBuilder applyContentToTemplate(ArtifactReadable headArtifact, String templateContent) {
+ StringBuilder strBuilder = new StringBuilder();
+ WordMLProducer wordMl = new WordMLProducer(strBuilder);
+
+ templateContent = setUpTemplateContent(wordMl, headArtifact, templateContent);
+
+ int lastEndIndex = 0;
+ Matcher matcher = headElementsPattern.matcher(templateContent);
+ while (matcher.find()) {
+ lastEndIndex = handleStartOfTemplate(wordMl, templateContent, matcher);
+ processContent(headArtifact, wordMl);
+ }
+
+ handleEndOfTemplate(wordMl, templateContent, lastEndIndex);
+
+ return strBuilder;
+ }
+
+ @Override
+ protected void processContent(ArtifactReadable headArtifact, WordMLProducer wordMl) {
+ processArtifact(headArtifact, wordMl);
+
+ addErrorLogToWordMl(wordMl);
+ }
+
+ @Override
+ protected void processArtifact(ArtifactReadable artifact, WordMLProducer wordMl) {
+ if (!artifact.isAttributeTypeValid(WholeWordContent) && !artifact.isAttributeTypeValid(NativeContent)) {
+ if (!processedArtifacts.contains(artifact)) {
+ boolean ignoreArtifact = (publishingOptions.excludeFolders && artifact.isOfType(Folder));
+ boolean startedSection = false;
+
+ if (!ignoreArtifact) {
+ if (outlining) {
+ setArtifactOutlining(artifact, wordMl);
+ startedSection = true;
+ }
+ processMetadata(artifact, wordMl);
+ processAttributes(artifact, wordMl);
+ }
+
+ if (recurseChildren) {
+ for (ArtifactReadable childArtifact : artifact.getChildren()) {
+ processArtifact(childArtifact, wordMl);
+ }
+ }
+
+ if (startedSection) {
+ wordMl.endOutlineSubSection();
+ }
+ processedArtifacts.add(artifact);
+ }
+ } else {
+ errorElements.add(new PublishingErrorElement(artifact.getId(), artifact.getName(), artifact.getArtifactType(),
+ "Only artifacts of type Word Template Content are supported in this case"));
+ }
+ }
+}

Back to the top