| /***************************************************************************** |
| * (c) Copyright 2016 Telefonaktiebolaget LM Ericsson |
| * |
| * |
| * 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: |
| * Antonio Campesino (Ericsson) antonio.campesino.robles@ericsson.com - Initial API and implementation, |
| * Bug 478883 |
| * |
| *****************************************************************************/ |
| package org.eclipse.gendoc.services.xlsx; |
| |
| import java.io.File; |
| import java.io.StringWriter; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Stack; |
| import java.util.regex.Pattern; |
| |
| import javax.xml.namespace.NamespaceContext; |
| import javax.xml.transform.OutputKeys; |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerConfigurationException; |
| import javax.xml.transform.TransformerException; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.transform.dom.DOMSource; |
| import javax.xml.transform.stream.StreamResult; |
| |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.gendoc.document.parser.documents.Document; |
| import org.eclipse.gendoc.document.parser.documents.Document.CONFIGURATION; |
| import org.eclipse.gendoc.document.parser.documents.XMLParser; |
| import org.eclipse.gendoc.document.parser.xlsx.XLSXDocument; |
| import org.eclipse.gendoc.document.parser.xlsx.XLSXNamespaceContext; |
| import org.eclipse.gendoc.document.parser.xlsx.XLSXParser; |
| import org.eclipse.gendoc.documents.IAdditionalResourceService; |
| import org.eclipse.gendoc.documents.IDocumentService; |
| import org.eclipse.gendoc.documents.ITableService; |
| import org.eclipse.gendoc.documents.XMLDocumentService; |
| import org.eclipse.gendoc.services.exception.DocumentServiceException; |
| import org.eclipse.gendoc.services.exception.InvalidContentException; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| // TODO: Provide a common ooxml abstract implementation of XMLDocumentService |
| // TODO: Some tags need to delegate to services associated to the tag (such as IHTMLService) which need to delegate to |
| // an extension point and to a default implementation of the service for cases where the document type does not support |
| // the function provided by the tag / service: |
| // - IAdditionalResourceService |
| // - IHtmlService (RichTextTag) |
| |
| public class XLSXDocumentService extends XMLDocumentService implements IDocumentService { |
| private static final boolean DEBUG = System.getProperty("debug") != null; |
| private String serviceId; |
| private XLSXAdditionalResourceService additionalResourceService = new XLSXAdditionalResourceService(); |
| |
| public XLSXDocumentService() { |
| // TODO Auto-generated constructor stub |
| } |
| |
| @Override |
| public String getServiceId () |
| { |
| return this.serviceId; |
| } |
| |
| @Override |
| public void setServiceId (String serviceId) |
| { |
| this.serviceId = serviceId; |
| } |
| |
| |
| @Override |
| public void saveDocument(Document doc, String path) throws DocumentServiceException { |
| if (!(doc instanceof XLSXDocument)) { |
| throw new DocumentServiceException("Document is not a valid DOCX document."); |
| } |
| |
| XLSXDocument document = (XLSXDocument)doc; |
| try |
| { |
| // back to the beginning |
| document.jumpToStart(); |
| do |
| { |
| DOMSource domSource = new DOMSource(document.getXMLParser().getDocument()); |
| if (document.getXMLParser().getKind() == CONFIGURATION.content) { |
| if (document.getXMLParser() instanceof XLSXParser) { |
| XLSXParser xlsxParser = (XLSXParser)document.getXMLParser(); |
| xlsxParser.handleDropCellReferences(); |
| } |
| |
| StreamResult fluxDestination = new StreamResult(new File( |
| document.getUnzipLocationDocumentFile().getAbsolutePath() + |
| "/xl/worksheets/" + document.getXMLParser().getXmlFile().getName())); |
| TransformerFactory transformBuilder = TransformerFactory.newInstance(); |
| |
| Transformer transformer = transformBuilder.newTransformer(); |
| transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); |
| transformer.setOutputProperty(OutputKeys.INDENT, "no"); |
| transformer.transform(domSource, fluxDestination); |
| |
| if (DEBUG) { |
| StringWriter swr = new StringWriter(); |
| transformer.setOutputProperty(OutputKeys.INDENT, "yes"); |
| transformer.transform(domSource, new StreamResult(swr)); |
| swr.flush(); |
| Activator.getDefault().getLog().log(new Status( |
| IStatus.INFO, Activator.PLUGIN_ID, |
| "/xl/worksheets/" + document.getXMLParser().getXmlFile().getName()+":\n"+ |
| swr.toString())); |
| } |
| } |
| } |
| while (document.jumpToNextFile()); |
| |
| for (XMLParser p : document.getSubdocuments()) { |
| DOMSource domSource = new DOMSource(p.getDocument()); |
| StreamResult fluxDestination = new StreamResult(p.getXmlFile()); |
| TransformerFactory transformBuilder = TransformerFactory.newInstance(); |
| |
| Transformer transformer = transformBuilder.newTransformer(); |
| transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); |
| transformer.setOutputProperty(OutputKeys.INDENT, "no"); |
| transformer.transform(domSource, fluxDestination); |
| if (DEBUG) { |
| StringWriter swr = new StringWriter(); |
| transformer.setOutputProperty(OutputKeys.INDENT, "yes"); |
| transformer.transform(domSource, new StreamResult(swr)); |
| swr.flush(); |
| Activator.getDefault().getLog().log(new Status( |
| IStatus.INFO, Activator.PLUGIN_ID, |
| p.getXmlFile().getAbsolutePath().replace( |
| document.getUnzipLocationDocumentFile().getAbsolutePath(),"")+":\n"+ |
| swr.toString())); |
| } |
| } |
| |
| ((XLSXDocument) document).zipToLocation(path); |
| } |
| catch (TransformerConfigurationException e1) |
| { |
| e1.printStackTrace(); |
| } |
| catch (TransformerException e2) |
| { |
| e2.printStackTrace(); |
| } |
| } |
| |
| @Override |
| public String getListLabel() { |
| return null; |
| } |
| |
| @Override |
| public boolean isList(String label) { |
| return false; |
| } |
| |
| @Override |
| public String getListId(Node n) { |
| return null; |
| } |
| |
| @Override |
| public String getContinueList(Node currentNode, String idList) throws InvalidContentException { |
| return null; |
| } |
| |
| @Override |
| public boolean isListItem(String label) { |
| return false; |
| } |
| |
| @Override |
| public String getTableLabel() { |
| return null; |
| } |
| |
| @Override |
| public boolean isTable(String label) { |
| return false; |
| } |
| |
| @Override |
| public boolean isRow(String label) { |
| return false; |
| } |
| |
| @Override |
| public boolean isCell(String label) { |
| return false; |
| } |
| |
| // TODO: The nobr tag can not use pattern matching as the xlsx format use <t> tags with |
| // xml:space="preserve" or by default preserve blanks. |
| // A mergeParagraphs(...) function may be needed on the DocumentService interface => IDocumentService2 |
| @Override |
| public Pattern getNobrReplacePattern() |
| { |
| return null; |
| } |
| |
| @Override |
| public String format(String input) { |
| // The <t> tags preserve blanks. |
| return input; |
| } |
| |
| @Override |
| public NamespaceContext getNameSpaceContext() { |
| return new XLSXNamespaceContext(); |
| } |
| |
| @Override |
| public String getNamingSpaceURL() { |
| return "xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\""; |
| } |
| |
| @Override |
| public String getTextStyle() { |
| return "t"; |
| } |
| |
| @Override |
| public boolean isPara(String label) { |
| return label.equals("row"); |
| } |
| |
| @Override |
| // It is not needed as XLSX use default namespaces |
| public String[] getTextTagLabels() { |
| return null; |
| } |
| |
| @Override |
| // TODO: Needed for insert images. |
| public IAdditionalResourceService getAdditionalResourceService() { |
| return additionalResourceService; |
| } |
| |
| @Override |
| protected Node cleanTags(Node currentNode, List<String> tagLabels, Node baseNode) throws InvalidContentException { |
| if (baseNode == null) |
| { |
| return null; |
| } |
| |
| // 2. Check that this node contains the start of a valid tag label |
| StringBuffer newNodeContent = new StringBuffer(extractNodeTextValue(baseNode)); |
| while (baseNode != null && !containsOneOf(tagLabels, newNodeContent.toString())) |
| |
| { |
| baseNode = findNodeWithStartTag(baseNode, currentNode); |
| if (baseNode != null) |
| { |
| newNodeContent = new StringBuffer(extractNodeTextValue(baseNode)); |
| } |
| } |
| if (baseNode == null) |
| { |
| return null; |
| } |
| // 3. Base node is found AND matches a valid tag => Check tag closure |
| |
| boolean isCompleteTag = containsFullTags(newNodeContent.toString(), tagLabels); |
| |
| // 4. If tag not closed : |
| if (!isCompleteTag) |
| { |
| // Find all nodes matching the base node label |
| NodeList followingNodes = getNextNodes(baseNode, baseNode.getNodeName()); |
| List<Node> nodesToRemove = new ArrayList<Node>(); |
| |
| if (followingNodes != null) |
| { |
| // Append text values of all these nodes until tag closure is found |
| for (int i = 0; i < followingNodes.getLength(); i++) |
| { |
| String textValue = extractNodeTextValue(followingNodes.item(i)); |
| newNodeContent.append(textValue); |
| Node nodeToRemove = getBestAscendantUntil(currentNode, followingNodes.item(i)); |
| if (!nodesToRemove.contains(nodeToRemove)) |
| { |
| nodesToRemove.add(nodeToRemove); |
| } |
| if (containsFullTags(newNodeContent.toString(), tagLabels)) |
| { |
| isCompleteTag = true; |
| break; |
| } |
| |
| } |
| |
| // Remove all nodes that are not useful anymore from initial current Node |
| for (Node nodeToRemove : nodesToRemove) |
| { |
| if (nodeToRemove != null && currentNode.equals(nodeToRemove.getParentNode())) |
| { |
| currentNode.removeChild(nodeToRemove); |
| } |
| } |
| } |
| } |
| // Replace content of base node with the text stored in "textContent" variable |
| String[] separated = asText(baseNode).split(XML_TAG_START + "|" + XML_TAG_END); |
| if (separated != null && separated.length > 1) |
| { |
| newNodeContent.insert(0, XML_TAG_START + separated[1] + XML_TAG_END); |
| newNodeContent.append(XML_TAG_START + separated[separated.length - 1] + XML_TAG_END); |
| } |
| else |
| { |
| newNodeContent.append(asText(baseNode)); |
| } |
| // Replace invalid characters |
| String nodeContent = cleanXMLContent(newNodeContent.toString()); |
| |
| // Replace base node by the value of the buffer |
| Node result = injectNode(baseNode, nodeContent); |
| baseNode.getParentNode().removeChild(baseNode); |
| return result; |
| } |
| |
| @Override |
| protected boolean areSimilarTags(String tagName1, String tagName2) { |
| return false; |
| } |
| |
| @Override |
| protected String containsSimilarTag(Stack<String> tagStack, String tagName) { |
| return null; |
| } |
| |
| @Override |
| public String getRowLabel() { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| @Override |
| public String getCellLabel() { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| @Override |
| public ITableService getTableService() { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| |
| } |