1*b3f79822SAndrew Rist /************************************************************** 2*b3f79822SAndrew Rist * 3*b3f79822SAndrew Rist * Licensed to the Apache Software Foundation (ASF) under one 4*b3f79822SAndrew Rist * or more contributor license agreements. See the NOTICE file 5*b3f79822SAndrew Rist * distributed with this work for additional information 6*b3f79822SAndrew Rist * regarding copyright ownership. The ASF licenses this file 7*b3f79822SAndrew Rist * to you under the Apache License, Version 2.0 (the 8*b3f79822SAndrew Rist * "License"); you may not use this file except in compliance 9*b3f79822SAndrew Rist * with the License. You may obtain a copy of the License at 10*b3f79822SAndrew Rist * 11*b3f79822SAndrew Rist * http://www.apache.org/licenses/LICENSE-2.0 12*b3f79822SAndrew Rist * 13*b3f79822SAndrew Rist * Unless required by applicable law or agreed to in writing, 14*b3f79822SAndrew Rist * software distributed under the License is distributed on an 15*b3f79822SAndrew Rist * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16*b3f79822SAndrew Rist * KIND, either express or implied. See the License for the 17*b3f79822SAndrew Rist * specific language governing permissions and limitations 18*b3f79822SAndrew Rist * under the License. 19*b3f79822SAndrew Rist * 20*b3f79822SAndrew Rist *************************************************************/ 21*b3f79822SAndrew Rist 22cdf0e10cSrcweir #include "cppuhelper/bootstrap.hxx" 23cdf0e10cSrcweir 24cdf0e10cSrcweir #include <com/sun/star/beans/Property.hpp> 25cdf0e10cSrcweir #include <com/sun/star/beans/XPropertySet.hpp> 26cdf0e10cSrcweir #include <com/sun/star/beans/XPropertySetInfo.hpp> 27cdf0e10cSrcweir #include <com/sun/star/container/XNameAccess.hpp> 28cdf0e10cSrcweir #include <com/sun/star/container/XNameContainer.hpp> 29cdf0e10cSrcweir #include <com/sun/star/frame/XComponentLoader.hpp> 30cdf0e10cSrcweir #include <com/sun/star/lang/XMultiComponentFactory.hpp> 31cdf0e10cSrcweir #include <com/sun/star/sheet/XSpreadsheet.hpp> 32cdf0e10cSrcweir #include <com/sun/star/sheet/XSpreadsheetDocument.hpp> 33cdf0e10cSrcweir #include <com/sun/star/util/XCloseable.hpp> 34cdf0e10cSrcweir #include <com/sun/star/uno/XComponentContext.hpp> 35cdf0e10cSrcweir #include <com/sun/star/ucb/XSimpleFileAccess.hpp> 36cdf0e10cSrcweir #include <com/sun/star/script/provider/XScriptProviderSupplier.hpp> 37cdf0e10cSrcweir #include <com/sun/star/document/XTypeDetection.hpp> 38cdf0e10cSrcweir 39cdf0e10cSrcweir #include <tools/urlobj.hxx> 40cdf0e10cSrcweir #include <osl/file.hxx> 41cdf0e10cSrcweir 42cdf0e10cSrcweir #include <memory> 43cdf0e10cSrcweir #include <iostream> 44cdf0e10cSrcweir 45cdf0e10cSrcweir using namespace ::com::sun::star; 46cdf0e10cSrcweir using namespace ::com::sun::star::sheet; 47cdf0e10cSrcweir 48cdf0e10cSrcweir using ::com::sun::star::beans::Property; 49cdf0e10cSrcweir using ::com::sun::star::beans::PropertyValue; 50cdf0e10cSrcweir using ::com::sun::star::beans::XPropertySet; 51cdf0e10cSrcweir using ::com::sun::star::beans::XPropertySetInfo; 52cdf0e10cSrcweir using ::com::sun::star::container::XNameContainer; 53cdf0e10cSrcweir using ::com::sun::star::lang::XComponent; 54cdf0e10cSrcweir using ::com::sun::star::lang::XMultiComponentFactory; 55cdf0e10cSrcweir using ::com::sun::star::frame::XComponentLoader; 56cdf0e10cSrcweir using ::com::sun::star::uno::Reference; 57cdf0e10cSrcweir using ::com::sun::star::uno::Sequence; 58cdf0e10cSrcweir using ::com::sun::star::uno::UNO_QUERY; 59cdf0e10cSrcweir using ::com::sun::star::uno::UNO_QUERY_THROW; 60cdf0e10cSrcweir using ::com::sun::star::uno::XComponentContext; 61cdf0e10cSrcweir using ::com::sun::star::uno::XInterface; 62cdf0e10cSrcweir using ::com::sun::star::ucb::XSimpleFileAccess; 63cdf0e10cSrcweir using ::com::sun::star::document::XTypeDetection; 64cdf0e10cSrcweir using ::rtl::OUString; 65cdf0e10cSrcweir 66cdf0e10cSrcweir using ::std::auto_ptr; 67cdf0e10cSrcweir 68cdf0e10cSrcweir const OUString EXTN = rtl::OUString::createFromAscii(".xls"); 69cdf0e10cSrcweir 70cdf0e10cSrcweir OUString convertToURL( const OUString& rPath ) 71cdf0e10cSrcweir { 72cdf0e10cSrcweir rtl::OUString aURL; 73cdf0e10cSrcweir INetURLObject aObj; 74cdf0e10cSrcweir aObj.SetURL( rPath ); 75cdf0e10cSrcweir bool bIsURL = aObj.GetProtocol() != INET_PROT_NOT_VALID; 76cdf0e10cSrcweir if ( bIsURL ) 77cdf0e10cSrcweir aURL = rPath; 78cdf0e10cSrcweir else 79cdf0e10cSrcweir { 80cdf0e10cSrcweir osl::FileBase::getFileURLFromSystemPath( rPath, aURL ); 81cdf0e10cSrcweir if ( aURL.equals( rPath ) ) 82cdf0e10cSrcweir throw uno::RuntimeException( rtl::OUString::createFromAscii( "could'nt convert " ).concat( rPath ).concat( rtl::OUString::createFromAscii( " to a URL, is it a fully qualified path name? " ) ), Reference< uno::XInterface >() ); 83cdf0e10cSrcweir } 84cdf0e10cSrcweir return aURL; 85cdf0e10cSrcweir } 86cdf0e10cSrcweir 87cdf0e10cSrcweir OUString ascii(const sal_Char* cstr) 88cdf0e10cSrcweir { 89cdf0e10cSrcweir return OUString::createFromAscii(cstr); 90cdf0e10cSrcweir } 91cdf0e10cSrcweir 92cdf0e10cSrcweir const sal_Char* getStr(const OUString& ou) 93cdf0e10cSrcweir { 94cdf0e10cSrcweir return OUStringToOString(ou, RTL_TEXTENCODING_UTF8).getStr(); 95cdf0e10cSrcweir } 96cdf0e10cSrcweir 97cdf0e10cSrcweir 98cdf0e10cSrcweir int usage( const char* pName ) 99cdf0e10cSrcweir { 100cdf0e10cSrcweir std::cerr << "usage: " << pName << "<path to testdocument dir> <output_directory>" << std::endl; 101cdf0e10cSrcweir return 1; 102cdf0e10cSrcweir 103cdf0e10cSrcweir } 104cdf0e10cSrcweir 105cdf0e10cSrcweir class TestVBA 106cdf0e10cSrcweir { 107cdf0e10cSrcweir private: 108cdf0e10cSrcweir Reference< XComponentContext > mxContext; 109cdf0e10cSrcweir Reference< XMultiComponentFactory > mxMCF; 110cdf0e10cSrcweir Reference< XComponentLoader > mxCompLoader; 111cdf0e10cSrcweir Reference< XSimpleFileAccess > mxSFA; 112cdf0e10cSrcweir rtl::OUString msOutDirPath; 113cdf0e10cSrcweir protected: 114cdf0e10cSrcweir public: 115cdf0e10cSrcweir TestVBA( const Reference< XComponentContext >& _xContext, 116cdf0e10cSrcweir const Reference< XMultiComponentFactory >& _xMCF, 117cdf0e10cSrcweir const Reference< XComponentLoader >& _xCompLoader, 118cdf0e10cSrcweir const rtl::OUString& _outDirPath ) : mxContext( _xContext ), mxMCF( _xMCF ), 119cdf0e10cSrcweir mxCompLoader( _xCompLoader ), msOutDirPath( convertToURL( _outDirPath ) ) 120cdf0e10cSrcweir { 121cdf0e10cSrcweir mxSFA.set( mxMCF->createInstanceWithContext( rtl::OUString::createFromAscii( "com.sun.star.ucb.SimpleFileAccess" ), mxContext), uno::UNO_QUERY_THROW ); 122cdf0e10cSrcweir } 123cdf0e10cSrcweir 124cdf0e10cSrcweir rtl::OUString getLogLocation() throw ( beans::UnknownPropertyException, lang::IllegalArgumentException, lang::WrappedTargetException, uno::Exception ) 125cdf0e10cSrcweir { 126cdf0e10cSrcweir rtl::OUString sLogLocation; 127cdf0e10cSrcweir Reference< XPropertySet > pathSettings( mxMCF->createInstanceWithContext( rtl::OUString::createFromAscii( "com.sun.star.comp.framework.PathSettings" ), mxContext), uno::UNO_QUERY_THROW ); 128cdf0e10cSrcweir pathSettings->getPropertyValue( rtl::OUString::createFromAscii( "Work" ) ) >>= sLogLocation; 129cdf0e10cSrcweir sLogLocation = sLogLocation.concat( rtl::OUString::createFromAscii( "/" ) ).concat( rtl::OUString::createFromAscii( "HelperAPI-test.log" ) ); 130cdf0e10cSrcweir return sLogLocation; 131cdf0e10cSrcweir } 132cdf0e10cSrcweir rtl::OUString getLogLocationWithName( OUString fileName ) throw ( beans::UnknownPropertyException, lang::IllegalArgumentException, lang::WrappedTargetException, uno::Exception ) 133cdf0e10cSrcweir { 134cdf0e10cSrcweir printf("%s\n", getenv("HOME") ); 135cdf0e10cSrcweir printf("file name %s\n", rtl::OUStringToOString( fileName, RTL_TEXTENCODING_UTF8 ).getStr() ); 136cdf0e10cSrcweir //rtl::OUString sLogLocation( rtl::OUString::createFromAscii( getenv("HOME") ) ); 137cdf0e10cSrcweir rtl::OUString sLogLocation; 138cdf0e10cSrcweir Reference< XPropertySet > pathSettings( mxMCF->createInstanceWithContext( rtl::OUString::createFromAscii( "com.sun.star.comp.framework.PathSettings" ), mxContext), uno::UNO_QUERY_THROW ); 139cdf0e10cSrcweir pathSettings->getPropertyValue( rtl::OUString::createFromAscii( "Work" ) ) >>= sLogLocation; 140cdf0e10cSrcweir sLogLocation = sLogLocation.concat( rtl::OUString::createFromAscii( "/" ) ).concat( fileName.copy ( 0, fileName.lastIndexOf( EXTN ) ) + rtl::OUString::createFromAscii( ".log" ) ); 141cdf0e10cSrcweir return sLogLocation; 142cdf0e10cSrcweir } 143cdf0e10cSrcweir 144cdf0e10cSrcweir void init() 145cdf0e10cSrcweir { 146cdf0e10cSrcweir // blow away previous logs? 147cdf0e10cSrcweir } 148cdf0e10cSrcweir 149cdf0e10cSrcweir void proccessDocument( const rtl::OUString& sUrl ) 150cdf0e10cSrcweir { 151cdf0e10cSrcweir if ( !mxSFA->isFolder( sUrl ) && sUrl.endsWithIgnoreAsciiCaseAsciiL( ".xls", 4 ) ) 152cdf0e10cSrcweir 153cdf0e10cSrcweir { 154cdf0e10cSrcweir try 155cdf0e10cSrcweir { 156cdf0e10cSrcweir OSL_TRACE( "processing %s", rtl::OUStringToOString( sUrl, RTL_TEXTENCODING_UTF8 ).getStr() ); 157cdf0e10cSrcweir printf( "processing %s\n", rtl::OUStringToOString( sUrl, RTL_TEXTENCODING_UTF8 ).getStr() ); 158cdf0e10cSrcweir // Loading the wanted document 159cdf0e10cSrcweir Sequence< PropertyValue > propertyValues(1); 160cdf0e10cSrcweir propertyValues[0].Name = rtl::OUString::createFromAscii( "Hidden" ); 161cdf0e10cSrcweir propertyValues[0].Value <<= sal_False; 162cdf0e10cSrcweir 163cdf0e10cSrcweir rtl::OUString sfileUrl = convertToURL( sUrl ); 164cdf0e10cSrcweir printf( "try to get xDoc %s\n", rtl::OUStringToOString( sfileUrl, RTL_TEXTENCODING_UTF8 ).getStr() ); 165cdf0e10cSrcweir Reference< uno::XInterface > xDoc = 166cdf0e10cSrcweir mxCompLoader->loadComponentFromURL( sfileUrl, rtl::OUString::createFromAscii( "_blank" ), 0, propertyValues); 167cdf0e10cSrcweir printf( "got xDoc\n" ); 168cdf0e10cSrcweir 169cdf0e10cSrcweir OUString logFileURL = convertToURL( getLogLocation() ); 170cdf0e10cSrcweir try 171cdf0e10cSrcweir { 172cdf0e10cSrcweir Reference< script::provider::XScriptProviderSupplier > xSupplier( xDoc, uno::UNO_QUERY_THROW ) ; 173cdf0e10cSrcweir if ( mxSFA->exists( logFileURL ) ) 174cdf0e10cSrcweir mxSFA->kill( logFileURL ); 175cdf0e10cSrcweir 176cdf0e10cSrcweir printf("try to get the ScriptProvider\n"); 177cdf0e10cSrcweir Reference< script::provider::XScriptProvider > xProv = xSupplier->getScriptProvider(); 178cdf0e10cSrcweir printf("get the ScriptProvider\n"); 179cdf0e10cSrcweir printf("try to get the Script\n"); 180cdf0e10cSrcweir Reference< script::provider::XScript > xScript; 181cdf0e10cSrcweir try 182cdf0e10cSrcweir { 183cdf0e10cSrcweir xScript = xProv->getScript( rtl::OUString::createFromAscii( "vnd.sun.star.script:Standard.TestMacros.Main?language=Basic&location=document" )); 184cdf0e10cSrcweir } catch ( uno::Exception& e ) 185cdf0e10cSrcweir { 186cdf0e10cSrcweir try 187cdf0e10cSrcweir { 188cdf0e10cSrcweir xScript = xProv->getScript( rtl::OUString::createFromAscii( "vnd.sun.star.script:Standard.testMacro.Main?language=Basic&location=document" )); 189cdf0e10cSrcweir } catch ( uno::Exception& e2 ) 190cdf0e10cSrcweir { 191cdf0e10cSrcweir xScript = xProv->getScript( rtl::OUString::createFromAscii( "vnd.sun.star.script:Standard.testMain.Main?language=Basic&location=document" )); 192cdf0e10cSrcweir } 193cdf0e10cSrcweir } 194cdf0e10cSrcweir OSL_TRACE("Got script for doc %s", rtl::OUStringToOString( sUrl, RTL_TEXTENCODING_UTF8 ).getStr() ); 195cdf0e10cSrcweir printf("get the Script\n"); 196cdf0e10cSrcweir Sequence< uno::Any > aArgs; 197cdf0e10cSrcweir Sequence< sal_Int16 > aOutArgsIndex; 198cdf0e10cSrcweir Sequence< uno::Any > aOutArgs; 199cdf0e10cSrcweir 200cdf0e10cSrcweir xScript->invoke(aArgs, aOutArgsIndex, aOutArgs); 201cdf0e10cSrcweir 202cdf0e10cSrcweir OUString fileName = sUrl.copy ( sUrl.lastIndexOf( '/' ) ); 203cdf0e10cSrcweir OUString newLocation = msOutDirPath + fileName.copy ( 0, fileName.lastIndexOf( EXTN ) ) + rtl::OUString::createFromAscii( ".log" ); 204cdf0e10cSrcweir try 205cdf0e10cSrcweir { 206cdf0e10cSrcweir printf("move log file\n"); 207cdf0e10cSrcweir mxSFA->move( logFileURL, newLocation ); 208cdf0e10cSrcweir OSL_TRACE("new logfile location is %s ", rtl::OUStringToOString( newLocation, RTL_TEXTENCODING_UTF8 ).getStr() ); 209cdf0e10cSrcweir printf("moved to new location\n"); 210cdf0e10cSrcweir } 211cdf0e10cSrcweir catch ( uno::Exception& e ) 212cdf0e10cSrcweir { 213cdf0e10cSrcweir logFileURL = convertToURL( getLogLocationWithName( fileName ) ); 214cdf0e10cSrcweir printf("move log file from %s\n", rtl::OUStringToOString( logFileURL, RTL_TEXTENCODING_UTF8 ).getStr() ); 215cdf0e10cSrcweir mxSFA->move( logFileURL, newLocation ); 216cdf0e10cSrcweir OSL_TRACE("new logfile location is %s ", rtl::OUStringToOString( newLocation, RTL_TEXTENCODING_UTF8 ).getStr() ); 217cdf0e10cSrcweir printf("moved to new location\n"); 218cdf0e10cSrcweir } 219cdf0e10cSrcweir 220cdf0e10cSrcweir } 221cdf0e10cSrcweir catch ( uno::Exception& e ) 222cdf0e10cSrcweir { 223cdf0e10cSrcweir std::cerr << "Caught exception " << rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_UTF8 ).getStr() << std::endl; 224cdf0e10cSrcweir } 225cdf0e10cSrcweir 226cdf0e10cSrcweir // interface is supported, otherwise use XComponent.dispose 227cdf0e10cSrcweir Reference< util::XCloseable > xCloseable ( xDoc, uno::UNO_QUERY ); 228cdf0e10cSrcweir 229cdf0e10cSrcweir if ( xCloseable.is() ) 230cdf0e10cSrcweir { 231cdf0e10cSrcweir printf("try to close\n"); 232cdf0e10cSrcweir // will close application. and only run a test case for 3.0 233cdf0e10cSrcweir // maybe it is a bug. yes, it is a bug 234cdf0e10cSrcweir // if only one frame and model, click a button which related will colse. 235cdf0e10cSrcweir // will make a crash. It related with window listener. 236cdf0e10cSrcweir // so, for run all test cases, it should not close the document at this moment. 237cdf0e10cSrcweir xCloseable->close(sal_False); 238cdf0e10cSrcweir printf("closed\n"); 239cdf0e10cSrcweir } 240cdf0e10cSrcweir else 241cdf0e10cSrcweir { 242cdf0e10cSrcweir printf("try to dispose\n"); 243cdf0e10cSrcweir Reference< XComponent > xComp( xDoc, uno::UNO_QUERY_THROW ); 244cdf0e10cSrcweir // same as close. 245cdf0e10cSrcweir xComp->dispose(); 246cdf0e10cSrcweir printf("disposed\n"); 247cdf0e10cSrcweir } 248cdf0e10cSrcweir } 249cdf0e10cSrcweir catch( uno::Exception& e ) 250cdf0e10cSrcweir { 251cdf0e10cSrcweir std::cerr << "Caught exception " << rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_UTF8 ).getStr() << std::endl; 252cdf0e10cSrcweir } 253cdf0e10cSrcweir 254cdf0e10cSrcweir } 255cdf0e10cSrcweir printf("complete processing %s\n", rtl::OUStringToOString( sUrl, RTL_TEXTENCODING_UTF8 ).getStr() ); 256cdf0e10cSrcweir } 257cdf0e10cSrcweir 258cdf0e10cSrcweir void traverse( const rtl::OUString& sFileDirectory ) 259cdf0e10cSrcweir { 260cdf0e10cSrcweir rtl::OUString sFileDirectoryURL = convertToURL( sFileDirectory ); 261cdf0e10cSrcweir if ( !mxSFA->isFolder( sFileDirectoryURL) ) 262cdf0e10cSrcweir { 263cdf0e10cSrcweir throw lang::IllegalArgumentException( rtl::OUString::createFromAscii( "not a directory: ").concat( sFileDirectoryURL ), Reference<uno::XInterface>(), 1 ); 264cdf0e10cSrcweir } 265cdf0e10cSrcweir // Getting all files and directories in the current directory 266cdf0e10cSrcweir Sequence<OUString> entries = mxSFA->getFolderContents( sFileDirectoryURL, sal_False ); 267cdf0e10cSrcweir 268cdf0e10cSrcweir // Iterating for each file and directory 269cdf0e10cSrcweir printf( "Entries %d\n", (int)entries.getLength() ); 270cdf0e10cSrcweir for ( sal_Int32 i = 0; i < entries.getLength(); ++i ) 271cdf0e10cSrcweir { 272cdf0e10cSrcweir proccessDocument( entries[ i ] ); 273cdf0e10cSrcweir } 274cdf0e10cSrcweir } 275cdf0e10cSrcweir }; 276cdf0e10cSrcweir 277cdf0e10cSrcweir void tryDispose( Reference< uno::XInterface > xIF, const char* sComp ) 278cdf0e10cSrcweir { 279cdf0e10cSrcweir Reference< lang::XComponent > xComponent( xIF, uno::UNO_QUERY ); 280cdf0e10cSrcweir if ( xComponent.is() ) 281cdf0e10cSrcweir { 282cdf0e10cSrcweir try 283cdf0e10cSrcweir { 284cdf0e10cSrcweir xComponent->dispose(); 285cdf0e10cSrcweir } 286cdf0e10cSrcweir catch( uno::Exception& e ) 287cdf0e10cSrcweir { 288cdf0e10cSrcweir std::cerr << "tryDispose caught exception " <<rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_UTF8 ).getStr() << " while disposing " << sComp << std::endl; 289cdf0e10cSrcweir } 290cdf0e10cSrcweir } 291cdf0e10cSrcweir } 292cdf0e10cSrcweir int main( int argv, char** argc ) 293cdf0e10cSrcweir { 294cdf0e10cSrcweir if ( !( argv > 2 ) ) 295cdf0e10cSrcweir return usage( argc[0] ); 296cdf0e10cSrcweir try 297cdf0e10cSrcweir { 298cdf0e10cSrcweir 299cdf0e10cSrcweir OSL_TRACE("Attempting to bootstrap normal"); 300cdf0e10cSrcweir Reference<XComponentContext> xCC = ::cppu::bootstrap(); 301cdf0e10cSrcweir Reference<XMultiComponentFactory> xFactory = xCC->getServiceManager(); 302cdf0e10cSrcweir OSL_TRACE("got servicemanager"); 303cdf0e10cSrcweir std::cout << "got servicemanager" << std::endl; 304cdf0e10cSrcweir Reference<XInterface> desktop = xFactory->createInstanceWithContext( 305cdf0e10cSrcweir ascii("com.sun.star.frame.Desktop"), xCC); 306cdf0e10cSrcweir OSL_TRACE("got desktop"); 307cdf0e10cSrcweir std::cout << "got desktop" << std::endl; 308cdf0e10cSrcweir Reference<frame::XComponentLoader> xLoader(desktop, UNO_QUERY_THROW); 309cdf0e10cSrcweir TestVBA* dTest = new TestVBA( xCC, xFactory, xLoader, ascii( argc[ 2 ] ) ); 310cdf0e10cSrcweir if ( argv == 4 ) 311cdf0e10cSrcweir { 312cdf0e10cSrcweir std::cout << "before process" << std::endl; 313cdf0e10cSrcweir dTest->proccessDocument( ascii( argc[ 3 ] ) ); 314cdf0e10cSrcweir std::cout << "after process" << std::endl; 315cdf0e10cSrcweir } 316cdf0e10cSrcweir else 317cdf0e10cSrcweir { 318cdf0e10cSrcweir dTest->traverse( ascii( argc[ 1 ] ) ); 319cdf0e10cSrcweir } 320cdf0e10cSrcweir delete dTest; 321cdf0e10cSrcweir // tryDispose( xLoader, "desktop" ); 322cdf0e10cSrcweir // tryDispose( xCC, "remote context" ); 323cdf0e10cSrcweir 324cdf0e10cSrcweir } 325cdf0e10cSrcweir catch( uno::Exception& e ) 326cdf0e10cSrcweir { 327cdf0e10cSrcweir std::cerr << "Caught Exception " << rtl::OUStringToOString( e.Message, RTL_TEXTENCODING_UTF8 ).getStr() << std::endl; 328cdf0e10cSrcweir } 329cdf0e10cSrcweir 330cdf0e10cSrcweir } 331