001package com.hfg.xml.msofficexml;
002
003
004import java.io.BufferedReader;
005import java.io.InputStream;
006import java.io.InputStreamReader;
007import java.io.Reader;
008import java.util.ArrayList;
009import java.util.List;
010import java.util.logging.Level;
011import java.util.logging.Logger;
012import java.util.zip.GZIPInputStream;
013import java.util.zip.ZipEntry;
014import java.util.zip.ZipInputStream;
015
016import com.hfg.util.StringBuilderPlus;
017import com.hfg.util.StringUtil;
018import com.hfg.xml.XMLException;
019import com.hfg.xml.msofficexml.docx.wordprocessingml.WmlXML;
020import com.hfg.xml.msofficexml.xlsx.spreadsheetml.SsmlXML;
021import com.hfg.xml.xsd.Xsd;
022
023//------------------------------------------------------------------------------
024/**
025 XML xsd specification for OfficeOpenXml files. This information is need when
026 producing OfficeOpenXml documents because, for some reason, MS Office applications
027 are extremely picky about XML tag order.
028 <div>
029 xsd files downloaded from:
030 <a href='http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-376,%20Fourth%20Edition,%20Part%201%20-%20Fundamentals%20And%20Markup%20Language%20Reference.zip'>
031   http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-376,%20Fourth%20Edition,%20Part%201%20-%20Fundamentals%20And%20Markup%20Language%20Reference.zip
032 </a>
033 </div>
034 <div>
035  @author J. Alex Taylor, hairyfatguy.com
036 </div>
037 */
038//------------------------------------------------------------------------------
039// com.hfg XML/HTML Coding Library
040//
041// This library is free software; you can redistribute it and/or
042// modify it under the terms of the GNU Lesser General Public
043// License as published by the Free Software Foundation; either
044// version 2.1 of the License, or (at your option) any later version.
045//
046// This library is distributed in the hope that it will be useful,
047// but WITHOUT ANY WARRANTY; without even the implied warranty of
048// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
049// Lesser General Public License for more details.
050//
051// You should have received a copy of the GNU Lesser General Public
052// License along with this library; if not, write to the Free Software
053// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
054//
055// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
056// jataylor@hairyfatguy.com
057//------------------------------------------------------------------------------
058
059public class OfficeOpenXmlXsd extends Xsd
060{
061   private static OfficeOpenXmlXsd sInstance;
062
063   private static final List<String> sXsdResources;
064   static
065   {
066      sXsdResources = new ArrayList<>(5);
067      sXsdResources.add("rsrc/OfficeOpenXML-XMLSchema-Strict.zip");
068      // From https://docs.microsoft.com/en-us/openspecs/office_standards/ms-xlsx/aa12452a-467d-4192-8ebf-0a90d86dd64b
069      sXsdResources.add("rsrc/xlbasictypes.xsd.gz");  // http://schemas.microsoft.com/office/excel/2006/main
070      sXsdResources.add("rsrc/xlslicercache.xsd.gz"); // http://schemas.microsoft.com/office/spreadsheetml/2009/9/main
071      sXsdResources.add("rsrc/xl15.xsd.gz");          // http://schemas.microsoft.com/office/spreadsheetml/2010/11/main
072      sXsdResources.add("rsrc/xlpivot16.xsd.gz");     // http://schemas.microsoft.com/office/spreadsheetml/2014/11/main
073   }
074
075   private final static Logger LOGGER = Logger.getLogger(OfficeOpenXmlXsd.class.getPackage().getName());
076
077
078   //###########################################################################
079   // CONSTRUCTORS
080   //###########################################################################
081
082   //---------------------------------------------------------------------------
083   private OfficeOpenXmlXsd()
084         throws Exception
085   {
086      super();
087
088      // If we don't start by initializing namespaces then some parsed xsd data will lack a prefix
089      // and values can collide.
090      SsmlXML.initializeNamespaces();
091      WmlXML.initializeNamespaces();
092
093      for (String rsrc : sXsdResources)
094      {
095         InputStream is = OfficeXML.class.getResourceAsStream(rsrc);
096         if (null == is)
097         {
098            throw new RuntimeException("The resource " + StringUtil.singleQuote(rsrc) + " couldn't be found!");
099         }
100
101         if (rsrc.endsWith(".zip"))
102         {
103            ZipInputStream zis = new ZipInputStream(is);
104            ZipEntry zipEntry;
105            while ((zipEntry = zis.getNextEntry()) != null)
106            {
107               LOGGER.log(Level.FINE, "Extracting: " + zipEntry + " ...");
108
109               Reader reader = new BufferedReader(new InputStreamReader(zis));
110               parse(reader);
111
112               zis.closeEntry();
113            }
114         }
115         else
116         {
117            if (rsrc.endsWith(".gz"))
118            {
119               is = new GZIPInputStream(is);
120            }
121
122            LOGGER.log(Level.FINE, "Extracting: " + rsrc + " ...");
123            Reader reader = new BufferedReader(new InputStreamReader(is));
124            parse(reader);
125         }
126      }
127
128      integrateTypesWithElements();
129   }
130
131   //###########################################################################
132   // PUBLIC METHODS
133   //###########################################################################
134
135   //---------------------------------------------------------------------------
136   public synchronized static OfficeOpenXmlXsd getInstance()
137   {
138      if (null == sInstance)
139      {
140         try
141         {
142            sInstance = new OfficeOpenXmlXsd();
143         }
144         catch (Exception e)
145         {
146            throw new XMLException("Problem initializing the OfficeOpenXML XSD info!", e);
147         }
148      }
149
150      return sInstance;
151   }
152
153   //---------------------------------------------------------------------------
154   public static Logger getLogger()
155   {
156      return LOGGER;
157   }
158
159   //---------------------------------------------------------------------------
160   public CharSequence getConfigSummary()
161   {
162      StringBuilderPlus buffer = new StringBuilderPlus();
163      buffer.appendln("Loaded XSD resources:");
164      for (String rsrc : sXsdResources)
165      {
166         buffer.appendln("  " + rsrc);
167      }
168      buffer.appendln();
169      buffer.appendln(super.getConfigSummary());
170
171      return buffer;
172   }
173
174}