1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
21 #include <com/sun/star/container/XEnumerationAccess.hpp>
22 #include <com/sun/star/frame/XModel.hpp>
23 #include <com/sun/star/uno/XComponentContext.hpp>
24 #include <com/sun/star/document/XTypeDetection.hpp>
25 
26 #include <tools/urlobj.hxx>
27 
28 #include "excelvbahelper.hxx"
29 #include "vbaworkbook.hxx"
30 #include "vbaworkbooks.hxx"
31 #include <vbahelper/vbahelper.hxx>
32 
33 #include <osl/file.hxx>
34 using namespace ::ooo::vba;
35 using namespace ::com::sun::star;
36 
37 const sal_Int16 CUSTOM_CHAR = 5;
38 
39 static uno::Any
getWorkbook(const uno::Reference<uno::XComponentContext> & xContext,const uno::Reference<sheet::XSpreadsheetDocument> & xDoc,const uno::Reference<XHelperInterface> & xParent)40 getWorkbook( const uno::Reference< uno::XComponentContext >& xContext,
41              const uno::Reference< sheet::XSpreadsheetDocument > &xDoc,
42              const uno::Reference< XHelperInterface >& xParent )
43 {
44     // FIXME: fine as long as ScVbaWorkbook is stateless ...
45     uno::Reference< frame::XModel > xModel( xDoc, uno::UNO_QUERY );
46     if( !xModel.is() )
47         return uno::Any();
48 
49     uno::Reference< excel::XWorkbook > xWb( getVBADocument( xModel ), uno::UNO_QUERY );
50     if ( xWb.is() )
51     {
52         return uno::Any( xWb );
53     }
54 
55     ScVbaWorkbook *pWb = new ScVbaWorkbook( xParent, xContext, xModel );
56     return uno::Any( uno::Reference< excel::XWorkbook > (pWb) );
57 }
58 
59 class WorkBookEnumImpl : public EnumerationHelperImpl
60 {
61 public:
62     /// @throws uno::RuntimeException
WorkBookEnumImpl(const uno::Reference<XHelperInterface> & xParent,const uno::Reference<uno::XComponentContext> & xContext,const uno::Reference<container::XEnumeration> & xEnumeration)63     WorkBookEnumImpl( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XEnumeration >& xEnumeration ) : EnumerationHelperImpl( xParent, xContext, xEnumeration ) {}
64 
nextElement()65     virtual uno::Any SAL_CALL nextElement(  ) override
66     {
67         uno::Reference< sheet::XSpreadsheetDocument > xDoc( m_xEnumeration->nextElement(), uno::UNO_QUERY_THROW );
68         return getWorkbook( m_xContext, xDoc, m_xParent );
69     }
70 
71 };
72 
ScVbaWorkbooks(const uno::Reference<XHelperInterface> & xParent,const uno::Reference<css::uno::XComponentContext> & xContext)73 ScVbaWorkbooks::ScVbaWorkbooks( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< css::uno::XComponentContext >& xContext ) : ScVbaWorkbooks_BASE( xParent, xContext, VbaDocumentsBase::EXCEL_DOCUMENT )
74 {
75 }
76 // XEnumerationAccess
77 uno::Type
getElementType()78 ScVbaWorkbooks::getElementType()
79 {
80     return cppu::UnoType<excel::XWorkbook>::get();
81 }
82 uno::Reference< container::XEnumeration >
createEnumeration()83 ScVbaWorkbooks::createEnumeration()
84 {
85     // #FIXME its possible the WorkBookEnumImpl here doesn't reflect
86     // the state of this object ( although it should ) would be
87     // safer to create an enumeration based on this objects state
88     // rather than one effectively based of the desktop component
89     uno::Reference< container::XEnumerationAccess > xEnumerationAccess( m_xIndexAccess, uno::UNO_QUERY_THROW );
90     return new WorkBookEnumImpl( mxParent, mxContext, xEnumerationAccess->createEnumeration() );
91 }
92 
93 uno::Any
createCollectionObject(const css::uno::Any & aSource)94 ScVbaWorkbooks::createCollectionObject( const css::uno::Any& aSource )
95 {
96     uno::Reference< sheet::XSpreadsheetDocument > xDoc( aSource, uno::UNO_QUERY_THROW );
97     return getWorkbook( mxContext, xDoc, mxParent );
98 }
99 
100 uno::Any SAL_CALL
Add(const uno::Any & Template)101 ScVbaWorkbooks::Add( const uno::Any& Template )
102 {
103     uno::Reference< sheet::XSpreadsheetDocument > xSpreadDoc;
104     sal_Int32 nWorkbookType = 0;
105     OUString aTemplateFileName;
106     if( Template >>= nWorkbookType )
107     {
108         // nWorkbookType is a constant from XlWBATemplate (added in Excel 2007)
109         // TODO: create chart-sheet if supported by Calc
110 
111         xSpreadDoc.set( createDocument(), uno::UNO_QUERY_THROW );
112         // create a document with one sheet only
113         uno::Reference< sheet::XSpreadsheets > xSheets( xSpreadDoc->getSheets(), uno::UNO_SET_THROW );
114         uno::Reference< container::XIndexAccess > xSheetsIA( xSheets, uno::UNO_QUERY_THROW );
115         while( xSheetsIA->getCount() > 1 )
116         {
117             uno::Reference< container::XNamed > xSheetName( xSheetsIA->getByIndex( xSheetsIA->getCount() - 1 ), uno::UNO_QUERY_THROW );
118             xSheets->removeByName( xSheetName->getName() );
119         }
120     }
121     else if( Template >>= aTemplateFileName )
122     {
123         // TODO: create document from template
124         xSpreadDoc.set( createDocument(), uno::UNO_QUERY_THROW );
125     }
126     else if( !Template.hasValue() )
127     {
128         // regular spreadsheet document with configured number of sheets
129         xSpreadDoc.set( createDocument(), uno::UNO_QUERY_THROW );
130     }
131     else
132     {
133         // illegal argument
134         throw uno::RuntimeException();
135     }
136 
137     // need to set up the document modules ( and vba mode ) here
138     excel::setUpDocumentModules( xSpreadDoc );
139     if( xSpreadDoc.is() )
140         return getWorkbook( mxContext, xSpreadDoc, mxParent );
141     return uno::Any();
142 }
143 
144 void SAL_CALL
Close()145 ScVbaWorkbooks::Close()
146 {
147 }
148 
149 bool
isTextFile(const OUString & sType)150 ScVbaWorkbooks::isTextFile( const OUString& sType )
151 {
152     // will return true if the file is
153     // a) a variant of a text file
154     // b) a csv file
155     // c) unknown
156     // returning true basically means treat this like a csv file
157     return sType == "generic_Text" || sType.isEmpty();
158 }
159 
160 bool
isSpreadSheetFile(const OUString & sType)161 ScVbaWorkbooks::isSpreadSheetFile( const OUString& sType )
162 {
163     // include calc_QPro etc. ? ( not for the moment anyway )
164     return sType.startsWith( "calc_MS" )
165       || sType.startsWith( "MS Excel" )
166       || sType.startsWith( "calc8" )
167       || sType.startsWith( "calc_StarOffice" );
168 }
169 
170 OUString
getFileFilterType(const OUString & rFileName)171 ScVbaWorkbooks::getFileFilterType( const OUString& rFileName )
172 {
173     uno::Reference< document::XTypeDetection > xTypeDetect( mxContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", mxContext), uno::UNO_QUERY_THROW );
174     uno::Sequence< beans::PropertyValue > aMediaDesc(1);
175     aMediaDesc[ 0 ].Name = "URL";
176     aMediaDesc[ 0 ].Value <<= rFileName;
177     OUString sType = xTypeDetect->queryTypeByDescriptor( aMediaDesc, true );
178     return sType;
179 }
180 
181 // #TODO# #FIXME# can any of the unused params below be used?
182 uno::Any SAL_CALL
Open(const OUString & rFileName,const uno::Any &,const uno::Any & ReadOnly,const uno::Any & Format,const uno::Any &,const uno::Any &,const uno::Any &,const uno::Any &,const uno::Any & Delimiter,const uno::Any &,const uno::Any &,const uno::Any &,const uno::Any &)183 ScVbaWorkbooks::Open( const OUString& rFileName, const uno::Any& /*UpdateLinks*/, const uno::Any& ReadOnly, const uno::Any& Format, const uno::Any& /*Password*/, const uno::Any& /*WriteResPassword*/, const uno::Any& /*IgnoreReadOnlyRecommended*/, const uno::Any& /*Origin*/, const uno::Any& Delimiter, const uno::Any& /*Editable*/, const uno::Any& /*Notify*/, const uno::Any& /*Converter*/, const uno::Any& /*AddToMru*/ )
184 {
185     // we need to detect if this is a URL, if not then assume it's a file path
186     OUString aURL;
187     INetURLObject aObj;
188     aObj.SetURL( rFileName );
189     bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
190     if ( bIsURL )
191         aURL = rFileName;
192     else
193         osl::FileBase::getFileURLFromSystemPath( rFileName, aURL );
194 
195     uno::Sequence< beans::PropertyValue > sProps(0);
196 
197     OUString sType = getFileFilterType( aURL );
198     // A text file means it needs to be processed as a csv file
199     if ( isTextFile( sType ) )
200     {
201         sal_Int32 nIndex = 0;
202         // Values for format
203         // 1 Tabs
204         // 2 Commas
205         // 3 Spaces
206         // 4 Semicolons
207         // 5 Nothing
208         // 6 Custom character (see the Delimiter argument
209         // no format means use the current delimiter
210         sProps.realloc( 3 );
211         sProps[ nIndex ].Name = "FilterOptions";
212         sal_Int16 const delims[] { 0 /*default not used*/, 9/*tab*/, 44/*comma*/, 32/*space*/, 59/*semicolon*/ };
213 
214         OUString sFormat;
215         sal_Int16 nFormat = 0; // default indicator
216 
217         if ( Format.hasValue() )
218         {
219             Format >>= nFormat; // val of nFormat overwritten if extracted
220             // validate param
221             if ( nFormat < 1 || nFormat > 6 )
222                 throw uno::RuntimeException("Illegal value for Format" );
223         }
224 
225         sal_Int16 nDelim = getCurrentDelim();
226 
227         if (  nFormat > 0 && nFormat < CUSTOM_CHAR )
228         {
229             nDelim =  delims[ nFormat ];
230         }
231         else if ( nFormat > CUSTOM_CHAR )
232         {
233             // Need to check Delimiter param
234             if ( !Delimiter.hasValue() )
235                 throw uno::RuntimeException("Expected value for Delimiter" );
236             OUString sStr;
237             Delimiter >>= sStr;
238             if ( sStr.isEmpty() )
239                 throw uno::RuntimeException("Incorrect value for Delimiter" );
240 
241             nDelim = sStr[0];
242 
243         }
244 
245         getCurrentDelim() = nDelim; //set new current
246 
247         sFormat = OUString::number( nDelim ) + ",34,0,1";
248         sProps[ nIndex++ ].Value <<= sFormat;
249         sProps[ nIndex ].Name = "FilterName";
250         sProps[ nIndex++ ].Value <<= OUString( SC_TEXT_CSV_FILTER_NAME );
251         // Ensure WORKAROUND_CSV_TXT_BUG_i60158 gets called in typedetection.cxx so
252         // csv is forced for deep detected 'writerxxx' types
253         sProps[ nIndex ].Name = "DocumentService";
254         sProps[ nIndex ].Value <<= OUString("com.sun.star.sheet.SpreadsheetDocument");
255     }
256     else if ( !isSpreadSheetFile( sType ) )
257         throw uno::RuntimeException("Bad Format" );
258 
259     uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( openDocument( rFileName, ReadOnly, sProps ), uno::UNO_QUERY_THROW );
260     uno::Any aRet = getWorkbook( mxContext, xSpreadDoc, mxParent );
261     uno::Reference< excel::XWorkbook > xWBook( aRet, uno::UNO_QUERY );
262     if ( xWBook.is() )
263         xWBook->Activate();
264     return aRet;
265 }
266 
267 OUString
getServiceImplName()268 ScVbaWorkbooks::getServiceImplName()
269 {
270     return "ScVbaWorkbooks";
271 }
272 
273 css::uno::Sequence<OUString>
getServiceNames()274 ScVbaWorkbooks::getServiceNames()
275 {
276     static uno::Sequence< OUString > const sNames
277     {
278         "ooo.vba.excel.Workbooks"
279     };
280     return sNames;
281 }
282 
283 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
284