001package com.hfg.xml.msofficexml.xlsx.part;
002
003import java.io.BufferedInputStream;
004import java.util.List;
005
006import com.hfg.util.collection.BiHashMap;
007import com.hfg.util.collection.BiMap;
008import com.hfg.xml.XMLDoc;
009import com.hfg.xml.XMLNode;
010import com.hfg.xml.XMLTag;
011import com.hfg.xml.XMLUtil;
012import com.hfg.xml.msofficexml.part.OfficeXMLPart;
013import com.hfg.xml.msofficexml.xlsx.SsmlRelationshipType;
014import com.hfg.xml.msofficexml.xlsx.Xlsx;
015import com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlTextRun;
016import com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlXML;
017import com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlXMLTag;
018
019//------------------------------------------------------------------------------
020/**
021 Office Open XML format Excel shared string part.
022
023 @author J. Alex Taylor, hairyfatguy.com
024 */
025//------------------------------------------------------------------------------
026// com.hfg XML/HTML Coding Library
027//
028// This library is free software; you can redistribute it and/or
029// modify it under the terms of the GNU Lesser General Public
030// License as published by the Free Software Foundation; either
031// version 2.1 of the License, or (at your option) any later version.
032//
033// This library is distributed in the hope that it will be useful,
034// but WITHOUT ANY WARRANTY; without even the implied warranty of
035// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
036// Lesser General Public License for more details.
037//
038// You should have received a copy of the GNU Lesser General Public
039// License along with this library; if not, write to the Free Software
040// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
041//
042// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
043// jataylor@hairyfatguy.com
044//------------------------------------------------------------------------------
045
046public class SharedStringsPart extends OfficeXMLPart
047{
048   private XMLTag mSharedStringsTableTag;
049
050   private BiMap<String, Integer> mStringIndexMap = new BiHashMap<>();
051
052   //##########################################################################
053   // CONSTRUCTORS
054   //##########################################################################
055
056   //---------------------------------------------------------------------------
057   public SharedStringsPart(Xlsx inXlsx)
058   {
059      super(inXlsx);
060      setFile(SsmlXML.SHARED_STRINGS_FILE);
061
062      // Register the content type
063      inXlsx.getContentTypesPart().addOverride(this, SsmlContentType.SPREADSHEET_SHARED_STRINGS);
064
065      // Register the relationship
066      inXlsx.getWorkbookRelationshipPart().addRelationship(SsmlRelationshipType.SHARED_STRINGS, this);
067
068      mSharedStringsTableTag = new SsmlSharedStringsTable(inXlsx);
069      setRootNode(mSharedStringsTableTag);
070   }
071
072   //---------------------------------------------------------------------------
073   public SharedStringsPart(Xlsx inXlsx, BufferedInputStream inStream)
074   {
075      this(inXlsx);
076
077      XMLDoc doc = new XMLDoc(inStream);
078      mSharedStringsTableTag = (XMLTag) doc.getRootNode();
079
080
081      int index = 0;
082      for (XMLNode stringItemTag : mSharedStringsTableTag.getSubtagsByName(SsmlXML.STRING_ITEM))
083      {
084         // Strip out the content // TODO: Return complex content (text runs) in their native state
085         StringBuilder buffer = new StringBuilder();
086         for (XMLNode subtag : (List<XMLNode>) (Object) stringItemTag.getSubtags())
087         {
088            if (subtag.getTagName().equals(SsmlXML.TEXT.getLocalName()))
089            {
090               buffer.append(subtag.getContent());
091            }
092            else if (subtag.getTagName().equals(SsmlXML.TEXT_RUN.getLocalName()))
093            {
094               for (XMLNode textTag : subtag.getSubtagsByName(SsmlXML.TEXT))
095               {
096                  buffer.append(textTag.getContent());
097               }
098            }
099         }
100
101         // Using unescapeEntities() instead of getUnescapedContent() because we were observing additional escaping like &quot;
102         mStringIndexMap.put(XMLUtil.unescapeEntities(buffer.toString()), index++);
103      }
104   }
105
106   //##########################################################################
107   // PUBLIC METHODS
108   //##########################################################################
109
110   //---------------------------------------------------------------------------
111   public int defineString(SsmlTextRun inValue)
112   {
113      Integer index = mStringIndexMap.get(inValue.toXML());
114      if (null == index)
115      {
116         // It's a new string value
117         index = mStringIndexMap.size();
118         mStringIndexMap.put(inValue.toXML(), index);
119
120         XMLTag stringItemTag = new XMLTag(SsmlXML.STRING_ITEM);
121         stringItemTag.addSubtag(inValue);
122
123         mSharedStringsTableTag.addSubtag(stringItemTag);
124         mSharedStringsTableTag.setAttribute(SsmlXML.COUNT_ATT, mStringIndexMap.size());
125         mSharedStringsTableTag.setAttribute(SsmlXML.UNIQUE_COUNT_ATT, mStringIndexMap.size());
126      }
127
128      return index;
129   }
130
131   //---------------------------------------------------------------------------
132   public int defineString(List<SsmlTextRun> inValue)
133   {
134      StringBuilder xml = new StringBuilder();
135      for (SsmlTextRun run : inValue)
136      {
137         xml.append(run.toXML());
138      }
139
140      Integer index = mStringIndexMap.get(xml.toString());
141      if (null == index)
142      {
143         // It's a new string value
144         index = mStringIndexMap.size();
145         mStringIndexMap.put(xml.toString(), index);
146
147         XMLTag stringItemTag = new XMLTag(SsmlXML.STRING_ITEM);
148         stringItemTag.addSubtags(inValue);
149
150         mSharedStringsTableTag.addSubtag(stringItemTag);
151         mSharedStringsTableTag.setAttribute(SsmlXML.COUNT_ATT, mStringIndexMap.size());
152         mSharedStringsTableTag.setAttribute(SsmlXML.UNIQUE_COUNT_ATT, mStringIndexMap.size());
153      }
154
155      return index;
156   }
157
158   //---------------------------------------------------------------------------
159   public int defineString(String inValue)
160   {
161      Integer index = mStringIndexMap.get(inValue);
162      if (null == index)
163      {
164         // It's a new string value
165         index = mStringIndexMap.size();
166         mStringIndexMap.put(inValue, index);
167
168         XMLTag textTag = new XMLTag(SsmlXML.TEXT);
169         textTag.setContent(inValue);
170
171         XMLTag stringItemTag = new XMLTag(SsmlXML.STRING_ITEM);
172         stringItemTag.addSubtag(textTag);
173
174         mSharedStringsTableTag.addSubtag(stringItemTag);
175         mSharedStringsTableTag.setAttribute(SsmlXML.COUNT_ATT, mStringIndexMap.size());
176         mSharedStringsTableTag.setAttribute(SsmlXML.UNIQUE_COUNT_ATT, mStringIndexMap.size());
177      }
178
179      return index;
180   }
181
182   //---------------------------------------------------------------------------
183   public String getString(int inIndex)
184   {
185      return mStringIndexMap.getKey(inIndex);
186   }
187
188
189   private class SsmlSharedStringsTable extends SsmlXMLTag
190   {
191      //---------------------------------------------------------------------------
192      public SsmlSharedStringsTable(Xlsx inXlsx)
193      {
194         super(SsmlXML.SHARED_STRING_TABLE, inXlsx);
195      }
196   }
197}