LibreOffice Module ucb (master)  1
bc.cxx
Go to the documentation of this file.
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 <rtl/uri.hxx>
21 #include <rtl/ustrbuf.hxx>
22 #include <sal/log.hxx>
23 #include <osl/file.hxx>
24 
25 #include <osl/diagnose.h>
26 #include <com/sun/star/lang/NoSupportException.hpp>
27 #include <com/sun/star/sdbc/SQLException.hpp>
28 #include <com/sun/star/ucb/IllegalIdentifierException.hpp>
29 #include <com/sun/star/ucb/OpenMode.hpp>
30 #include <com/sun/star/beans/IllegalTypeException.hpp>
31 #include <com/sun/star/beans/PropertyAttribute.hpp>
32 #include <com/sun/star/ucb/XProgressHandler.hpp>
33 #include <com/sun/star/task/XInteractionHandler.hpp>
34 #include <com/sun/star/io/XActiveDataStreamer.hpp>
35 #include <com/sun/star/io/XOutputStream.hpp>
36 #include <com/sun/star/ucb/NumberedSortingInfo.hpp>
37 #include <com/sun/star/io/XActiveDataSink.hpp>
38 #include <com/sun/star/beans/PropertyChangeEvent.hpp>
39 #include <com/sun/star/beans/PropertySetInfoChange.hpp>
40 #include <com/sun/star/ucb/ContentAction.hpp>
41 #include <com/sun/star/ucb/NameClash.hpp>
42 #include <comphelper/fileurl.hxx>
46 #include <ucbhelper/macros.hxx>
47 #include "filglob.hxx"
48 #include "filid.hxx"
49 #include "filrow.hxx"
50 #include "bc.hxx"
51 #include "prov.hxx"
52 #include "filerror.hxx"
53 #include "filinsreq.hxx"
54 
55 using namespace fileaccess;
56 using namespace com::sun::star;
57 using namespace com::sun::star::uno;
58 using namespace com::sun::star::ucb;
59 
60 #if OSL_DEBUG_LEVEL > 0
61 #define THROW_WHERE SAL_WHERE
62 #else
63 #define THROW_WHERE ""
64 #endif
65 
68 
70  : public PropertyListeners_impl
71 {
72 public:
73  explicit PropertyListeners( ::osl::Mutex& aMutex )
74  : PropertyListeners_impl( aMutex )
75  {
76  }
77 };
78 
79 
80 /****************************************************************************************/
81 /* */
82 /* BaseContent */
83 /* */
84 /****************************************************************************************/
85 
86 
87 // Private Constructor for just inserted Contents
88 
90  const OUString& parentName,
91  bool bFolder )
92  : m_pMyShell( pMyShell ),
93  m_aUncPath( parentName ),
94  m_bFolder( bFolder ),
95  m_nState( JustInserted )
96 {
97  m_pMyShell->m_pProvider->acquire();
98  // No registering, since we have no name
99 }
100 
101 
102 // Constructor for full featured Contents
103 
105  const Reference< XContentIdentifier >& xContentIdentifier,
106  const OUString& aUncPath )
107  : m_pMyShell( pMyShell ),
108  m_xContentIdentifier( xContentIdentifier ),
109  m_aUncPath( aUncPath ),
110  m_bFolder( false ),
111  m_nState( FullFeatured )
112 {
113  m_pMyShell->m_pProvider->acquire();
116 }
117 
118 
120 {
121  if( ( m_nState & FullFeatured ) || ( m_nState & Deleted ) )
122  {
124  }
125  m_pMyShell->m_pProvider->release();
126 }
127 
128 
129 // XComponent
130 
131 
132 void SAL_CALL
134 {
135  osl::MutexGuard aGuard( m_aMutex );
136 
137  if ( ! m_pDisposeEventListeners )
140 
141  m_pDisposeEventListeners->addInterface( Listener );
142 }
143 
144 
145 void SAL_CALL
147 {
148  osl::MutexGuard aGuard( m_aMutex );
149 
151  m_pDisposeEventListeners->removeInterface( Listener );
152 }
153 
154 
155 void SAL_CALL
157 {
158  lang::EventObject aEvt;
159  std::unique_ptr<comphelper::OInterfaceContainerHelper2> pDisposeEventListeners;
160  std::unique_ptr<comphelper::OInterfaceContainerHelper2> pContentEventListeners;
161  std::unique_ptr<comphelper::OInterfaceContainerHelper2> pPropertySetInfoChangeListeners;
162  std::unique_ptr<PropertyListeners> pPropertyListener;
163 
164  {
165  osl::MutexGuard aGuard( m_aMutex );
166  aEvt.Source = static_cast< XContent* >( this );
167 
168  pDisposeEventListeners = std::move(m_pDisposeEventListeners);
169  pContentEventListeners = std::move(m_pContentEventListeners);
170  pPropertySetInfoChangeListeners = std::move(m_pPropertySetInfoChangeListeners);
171  pPropertyListener = std::move(m_pPropertyListener);
172  }
173 
174  if ( pDisposeEventListeners && pDisposeEventListeners->getLength() )
175  pDisposeEventListeners->disposeAndClear( aEvt );
176 
177  if ( pContentEventListeners && pContentEventListeners->getLength() )
178  pContentEventListeners->disposeAndClear( aEvt );
179 
180  if( pPropertyListener )
181  pPropertyListener->disposeAndClear( aEvt );
182 
183  if( pPropertySetInfoChangeListeners )
184  pPropertySetInfoChangeListeners->disposeAndClear( aEvt );
185 }
186 
187 // XServiceInfo
188 OUString SAL_CALL
190 {
191  return "com.sun.star.comp.ucb.FileContent";
192 }
193 
194 sal_Bool SAL_CALL
195 BaseContent::supportsService( const OUString& ServiceName )
196 {
197  return cppu::supportsService( this, ServiceName );
198 }
199 
200 Sequence< OUString > SAL_CALL
202 {
203  Sequence<OUString> ret { "com.sun.star.ucb.FileContent" };
204  return ret;
205 }
206 
207 // XCommandProcessor
208 
209 
210 sal_Int32 SAL_CALL
212 {
213  return m_pMyShell->getCommandId();
214 }
215 
216 
217 void SAL_CALL
218 BaseContent::abort( sal_Int32 /*CommandId*/ )
219 {
220 }
221 
222 
223 Any SAL_CALL
224 BaseContent::execute( const Command& aCommand,
225  sal_Int32 CommandId,
226  const Reference< XCommandEnvironment >& Environment )
227 {
228  if( ! CommandId )
229  // A Command with commandid zero cannot be aborted
230  CommandId = createCommandIdentifier();
231 
232  m_pMyShell->startTask( CommandId,
233  Environment );
234 
235  Any aAny;
236 
237  if (aCommand.Name == "getPropertySetInfo") // No exceptions
238  {
239  aAny <<= getPropertySetInfo();
240  }
241  else if (aCommand.Name == "getCommandInfo") // no exceptions
242  {
243  aAny <<= getCommandInfo();
244  }
245  else if ( aCommand.Name == "setPropertyValues" )
246  {
247  Sequence< beans::PropertyValue > sPropertyValues;
248 
249  if( ! ( aCommand.Argument >>= sPropertyValues ) )
250  m_pMyShell->installError( CommandId,
252  else
253  aAny <<= setPropertyValues( CommandId,sPropertyValues ); // calls endTask by itself
254  }
255  else if ( aCommand.Name == "getPropertyValues" )
256  {
257  Sequence< beans::Property > ListOfRequestedProperties;
258 
259  if( ! ( aCommand.Argument >>= ListOfRequestedProperties ) )
260  m_pMyShell->installError( CommandId,
262  else
263  aAny <<= getPropertyValues( CommandId,
264  ListOfRequestedProperties );
265  }
266  else if ( aCommand.Name == "open" )
267  {
268  OpenCommandArgument2 aOpenArgument;
269  if( ! ( aCommand.Argument >>= aOpenArgument ) )
270  m_pMyShell->installError( CommandId,
272  else
273  {
274  Reference< XDynamicResultSet > result = open( CommandId,aOpenArgument );
275  if( result.is() )
276  aAny <<= result;
277  }
278  }
279  else if ( aCommand.Name == "delete" )
280  {
281  if( ! aCommand.Argument.has< bool >() )
282  m_pMyShell->installError( CommandId,
284  else
285  deleteContent( CommandId );
286  }
287  else if ( aCommand.Name == "transfer" )
288  {
289  TransferInfo aTransferInfo;
290  if( ! ( aCommand.Argument >>= aTransferInfo ) )
291  m_pMyShell->installError( CommandId,
293  else
294  transfer( CommandId, aTransferInfo );
295  }
296  else if ( aCommand.Name == "insert" )
297  {
298  InsertCommandArgument aInsertArgument;
299  if( ! ( aCommand.Argument >>= aInsertArgument ) )
300  m_pMyShell->installError( CommandId,
302  else
303  insert( CommandId,aInsertArgument );
304  }
305  else if ( aCommand.Name == "getCasePreservingURL" )
306  {
308  seq[0] = beans::Property(
309  "CasePreservingURL",
310  -1,
312  0 );
313  Reference< sdbc::XRow > xRow = getPropertyValues( CommandId,seq );
314  OUString CasePreservingURL = xRow->getString(1);
315  if(!xRow->wasNull())
316  aAny <<= CasePreservingURL;
317  }
318  else if ( aCommand.Name == "createNewContent" )
319  {
320  ucb::ContentInfo aArg;
321  if ( !( aCommand.Argument >>= aArg ) )
322  m_pMyShell->installError( CommandId,
324  else
325  aAny <<= createNewContent( aArg );
326  }
327  else
328  m_pMyShell->installError( CommandId,
330 
331 
332  // This is the only function allowed to throw an exception
333  endTask( CommandId );
334 
335  return aAny;
336 }
337 
338 
339 void SAL_CALL
341  const Sequence< OUString >& PropertyNames,
343 {
344  if( ! Listener.is() )
345  return;
346 
347  osl::MutexGuard aGuard( m_aMutex );
348 
349  if( ! m_pPropertyListener )
351 
352 
353  if( !PropertyNames.hasElements() )
354  m_pPropertyListener->addInterface( OUString(),Listener );
355  else
356  {
358  for( const auto& rName : PropertyNames )
359  if( xProp->hasPropertyByName( rName ) )
360  m_pPropertyListener->addInterface( rName,Listener );
361  }
362 }
363 
364 
365 void SAL_CALL
368 {
369  if( ! Listener.is() )
370  return;
371 
372  osl::MutexGuard aGuard( m_aMutex );
373 
374  if( ! m_pPropertyListener )
375  return;
376 
377  for( const auto& rName : PropertyNames )
378  m_pPropertyListener->removeInterface( rName,Listener );
379 
380  m_pPropertyListener->removeInterface( OUString(), Listener );
381 }
382 
383 
384 // XContent
385 
386 
389 {
390  return m_xContentIdentifier;
391 }
392 
393 
394 OUString SAL_CALL
396 {
397  if( !( m_nState & Deleted ) )
398  {
399  if( m_nState & JustInserted )
400  {
401  if ( m_bFolder )
403  else
405  }
406  else
407  {
408  try
409  {
410  // Who am I ?
412  seq[0] = beans::Property( "IsDocument",
413  -1,
415  0 );
417  bool IsDocument = xRow->getBoolean( 1 );
418 
419  if ( !xRow->wasNull() )
420  {
421  if ( IsDocument )
423  else
425  }
426  else
427  {
428  OSL_FAIL( "BaseContent::getContentType - Property value was null!" );
429  }
430  }
431  catch (const sdbc::SQLException&)
432  {
433  OSL_FAIL( "BaseContent::getContentType - Caught SQLException!" );
434  }
435  }
436  }
437 
438  return OUString();
439 }
440 
441 
442 void SAL_CALL
444  const Reference< XContentEventListener >& Listener )
445 {
446  osl::MutexGuard aGuard( m_aMutex );
447 
448  if ( ! m_pContentEventListeners )
451 
452 
453  m_pContentEventListeners->addInterface( Listener );
454 }
455 
456 
457 void SAL_CALL
459  const Reference< XContentEventListener >& Listener )
460 {
461  osl::MutexGuard aGuard( m_aMutex );
462 
464  m_pContentEventListeners->removeInterface( Listener );
465 }
466 
467 
468 // XPropertyContainer
469 
470 
471 void SAL_CALL
473  const OUString& Name,
474  sal_Int16 Attributes,
475  const Any& DefaultValue )
476 {
477  if( ( m_nState & JustInserted ) || ( m_nState & Deleted ) || Name.isEmpty() )
478  {
479  throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
480  }
481 
482  m_pMyShell->associate( m_aUncPath,Name,DefaultValue,Attributes );
483 }
484 
485 
486 void SAL_CALL
487 BaseContent::removeProperty( const OUString& Name )
488 {
489 
490  if( m_nState & Deleted )
491  throw beans::UnknownPropertyException( Name );
492 
494 }
495 
496 
497 // XContentCreator
498 
499 
502 {
504 }
505 
506 
507 Reference< XContent > SAL_CALL
509 {
510  // Check type.
511  if ( Info.Type.isEmpty() )
512  return Reference< XContent >();
513 
514  bool bFolder = Info.Type == TaskManager::FolderContentType;
515  if ( !bFolder )
516  {
517  if ( Info.Type != TaskManager::FileContentType )
518  {
519  // Neither folder nor file to create!
520  return Reference< XContent >();
521  }
522  }
523 
524  // Who am I ?
525  bool IsDocument = false;
526 
527  try
528  {
530  seq[0] = beans::Property( "IsDocument",
531  -1,
533  0 );
535  IsDocument = xRow->getBoolean( 1 );
536 
537  if ( xRow->wasNull() )
538  {
539  IsDocument = false;
540 // OSL_FAIL( // "BaseContent::createNewContent - Property value was null!" );
541 // return Reference< XContent >();
542  }
543  }
544  catch (const sdbc::SQLException&)
545  {
546  OSL_FAIL( "BaseContent::createNewContent - Caught SQLException!" );
547  return Reference< XContent >();
548  }
549 
550  OUString dstUncPath;
551 
552  if( IsDocument )
553  {
554  // KSO: Why is a document a XContentCreator? This is quite unusual.
555  dstUncPath = getParentName( m_aUncPath );
556  }
557  else
558  dstUncPath = m_aUncPath;
559 
560  BaseContent* p = new BaseContent( m_pMyShell, dstUncPath, bFolder );
561  return Reference< XContent >( p );
562 }
563 
564 
565 // XPropertySetInfoChangeNotifier
566 
567 
568 void SAL_CALL
571 {
572  osl::MutexGuard aGuard( m_aMutex );
575 
576  m_pPropertySetInfoChangeListeners->addInterface( Listener );
577 }
578 
579 
580 void SAL_CALL
583 {
584  osl::MutexGuard aGuard( m_aMutex );
585 
587  m_pPropertySetInfoChangeListeners->removeInterface( Listener );
588 }
589 
590 
591 // XChild
592 
593 
596 {
597  OUString ParentUnq = getParentName( m_aUncPath );
598  OUString ParentUrl;
599 
600 
601  bool err = fileaccess::TaskManager::getUrlFromUnq( ParentUnq, ParentUrl );
602  if( err )
603  return Reference< XInterface >( nullptr );
604 
605  FileContentIdentifier* p = new FileContentIdentifier( ParentUnq );
607 
608  try
609  {
610  return Reference<XInterface>( m_pMyShell->m_pProvider->queryContent( Identifier ), UNO_QUERY );
611  }
612  catch (const IllegalIdentifierException&)
613  {
614  return Reference< XInterface >();
615  }
616 }
617 
618 
619 void SAL_CALL
621  const Reference< XInterface >& )
622 {
623  throw lang::NoSupportException( THROW_WHERE );
624 }
625 
626 
627 // Private Methods
628 
629 
632 {
633  if( m_nState & Deleted )
634  return Reference< XCommandInfo >();
635 
636  return m_pMyShell->info_c();
637 }
638 
639 
642 {
643  if( m_nState & Deleted )
645 
646  return m_pMyShell->info_p( m_aUncPath );
647 }
648 
651  sal_Int32 nMyCommandIdentifier,
652  const Sequence< beans::Property >& PropertySet )
653 {
654  sal_Int32 nProps = PropertySet.getLength();
655  if ( !nProps )
656  return Reference< sdbc::XRow >();
657 
658  if( m_nState & Deleted )
659  {
660  Sequence< Any > aValues( nProps );
661  return Reference< sdbc::XRow >( new XRow_impl( m_pMyShell, aValues ) );
662  }
663 
664  if( m_nState & JustInserted )
665  {
666  Sequence< Any > aValues( nProps );
667  Any* pValues = aValues.getArray();
668 
669  const beans::Property* pProps = PropertySet.getConstArray();
670 
671  for ( sal_Int32 n = 0; n < nProps; ++n )
672  {
673  const beans::Property& rProp = pProps[ n ];
674  Any& rValue = pValues[ n ];
675 
676  if ( rProp.Name == "ContentType" )
677  {
678  rValue <<= OUString(m_bFolder ? TaskManager::FolderContentType
680  }
681  else if ( rProp.Name == "IsFolder" )
682  {
683  rValue <<= m_bFolder;
684  }
685  else if ( rProp.Name == "IsDocument" )
686  {
687  rValue <<= !m_bFolder;
688  }
689  }
690 
692  new XRow_impl( m_pMyShell, aValues ) );
693  }
694 
695  return m_pMyShell->getv( nMyCommandIdentifier,
696  m_aUncPath,
697  PropertySet );
698 }
699 
700 
703  sal_Int32 nMyCommandIdentifier,
704  const Sequence< beans::PropertyValue >& Values )
705 {
706  if( m_nState & Deleted )
707  { // To do
708  return Sequence< Any >( Values.getLength() );
709  }
710 
711  const OUString Title("Title");
712 
713  // Special handling for files which have to be inserted
714  if( m_nState & JustInserted )
715  {
716  for( const auto& rValue : Values )
717  {
718  if( rValue.Name == Title )
719  {
720  OUString NewTitle;
721  if( rValue.Value >>= NewTitle )
722  {
724  {
725  // User wants to set another Title before "insert".
726  // m_aUncPath contains previous own URI.
727 
728  sal_Int32 nLastSlash = m_aUncPath.lastIndexOf( '/' );
729  bool bTrailingSlash = false;
730  if ( nLastSlash == m_aUncPath.getLength() - 1 )
731  {
732  bTrailingSlash = true;
733  nLastSlash
734  = m_aUncPath.lastIndexOf( '/', nLastSlash );
735  }
736 
737  OSL_ENSURE( nLastSlash != -1,
738  "BaseContent::setPropertyValues: "
739  "Invalid URL!" );
740 
741  OUStringBuffer aBuf(
742  m_aUncPath.copy( 0, nLastSlash + 1 ) );
743 
744  if ( !NewTitle.isEmpty() )
745  {
746  aBuf.append( NewTitle );
747  if ( bTrailingSlash )
748  aBuf.append( '/' );
749  }
750  else
751  {
752  m_nState &= ~NameForInsertionSet;
753  }
754 
755  m_aUncPath = aBuf.makeStringAndClear();
756  }
757  else
758  {
759  if ( !NewTitle.isEmpty() )
760  {
761  // Initial Title before "insert".
762  // m_aUncPath contains parent's URI.
763 
764  if( !m_aUncPath.endsWith( "/" ) )
765  m_aUncPath += "/";
766 
767  m_aUncPath += rtl::Uri::encode( NewTitle,
768  rtl_UriCharClassPchar,
769  rtl_UriEncodeIgnoreEscapes,
770  RTL_TEXTENCODING_UTF8 );
772  }
773  }
774  }
775  }
776  }
777 
778  return Sequence< Any >( Values.getLength() );
779  }
780  else
781  {
782  Sequence< Any > ret = m_pMyShell->setv( m_aUncPath, // Does not handle Title
783  Values );
784 
785  // Special handling Title: Setting Title is equivalent to a renaming of the underlying file
786  for( sal_Int32 i = 0; i < Values.getLength(); ++i )
787  {
788  if( Values[i].Name != Title )
789  continue; // handled by setv
790 
791  OUString NewTitle;
792  if( !( Values[i].Value >>= NewTitle ) )
793  {
794  ret[i] <<= beans::IllegalTypeException( THROW_WHERE );
795  break;
796  }
797  else if( NewTitle.isEmpty() )
798  {
799  ret[i] <<= lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
800  break;
801  }
802 
803 
804  OUString aDstName = getParentName( m_aUncPath );
805  if( !aDstName.endsWith("/") )
806  aDstName += "/";
807 
808  aDstName += rtl::Uri::encode( NewTitle,
809  rtl_UriCharClassPchar,
810  rtl_UriEncodeIgnoreEscapes,
811  RTL_TEXTENCODING_UTF8 );
812 
813  m_pMyShell->move( nMyCommandIdentifier, // move notifies the children also;
814  m_aUncPath,
815  aDstName,
816  NameClash::KEEP );
817 
818  try
819  {
820  endTask( nMyCommandIdentifier );
821  }
822  catch(const Exception& e)
823  {
824  ret[i] <<= e;
825  }
826 
827  // NameChanges come back through a ContentEvent
828  break; // only handling Title
829  } // end for
830 
831  return ret;
832  }
833 }
834 
835 
838  sal_Int32 nMyCommandIdentifier,
839  const OpenCommandArgument2& aCommandArgument )
840 {
842 
843  if( m_nState & Deleted )
844  {
845  m_pMyShell->installError( nMyCommandIdentifier,
847  }
848  else if( m_nState & JustInserted )
849  {
850  m_pMyShell->installError( nMyCommandIdentifier,
852  }
853  else
854  {
855  if( aCommandArgument.Mode == OpenMode::DOCUMENT ||
856  aCommandArgument.Mode == OpenMode::DOCUMENT_SHARE_DENY_NONE )
857 
858  {
859  Reference< io::XOutputStream > outputStream( aCommandArgument.Sink,UNO_QUERY );
860  if( outputStream.is() )
861  {
862  m_pMyShell->page( nMyCommandIdentifier,
863  m_aUncPath,
864  outputStream );
865  }
866 
867  bool bLock = ( aCommandArgument.Mode != OpenMode::DOCUMENT_SHARE_DENY_NONE );
868 
869  Reference< io::XActiveDataSink > activeDataSink( aCommandArgument.Sink,UNO_QUERY );
870  if( activeDataSink.is() )
871  {
872  activeDataSink->setInputStream( m_pMyShell->open( nMyCommandIdentifier,
873  m_aUncPath,
874  bLock ) );
875  }
876 
877  Reference< io::XActiveDataStreamer > activeDataStreamer( aCommandArgument.Sink,UNO_QUERY );
878  if( activeDataStreamer.is() )
879  {
880  activeDataStreamer->setStream( m_pMyShell->open_rw( nMyCommandIdentifier,
881  m_aUncPath,
882  bLock ) );
883  }
884  }
885  else if ( aCommandArgument.Mode == OpenMode::ALL ||
886  aCommandArgument.Mode == OpenMode::FOLDERS ||
887  aCommandArgument.Mode == OpenMode::DOCUMENTS )
888  {
889  retValue = m_pMyShell->ls( nMyCommandIdentifier,
890  m_aUncPath,
891  aCommandArgument.Mode,
892  aCommandArgument.Properties,
893  aCommandArgument.SortingInfo );
894  }
895 // else if( aCommandArgument.Mode ==
896 // OpenMode::DOCUMENT_SHARE_DENY_NONE ||
897 // aCommandArgument.Mode ==
898 // OpenMode::DOCUMENT_SHARE_DENY_WRITE )
899 // m_pMyShell->installError( nMyCommandIdentifier,
900 // TASKHANDLING_UNSUPPORTED_OPEN_MODE,
901 // aCommandArgument.Mode);
902  else
903  m_pMyShell->installError( nMyCommandIdentifier,
905  aCommandArgument.Mode);
906  }
907 
908  return retValue;
909 }
910 
911 
912 void
913 BaseContent::deleteContent( sal_Int32 nMyCommandIdentifier )
914 {
915  if( m_nState & Deleted )
916  return;
917 
918  if( m_pMyShell->remove( nMyCommandIdentifier,m_aUncPath ) )
919  {
920  osl::MutexGuard aGuard( m_aMutex );
921  m_nState |= Deleted;
922  }
923 }
924 
925 
926 void
927 BaseContent::transfer( sal_Int32 nMyCommandIdentifier,
928  const TransferInfo& aTransferInfo )
929 {
930  if( m_nState & Deleted )
931  return;
932 
933  if( !comphelper::isFileUrl(aTransferInfo.SourceURL) )
934  {
935  m_pMyShell->installError( nMyCommandIdentifier,
937  return;
938  }
939 
940  OUString srcUnc;
941  if( fileaccess::TaskManager::getUnqFromUrl( aTransferInfo.SourceURL,srcUnc ) )
942  {
943  m_pMyShell->installError( nMyCommandIdentifier,
945  return;
946  }
947 
948  OUString srcUncPath = srcUnc;
949 
950  // Determine the new title !
951  OUString NewTitle;
952  if( !aTransferInfo.NewTitle.isEmpty() )
953  NewTitle = rtl::Uri::encode( aTransferInfo.NewTitle,
954  rtl_UriCharClassPchar,
955  rtl_UriEncodeIgnoreEscapes,
956  RTL_TEXTENCODING_UTF8 );
957  else
958  NewTitle = srcUncPath.copy( 1 + srcUncPath.lastIndexOf( '/' ) );
959 
960  // Is destination a document or a folder ?
962  seq[0] = beans::Property( "IsDocument",
963  -1,
965  0 );
966  Reference< sdbc::XRow > xRow = getPropertyValues( nMyCommandIdentifier,seq );
967  bool IsDocument = xRow->getBoolean( 1 );
968  if( xRow->wasNull() )
969  { // Destination file type could not be determined
970  m_pMyShell->installError( nMyCommandIdentifier,
972  return;
973  }
974 
975  OUString dstUncPath;
976  if( IsDocument )
977  { // as sibling
978  sal_Int32 lastSlash = m_aUncPath.lastIndexOf( '/' );
979  dstUncPath = m_aUncPath.copy(0,lastSlash );
980  }
981  else
982  // as child
983  dstUncPath = m_aUncPath;
984 
985  dstUncPath += "/" + NewTitle;
986 
987  sal_Int32 NameClash = aTransferInfo.NameClash;
988 
989  if( aTransferInfo.MoveData )
990  m_pMyShell->move( nMyCommandIdentifier,srcUncPath,dstUncPath,NameClash );
991  else
992  m_pMyShell->copy( nMyCommandIdentifier,srcUncPath,dstUncPath,NameClash );
993 }
994 
995 
996 void BaseContent::insert( sal_Int32 nMyCommandIdentifier,
997  const InsertCommandArgument& aInsertArgument )
998 {
999  if( m_nState & FullFeatured )
1000  {
1001  m_pMyShell->write( nMyCommandIdentifier,
1002  m_aUncPath,
1003  aInsertArgument.ReplaceExisting,
1004  aInsertArgument.Data );
1005  return;
1006  }
1007 
1008  if( ! ( m_nState & JustInserted ) )
1009  {
1010  m_pMyShell->installError( nMyCommandIdentifier,
1012  return;
1013  }
1014 
1015  // Inserts the content, which has the flag m_bIsFresh
1016 
1017  if( ! (m_nState & NameForInsertionSet) )
1018  {
1019  m_pMyShell->installError( nMyCommandIdentifier,
1021  return;
1022  }
1023 
1024  // Inserting a document or a file?
1025  bool bDocument = false;
1026 
1028  seq[0] = beans::Property( "IsDocument",
1029  -1,
1031  0 );
1032 
1033  Reference< sdbc::XRow > xRow = getPropertyValues( -1,seq );
1034 
1035  bool contentTypeSet = true; // is set to false, if contentType not set
1036  try
1037  {
1038  bDocument = xRow->getBoolean( 1 );
1039  if( xRow->wasNull() )
1040  contentTypeSet = false;
1041 
1042  }
1043  catch (const sdbc::SQLException&)
1044  {
1045  OSL_FAIL( "BaseContent::insert - Caught SQLException!" );
1046  contentTypeSet = false;
1047  }
1048 
1049  if( ! contentTypeSet )
1050  {
1051  m_pMyShell->installError( nMyCommandIdentifier,
1053  return;
1054  }
1055 
1056 
1057  bool success = false;
1058  if( bDocument )
1059  success = m_pMyShell->mkfil( nMyCommandIdentifier,
1060  m_aUncPath,
1061  aInsertArgument.ReplaceExisting,
1062  aInsertArgument.Data );
1063  else
1064  {
1065  while( ! success )
1066  {
1067  success = m_pMyShell->mkdir( nMyCommandIdentifier,
1068  m_aUncPath,
1069  aInsertArgument.ReplaceExisting );
1070  if( success )
1071  break;
1072 
1073  XInteractionRequestImpl aRequestImpl(
1074  rtl::Uri::decode(
1076  rtl_UriDecodeWithCharset,
1077  RTL_TEXTENCODING_UTF8),
1078  static_cast<cppu::OWeakObject*>(this),
1079  m_pMyShell,nMyCommandIdentifier);
1080  uno::Reference<task::XInteractionRequest> const& xReq(aRequestImpl.getRequest());
1081 
1082  m_pMyShell->handleTask( nMyCommandIdentifier, xReq );
1083  if (aRequestImpl.aborted() || aRequestImpl.newName().isEmpty())
1084  // means aborting
1085  break;
1086 
1087  // determine new uncpath
1088  m_pMyShell->clearError( nMyCommandIdentifier );
1090  if( !m_aUncPath.endsWith( "/" ) )
1091  m_aUncPath += "/";
1092 
1093  m_aUncPath += rtl::Uri::encode( aRequestImpl.newName(),
1094  rtl_UriCharClassPchar,
1095  rtl_UriEncodeIgnoreEscapes,
1096  RTL_TEXTENCODING_UTF8 );
1097  }
1098  }
1099 
1100  if ( ! success )
1101  return;
1102 
1104 
1107 
1108  osl::MutexGuard aGuard( m_aMutex );
1110 }
1111 
1112 
1113 void BaseContent::endTask( sal_Int32 CommandId )
1114 {
1115  // This is the only function allowed to throw an exception
1116  m_pMyShell->endTask( CommandId,m_aUncPath,this );
1117 }
1118 
1119 
1120 std::unique_ptr<ContentEventNotifier>
1122 {
1123  osl::MutexGuard aGuard( m_aMutex );
1124 
1125  m_nState |= Deleted;
1126 
1127  std::unique_ptr<ContentEventNotifier> p;
1129  {
1130  p.reset( new ContentEventNotifier( m_pMyShell,
1131  this,
1133  m_pContentEventListeners->getElements() ) );
1134  }
1135 
1136  return p;
1137 }
1138 
1139 
1140 std::unique_ptr<ContentEventNotifier>
1141 BaseContent::cEXC( const OUString& aNewName )
1142 {
1143  osl::MutexGuard aGuard( m_aMutex );
1144 
1146  m_aUncPath = aNewName;
1147  FileContentIdentifier* pp = new FileContentIdentifier( aNewName );
1148  m_xContentIdentifier.set( pp );
1149 
1150  std::unique_ptr<ContentEventNotifier> p;
1152  p.reset( new ContentEventNotifier( m_pMyShell,
1153  this,
1155  xOldRef,
1156  m_pContentEventListeners->getElements() ) );
1157 
1158  return p;
1159 }
1160 
1161 
1162 std::unique_ptr<ContentEventNotifier>
1164 {
1165  osl::MutexGuard aGuard( m_aMutex );
1166  std::unique_ptr<ContentEventNotifier> p;
1168  p.reset( new ContentEventNotifier( m_pMyShell,
1169  this,
1171  m_pContentEventListeners->getElements() ) );
1172 
1173  return p;
1174 }
1175 
1176 std::unique_ptr<PropertySetInfoChangeNotifier>
1178 {
1179  osl::MutexGuard aGuard( m_aMutex );
1180  std::unique_ptr<PropertySetInfoChangeNotifier> p;
1182  p.reset( new PropertySetInfoChangeNotifier( this,
1183  m_pPropertySetInfoChangeListeners->getElements() ) );
1184 
1185  return p;
1186 }
1187 
1188 
1189 std::unique_ptr<PropertyChangeNotifier>
1191 {
1192  osl::MutexGuard aGuard( m_aMutex );
1193 
1194  if (!m_pPropertyListener)
1195  return nullptr;
1196 
1197  const Sequence< OUString > seqNames = m_pPropertyListener->getContainedTypes();
1198 
1199  std::unique_ptr<PropertyChangeNotifier> p;
1200 
1201  if( seqNames.hasElements() )
1202  {
1203  std::unique_ptr<ListenerMap> listener(new ListenerMap);
1204  for( const auto& rName : seqNames )
1205  {
1206  cppu::OInterfaceContainerHelper* pContainer = m_pPropertyListener->getContainer(rName);
1207  if (!pContainer)
1208  continue;
1209  (*listener)[rName] = pContainer->getElements();
1210  }
1211 
1212  p.reset( new PropertyChangeNotifier( this, std::move(listener) ) );
1213  }
1214 
1215  return p;
1216 }
1217 
1218 
1219 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define TASKHANDLING_WRONG_GETPROPERTYVALUES_ARGUMENT
Definition: filerror.hxx:30
#define TASKHANDLING_WRONG_TRANSFER_ARGUMENT
Definition: filerror.hxx:33
std::unique_ptr< ContentEventNotifier > cCEL() override
Definition: bc.cxx:1163
css::uno::Reference< css::ucb::XCommandInfo > getCommandInfo()
Definition: bc.cxx:631
#define TASKHANDLING_WRONG_DELETE_ARGUMENT
Definition: filerror.hxx:32
css::uno::Reference< css::ucb::XContentIdentifier > m_xContentIdentifier
Definition: bc.hxx:198
static const OUStringLiteral IsDocument("IsDocument")
#define TASKHANDLING_WRONG_SETPROPERTYVALUES_ARGUMENT
Definition: filerror.hxx:29
virtual void SAL_CALL setParent(const css::uno::Reference< css::uno::XInterface > &Parent) override
Definition: bc.cxx:620
void endTask(sal_Int32 CommandId, const OUString &aUnqPath, BaseContent *pContent)
Deinstalls the task and evaluates a possibly set error code.
Definition: filtask.cxx:390
void deleteContent(sal_Int32 nMyCommandIdentifier)
Definition: bc.cxx:913
#define TASKHANDLING_UNSUPPORTED_OPEN_MODE
Definition: filerror.hxx:36
friend class ContentEventNotifier
Definition: bc.hxx:256
virtual void SAL_CALL abort(sal_Int32 CommandId) override
Definition: bc.cxx:218
static const OUStringLiteral CasePreservingURL("CasePreservingURL")
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
Definition: bc.cxx:195
virtual css::uno::Any SAL_CALL execute(const css::ucb::Command &aCommand, sal_Int32 CommandId, const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment) override
Definition: bc.cxx:224
aBuf
css::uno::Reference< css::ucb::XDynamicResultSet > open(sal_Int32 nMyCommandIdentifier, const css::ucb::OpenCommandArgument2 &aCommandArgument)
Definition: bc.cxx:837
std::unique_ptr< comphelper::OInterfaceContainerHelper2 > m_pPropertySetInfoChangeListeners
Definition: bc.hxx:213
void deassociate(const OUString &UnqPath, const OUString &PropertyName)
Definition: filtask.cxx:576
virtual void SAL_CALL addContentEventListener(const css::uno::Reference< css::ucb::XContentEventListener > &Listener) override
Definition: bc.cxx:443
OUString Name
bool remove(sal_Int32 CommandId, const OUString &aUnqPath, FileUrlType eTypeToMove=FileUrlType::Unknown, bool MustExist=true)
Deletes the content belonging to fileURL aUnqPath( recursively in case of directory ) ...
Definition: filtask.cxx:1520
void insertDefaultProperties(const OUString &aUnqPath)
Definition: filtask.cxx:1932
virtual void SAL_CALL addPropertiesChangeListener(const css::uno::Sequence< OUString > &PropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener > &Listener) override
Definition: bc.cxx:340
std::unordered_map< OUString, css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > > ListenerMap
Definition: filnot.hxx:81
Title
#define TASKHANDLING_INSERTED_STATE_IN_OPEN_COMMAND
Definition: filerror.hxx:39
#define TASKHANDLING_TRANSFER_INVALIDURL
Definition: filerror.hxx:82
virtual void SAL_CALL addProperty(const OUString &Name, sal_Int16 Attributes, const css::uno::Any &DefaultValue) override
Definition: bc.cxx:472
#define TASKHANDLING_DELETED_STATE_IN_OPEN_COMMAND
Definition: filerror.hxx:38
#define TASKHANDLER_UNSUPPORTED_COMMAND
Definition: filerror.hxx:28
virtual void SAL_CALL dispose() override
Definition: bc.cxx:156
virtual void SAL_CALL removeEventListener(const css::uno::Reference< css::lang::XEventListener > &aListener) override
Definition: bc.cxx:146
static const OUStringLiteral FolderContentType
Definition: filtask.hxx:634
Value
void installError(sal_Int32 CommandId, sal_Int32 ErrorCode, sal_Int32 minorCode=TASKHANDLER_NO_ERROR)
The error code may be one of the error codes defined in filerror.hxx.
Definition: filtask.cxx:444
virtual sal_Int32 SAL_CALL createCommandIdentifier() override
Definition: bc.cxx:211
static const OUStringLiteral FileContentType
Definition: filtask.hxx:635
FileProvider * m_pProvider
Definition: filtask.hxx:487
std::unique_ptr< PropertyListeners > m_pPropertyListener
Definition: bc.hxx:214
virtual void SAL_CALL addEventListener(const css::uno::Reference< css::lang::XEventListener > &xListener) override
Definition: bc.cxx:133
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
#define TASKHANDLING_TRANSFER_DESTFILETYPE
Definition: filerror.hxx:83
PropertyListeners(::osl::Mutex &aMutex)
Definition: bc.cxx:73
virtual void SAL_CALL addPropertySetInfoChangeListener(const css::uno::Reference< css::beans::XPropertySetInfoChangeListener > &Listener) override
Definition: bc.cxx:569
virtual OUString SAL_CALL getContentType() override
Definition: bc.cxx:395
virtual css::uno::Reference< css::ucb::XContent > SAL_CALL createNewContent(const css::ucb::ContentInfo &Info) override
Definition: bc.cxx:508
void transfer(sal_Int32 nMyCommandIdentifier, const css::ucb::TransferInfo &aTransferInfo)
Definition: bc.cxx:927
std::unique_ptr< ContentEventNotifier > cDEL() override
Definition: bc.cxx:1121
static bool getUrlFromUnq(const OUString &Unq, OUString &Url)
Definition: filtask.cxx:1983
bool mkfil(sal_Int32 CommandId, const OUString &aFileName, bool OverWrite, const css::uno::Reference< css::io::XInputStream > &aInputStream)
Creates new file with given URL.
Definition: filtask.cxx:1742
std::unique_ptr< comphelper::OInterfaceContainerHelper2 > m_pDisposeEventListeners
Definition: bc.hxx:211
#define TASKHANDLING_NOCONTENTTYPE_INSERT_COMMAND
Definition: filerror.hxx:53
err
#define TASKHANDLING_TRANSFER_INVALIDSCHEME
Definition: filerror.hxx:81
virtual OUString SAL_CALL getImplementationName() override
Definition: bc.cxx:189
#define TASKHANDLING_WRONG_CREATENEWCONTENT_ARGUMENT
Definition: filerror.hxx:35
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
Definition: bc.cxx:201
MetadataImporterPluginType * result
int i
osl::Mutex m_aEventListenerMutex
Definition: bc.hxx:210
void copy(sal_Int32 CommandId, const OUString &srcUnqPath, const OUString &dstUnqPath, sal_Int32 NameClash)
Copies the content belonging to fileURL srcUnqPath to fileURL dstUnqPath ( files and directories ) ...
Definition: filtask.cxx:1348
css::uno::Reference< css::task::XInteractionRequest > const & getRequest() const
Definition: filinsreq.hxx:125
unsigned char sal_Bool
sal_uInt16 m_nState
Definition: bc.hxx:206
OUString getTitle(const OUString &aPath)
Definition: filglob.cxx:185
static css::uno::Sequence< css::ucb::ContentInfo > queryCreatableContentsInfo()
Definition: filtask.cxx:2911
OUString m_aUncPath
Definition: bc.hxx:199
virtual css::uno::Reference< css::ucb::XContent > SAL_CALL queryContent(const css::uno::Reference< css::ucb::XContentIdentifier > &Identifier) override
Definition: prov.cxx:166
virtual void SAL_CALL removePropertySetInfoChangeListener(const css::uno::Reference< css::beans::XPropertySetInfoChangeListener > &Listener) override
Definition: bc.cxx:581
cppu::OMultiTypeInterfaceContainerHelperVar< OUString > PropertyListeners_impl
Definition: bc.cxx:67
void insert(sal_Int32 nMyCommandIdentifier, const css::ucb::InsertCommandArgument &aInsertArgument)
Definition: bc.cxx:996
sal_Int32 getCommandId()
Definition: filtask.cxx:456
void deregisterNotifier(const OUString &aUnqPath, Notifier *pNotifier)
Definition: filtask.cxx:510
css::uno::Reference< css::io::XStream > open_rw(sal_Int32 CommandId, const OUString &aUnqPath, bool bLock)
Given a file URL aUnqPath, this methods returns a XStream which can be used to read and write from/to...
Definition: filtask.cxx:750
virtual void SAL_CALL removeContentEventListener(const css::uno::Reference< css::ucb::XContentEventListener > &Listener) override
Definition: bc.cxx:458
virtual ~BaseContent() override
Definition: bc.cxx:119
css::uno::Reference< css::sdbc::XRow > getv(sal_Int32 CommandId, const OUString &aUnqPath, const css::uno::Sequence< css::beans::Property > &properties)
Reads the values of the properties belonging to fileURL aUnqPath; Returns an XRow object containing t...
virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent() override
Definition: bc.cxx:595
css::uno::Sequence< css::uno::Any > setPropertyValues(sal_Int32 nMyCommandIdentifier, const css::uno::Sequence< css::beans::PropertyValue > &Values)
Definition: bc.cxx:702
css::uno::Reference< css::ucb::XCommandInfo > info_c()
Info methods.
Definition: filtask.cxx:812
css::uno::Reference< css::io::XInputStream > open(sal_Int32 CommandId, const OUString &aUnqPath, bool bLock)
Given a file URL aUnqPath, this methods returns a XInputStream which reads from the open file...
Definition: filtask.cxx:718
virtual css::uno::Sequence< css::ucb::ContentInfo > SAL_CALL queryCreatableContentsInfo() override
Definition: bc.cxx:501
#define TASKHANDLING_WRONG_OPEN_ARGUMENT
Definition: filerror.hxx:31
std::unique_ptr< comphelper::OInterfaceContainerHelper2 > m_pContentEventListeners
Definition: bc.hxx:212
static bool getUnqFromUrl(const OUString &Url, OUString &Unq)
Definition: filtask.cxx:1962
COMPHELPER_DLLPUBLIC bool isFileUrl(OUString const &url)
virtual void SAL_CALL removeProperty(const OUString &Name) override
Definition: bc.cxx:487
css::uno::Reference< css::ucb::XDynamicResultSet > ls(sal_Int32 CommandId, const OUString &aUnqPath, const sal_Int32 OpenMode, const css::uno::Sequence< css::beans::Property > &sProperty, const css::uno::Sequence< css::ucb::NumberedSortingInfo > &sSortingInfo)
This method returns the result set containing the children of the directory belonging to file URL aUn...
Definition: filtask.cxx:781
bool const m_bFolder
Definition: bc.hxx:205
std::unique_ptr< PropertyChangeNotifier > cPCL() override
Definition: bc.cxx:1190
void move(sal_Int32 CommandId, const OUString &srcUnqPath, const OUString &dstUnqPath, const sal_Int32 NameClash)
Moves the content belonging to fileURL srcUnqPath to fileURL dstUnqPath( files and directories ) ...
Definition: filtask.cxx:1140
void page(sal_Int32 CommandId, const OUString &aUnqPath, const css::uno::Reference< css::io::XOutputStream > &xOutputStream)
Given an xOutputStream, this method writes the content of the file belonging to URL aUnqPath into the...
Definition: filtask.cxx:629
virtual css::uno::Reference< css::ucb::XContentIdentifier > SAL_CALL getIdentifier() override
Definition: bc.cxx:388
css::uno::Sequence< css::uno::Any > setv(const OUString &aUnqPath, const css::uno::Sequence< css::beans::PropertyValue > &values)
Sets the values of the properties belonging to fileURL aUnqPath.
Definition: filtask.cxx:845
osl::Mutex m_aMutex
Definition: bc.hxx:208
#define THROW_WHERE
Definition: bc.cxx:61
css::uno::Reference< css::beans::XPropertySetInfo > info_p(const OUString &aUnqPath)
Definition: filtask.cxx:827
bool write(sal_Int32 CommandId, const OUString &aUnqPath, bool OverWrite, const css::uno::Reference< css::io::XInputStream > &aInputStream)
writes to the file with given URL.
Definition: filtask.cxx:1773
BaseContent(TaskManager *pMyShell, const OUString &parentName, bool bFolder)
Definition: bc.cxx:89
void endTask(sal_Int32 CommandId)
Definition: bc.cxx:1113
#define TASKHANDLING_WRONG_INSERT_ARGUMENT
Definition: filerror.hxx:34
css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > SAL_CALL getElements() const
css::uno::Reference< css::beans::XPropertySetInfo > getPropertySetInfo()
Definition: bc.cxx:641
OUString getParentName(const OUString &aFileName)
Definition: filglob.cxx:192
bool mkdir(sal_Int32 CommandId, const OUString &aDirectoryName, bool OverWrite)
Creates new directory with given URL, recursively if necessary Return:: success of operation...
Definition: filtask.cxx:1682
#define TASKHANDLING_NONAMESET_INSERT_COMMAND
Definition: filerror.hxx:52
void registerNotifier(const OUString &aUnqPath, Notifier *pNotifier)
This two methods register and deregister a change listener for the content belonging to URL aUnqPath...
Definition: filtask.cxx:491
#define TASKHANDLING_NOFRESHINSERT_IN_INSERT_COMMAND
Definition: filerror.hxx:51
void startTask(sal_Int32 CommandId, const css::uno::Reference< css::ucb::XCommandEnvironment > &xCommandEnv)
Definition: filtask.cxx:375
css::uno::Reference< css::sdbc::XRow > getPropertyValues(sal_Int32 nMyCommandIdentifier, const css::uno::Sequence< css::beans::Property > &PropertySet)
Definition: bc.cxx:650
virtual void SAL_CALL removePropertiesChangeListener(const css::uno::Sequence< OUString > &PropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener > &Listener) override
Definition: bc.cxx:366
void clearError(sal_Int32)
Clears any error which are set on the commandid.
Definition: filtask.cxx:421
void handleTask(sal_Int32 CommandId, const css::uno::Reference< css::task::XInteractionRequest > &request)
Handles an interactionrequest.
Definition: filtask.cxx:463
void associate(const OUString &UnqPath, const OUString &PropertyName, const css::uno::Any &DefaultValue, const sal_Int16 Attributes)
Used to associate and deassociate a new property with the content belonging to URL UnqPath...
Definition: filtask.cxx:537
std::unique_ptr< PropertySetInfoChangeNotifier > cPSL() override
Definition: bc.cxx:1177
std::unique_ptr< ContentEventNotifier > cEXC(const OUString &aNewName) override
Definition: bc.cxx:1141
TaskManager * m_pMyShell
Definition: bc.hxx:197