You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
590 lines
15 KiB
590 lines
15 KiB
15 years ago
|
/*
|
||
|
This file is or will be part of KDE desktop environment
|
||
|
|
||
|
Copyright 1999 Matt Koss <koss@miesto.sk>
|
||
|
|
||
|
It is licensed under GPL version 2.
|
||
|
|
||
|
If it is part of KDE libraries than this file is licensed under
|
||
|
LGPL version 2.
|
||
|
*/
|
||
|
|
||
|
import org.kde.qt.*;
|
||
|
import org.kde.koala.*;
|
||
|
import java.io.ByteArrayOutputStream;
|
||
|
|
||
|
class KioslaveTest extends KMainWindow {
|
||
|
|
||
|
static final int List = 0;
|
||
|
static final int ListRecursive = 1;
|
||
|
static final int Stat = 2;
|
||
|
static final int Get = 3;
|
||
|
static final int Put = 4;
|
||
|
static final int Copy = 5;
|
||
|
static final int Move = 6;
|
||
|
static final int Delete = 7;
|
||
|
static final int Shred = 8;
|
||
|
static final int Mkdir = 9;
|
||
|
static final int Mimetype = 10;
|
||
|
|
||
|
static final int ProgressNone = 0;
|
||
|
static final int ProgressDefault = 1;
|
||
|
static final int ProgressStatus = 2;
|
||
|
|
||
|
// info stuff
|
||
|
QLabel lb_from;
|
||
|
QLineEdit le_source;
|
||
|
|
||
|
QLabel lb_to;
|
||
|
QLineEdit le_dest;
|
||
|
|
||
|
// operation stuff
|
||
|
QButtonGroup opButtons;
|
||
|
|
||
|
QRadioButton rbList;
|
||
|
QRadioButton rbListRecursive;
|
||
|
QRadioButton rbStat;
|
||
|
QRadioButton rbGet;
|
||
|
QRadioButton rbPut;
|
||
|
QRadioButton rbCopy;
|
||
|
QRadioButton rbMove;
|
||
|
QRadioButton rbDelete;
|
||
|
QRadioButton rbShred;
|
||
|
QRadioButton rbMkdir;
|
||
|
QRadioButton rbMimetype;
|
||
|
|
||
|
// progress stuff
|
||
|
QButtonGroup progressButtons;
|
||
|
|
||
|
QRadioButton rbProgressNone;
|
||
|
QRadioButton rbProgressDefault;
|
||
|
QRadioButton rbProgressStatus;
|
||
|
|
||
|
QPushButton pbStart;
|
||
|
QPushButton pbStop;
|
||
|
|
||
|
QPushButton close;
|
||
|
|
||
|
|
||
|
private Job job;
|
||
|
private QWidget main_widget;
|
||
|
|
||
|
private StatusbarProgress statusProgress;
|
||
|
|
||
|
private int selectedOperation;
|
||
|
private int progressMode;
|
||
|
private int putBuffer;
|
||
|
private Slave slave;
|
||
|
|
||
|
/*
|
||
|
This file is or will be part of KDE desktop environment
|
||
|
|
||
|
Copyright 1999 Matt Koss <koss@miesto.sk>
|
||
|
|
||
|
It is licensed under GPL version 2.
|
||
|
|
||
|
If it is part of KDE libraries than this file is licensed under
|
||
|
LGPL version 2.
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
KioslaveTest( String src, String dest, int op, int pr )
|
||
|
{
|
||
|
super(null, "");
|
||
|
job = null;
|
||
|
|
||
|
main_widget = new QWidget( this, "");
|
||
|
QBoxLayout topLayout = new QVBoxLayout( main_widget, 10, 5 );
|
||
|
|
||
|
QGridLayout grid = new QGridLayout( 2, 2, 10 );
|
||
|
topLayout.addLayout( grid );
|
||
|
|
||
|
grid.setRowStretch(0,1);
|
||
|
grid.setRowStretch(1,1);
|
||
|
|
||
|
grid.setColStretch(0,1);
|
||
|
grid.setColStretch(1,100);
|
||
|
|
||
|
lb_from = new QLabel( "From :", main_widget );
|
||
|
grid.addWidget( lb_from, 0, 0 );
|
||
|
|
||
|
le_source = new QLineEdit( main_widget );
|
||
|
grid.addWidget( le_source, 0, 1 );
|
||
|
le_source.setText( src );
|
||
|
|
||
|
lb_to = new QLabel( "To :", main_widget );
|
||
|
grid.addWidget( lb_to, 1, 0 );
|
||
|
|
||
|
le_dest = new QLineEdit( main_widget );
|
||
|
grid.addWidget( le_dest, 1, 1 );
|
||
|
le_dest.setText( dest );
|
||
|
|
||
|
// Operation groupbox & buttons
|
||
|
opButtons = new QButtonGroup( "Operation", main_widget );
|
||
|
topLayout.addWidget( opButtons, 10 );
|
||
|
connect( opButtons, SIGNAL("clicked(int)"), SLOT("changeOperation(int)") );
|
||
|
|
||
|
QBoxLayout hbLayout = new QHBoxLayout( opButtons, 15 );
|
||
|
|
||
|
rbList = new QRadioButton( "List", opButtons );
|
||
|
opButtons.insert( rbList, List );
|
||
|
hbLayout.addWidget( rbList, 5 );
|
||
|
|
||
|
rbListRecursive = new QRadioButton( "ListRecursive", opButtons );
|
||
|
opButtons.insert( rbListRecursive, ListRecursive );
|
||
|
hbLayout.addWidget( rbListRecursive, 5 );
|
||
|
|
||
|
rbStat = new QRadioButton( "Stat", opButtons );
|
||
|
opButtons.insert( rbStat, Stat );
|
||
|
hbLayout.addWidget( rbStat, 5 );
|
||
|
|
||
|
rbGet = new QRadioButton( "Get", opButtons );
|
||
|
opButtons.insert( rbGet, Get );
|
||
|
hbLayout.addWidget( rbGet, 5 );
|
||
|
|
||
|
rbPut = new QRadioButton( "Put", opButtons );
|
||
|
opButtons.insert( rbPut, Put );
|
||
|
hbLayout.addWidget( rbPut, 5 );
|
||
|
|
||
|
rbCopy = new QRadioButton( "Copy", opButtons );
|
||
|
opButtons.insert( rbCopy, Copy );
|
||
|
hbLayout.addWidget( rbCopy, 5 );
|
||
|
|
||
|
rbMove = new QRadioButton( "Move", opButtons );
|
||
|
opButtons.insert( rbMove, Move );
|
||
|
hbLayout.addWidget( rbMove, 5 );
|
||
|
|
||
|
rbDelete = new QRadioButton( "Delete", opButtons );
|
||
|
opButtons.insert( rbDelete, Delete );
|
||
|
hbLayout.addWidget( rbDelete, 5 );
|
||
|
|
||
|
rbShred = new QRadioButton( "Shred", opButtons );
|
||
|
opButtons.insert( rbShred, Shred );
|
||
|
hbLayout.addWidget( rbShred, 5 );
|
||
|
|
||
|
rbMkdir = new QRadioButton( "Mkdir", opButtons );
|
||
|
opButtons.insert( rbMkdir, Mkdir );
|
||
|
hbLayout.addWidget( rbMkdir, 5 );
|
||
|
|
||
|
rbMimetype = new QRadioButton( "Mimetype", opButtons );
|
||
|
opButtons.insert( rbMimetype, Mimetype );
|
||
|
hbLayout.addWidget( rbMimetype, 5 );
|
||
|
|
||
|
opButtons.setButton( op );
|
||
|
changeOperation( op );
|
||
|
|
||
|
// Progress groupbox & buttons
|
||
|
progressButtons = new QButtonGroup( "Progress dialog mode", main_widget );
|
||
|
topLayout.addWidget( progressButtons, 10 );
|
||
|
connect( progressButtons, SIGNAL("clicked(int)"), SLOT("changeProgressMode(int)") );
|
||
|
|
||
|
hbLayout = new QHBoxLayout( progressButtons, 15 );
|
||
|
|
||
|
rbProgressNone = new QRadioButton( "None", progressButtons );
|
||
|
progressButtons.insert( rbProgressNone, ProgressNone );
|
||
|
hbLayout.addWidget( rbProgressNone, 5 );
|
||
|
|
||
|
rbProgressDefault = new QRadioButton( "Default", progressButtons );
|
||
|
progressButtons.insert( rbProgressDefault, ProgressDefault );
|
||
|
hbLayout.addWidget( rbProgressDefault, 5 );
|
||
|
|
||
|
rbProgressStatus = new QRadioButton( "Status", progressButtons );
|
||
|
progressButtons.insert( rbProgressStatus, ProgressStatus );
|
||
|
hbLayout.addWidget( rbProgressStatus, 5 );
|
||
|
|
||
|
progressButtons.setButton( pr );
|
||
|
changeProgressMode( pr );
|
||
|
|
||
|
// statusbar progress widget
|
||
|
statusProgress = new StatusbarProgress( statusBar() );
|
||
|
statusBar().addWidget( statusProgress, 0, true );
|
||
|
|
||
|
// run & stop butons
|
||
|
hbLayout = new QHBoxLayout( topLayout, 15 );
|
||
|
|
||
|
pbStart = new QPushButton( "&Start", main_widget );
|
||
|
pbStart.setFixedSize( pbStart.sizeHint() );
|
||
|
connect( pbStart, SIGNAL("clicked()"), SLOT("startJob()") );
|
||
|
hbLayout.addWidget( pbStart, 5 );
|
||
|
|
||
|
pbStop = new QPushButton( "Sto&p", main_widget );
|
||
|
pbStop.setFixedSize( pbStop.sizeHint() );
|
||
|
pbStop.setEnabled( false );
|
||
|
connect( pbStop, SIGNAL("clicked()"), SLOT("stopJob()") );
|
||
|
hbLayout.addWidget( pbStop, 5 );
|
||
|
|
||
|
// close button
|
||
|
close = new QPushButton( "&Close", main_widget );
|
||
|
close.setFixedSize( close.sizeHint() );
|
||
|
connect(close, SIGNAL("clicked()"), this, SLOT("slotQuit()"));
|
||
|
|
||
|
topLayout.addWidget( close, 5 );
|
||
|
|
||
|
main_widget.setMinimumSize( main_widget.sizeHint() );
|
||
|
setCentralWidget( main_widget );
|
||
|
|
||
|
slave = null;
|
||
|
// slave = Scheduler.getConnectedSlave(new KURL("ftp://ftp.kde.org"));
|
||
|
Scheduler.connect(SIGNAL("slaveConnected(Slave)"),
|
||
|
this, SLOT("slotSlaveConnected()"));
|
||
|
Scheduler.connect(SIGNAL("slaveError(Slave,int,String)"),
|
||
|
this, SLOT("slotSlaveError()"));
|
||
|
}
|
||
|
|
||
|
|
||
|
protected void closeEvent( QCloseEvent e ){
|
||
|
slotQuit();
|
||
|
}
|
||
|
|
||
|
|
||
|
void slotQuit(){
|
||
|
if ( job != null ) {
|
||
|
job.kill( true ); // kill the job quietly
|
||
|
}
|
||
|
if (slave != null )
|
||
|
Scheduler.disconnectSlave(slave);
|
||
|
KApplication.kApplication().quit();
|
||
|
}
|
||
|
|
||
|
|
||
|
void changeOperation( int id ) {
|
||
|
// only two urls for copy and move
|
||
|
boolean enab = rbCopy.isChecked() || rbMove.isChecked();
|
||
|
|
||
|
le_dest.setEnabled( enab );
|
||
|
|
||
|
selectedOperation = id;
|
||
|
}
|
||
|
|
||
|
|
||
|
void changeProgressMode( int id ) {
|
||
|
progressMode = id;
|
||
|
|
||
|
if ( progressMode == ProgressStatus ) {
|
||
|
statusBar().show();
|
||
|
} else {
|
||
|
statusBar().hide();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void startJob() {
|
||
|
String sCurrent = QDir.currentDirPath()+"/";
|
||
|
KURL.encode_string(sCurrent);
|
||
|
String sSrc = le_source.text();
|
||
|
KURL src = new KURL( new KURL(sCurrent), sSrc );
|
||
|
|
||
|
if ( !src.isValid() ) {
|
||
|
QMessageBox.critical(this, "Kioslave Error Message", "Source URL is malformed" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
String sDest = le_dest.text();
|
||
|
KURL dest = new KURL( new KURL(sCurrent), sDest );
|
||
|
|
||
|
if ( !dest.isValid() &&
|
||
|
( selectedOperation == Copy || selectedOperation == Move ) ) {
|
||
|
QMessageBox.critical(this, "Kioslave Error Message",
|
||
|
"Destination URL is malformed" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
pbStart.setEnabled( false );
|
||
|
|
||
|
boolean observe = true;
|
||
|
if (progressMode != ProgressDefault) {
|
||
|
observe = false;
|
||
|
}
|
||
|
|
||
|
SimpleJob myJob = null;
|
||
|
|
||
|
switch ( selectedOperation ) {
|
||
|
case List:
|
||
|
myJob = KIO.listDir( src );
|
||
|
// connect(myJob, SIGNAL(" entries( Job, const KIO.UDSEntryList&)"),
|
||
|
// SLOT(" slotEntries( KIO.Job, const KIO.UDSEntryList&)"));
|
||
|
break;
|
||
|
|
||
|
case ListRecursive:
|
||
|
myJob = KIO.listRecursive( src );
|
||
|
// connect(myJob, SIGNAL(" entries( KIO.Job, const KIO.UDSEntryList&)"),
|
||
|
// SLOT(" slotEntries( KIO.Job, const KIO.UDSEntryList&)"));
|
||
|
break;
|
||
|
|
||
|
case Stat:
|
||
|
myJob = KIO.stat( src );
|
||
|
break;
|
||
|
|
||
|
case Get:
|
||
|
myJob = KIO.get( src, true );
|
||
|
connect(myJob, SIGNAL("data( Job, byte[])"),
|
||
|
SLOT("slotData( Job, byte[])"));
|
||
|
break;
|
||
|
|
||
|
case Put:
|
||
|
putBuffer = 0;
|
||
|
myJob = KIO.put( src, -1, true, false);
|
||
|
connect(myJob, SIGNAL("dataReq( Job, ByteArrayOutputStream)"),
|
||
|
SLOT("slotDataReq( Job, ByteArrayOutputStream)"));
|
||
|
break;
|
||
|
|
||
|
case Copy:
|
||
|
job = KIO.copy( src, dest, observe );
|
||
|
break;
|
||
|
|
||
|
case Move:
|
||
|
job = KIO.move( src, dest, observe );
|
||
|
break;
|
||
|
|
||
|
case Delete:
|
||
|
job = KIO.del( src, false, observe );
|
||
|
break;
|
||
|
|
||
|
case Shred:
|
||
|
job = KIO.del(src, true, observe);
|
||
|
break;
|
||
|
|
||
|
case Mkdir:
|
||
|
myJob = KIO.mkdir( src );
|
||
|
break;
|
||
|
|
||
|
case Mimetype:
|
||
|
myJob = KIO.mimetype( src );
|
||
|
break;
|
||
|
}
|
||
|
if (myJob != null)
|
||
|
{
|
||
|
if (slave != null)
|
||
|
Scheduler.assignJobToSlave(slave, myJob);
|
||
|
job = myJob;
|
||
|
}
|
||
|
|
||
|
connect( job, SIGNAL(" result( Job )"),
|
||
|
SLOT(" slotResult( Job )") );
|
||
|
|
||
|
connect( job, SIGNAL(" canceled( Job )"),
|
||
|
SLOT(" slotResult( Job )") );
|
||
|
|
||
|
if (progressMode == ProgressStatus) {
|
||
|
statusProgress.setJob( job );
|
||
|
}
|
||
|
|
||
|
pbStop.setEnabled( true );
|
||
|
}
|
||
|
|
||
|
|
||
|
void slotResult(Job _job )
|
||
|
{
|
||
|
if ( _job.error() != 0 )
|
||
|
{
|
||
|
_job.showErrorDialog();
|
||
|
}
|
||
|
else if ( selectedOperation == Stat )
|
||
|
{
|
||
|
// UDSEntry entry = ((KIO.StatJob)_job).statResult();
|
||
|
// printUDSEntry( entry );
|
||
|
}
|
||
|
else if ( selectedOperation == Mimetype )
|
||
|
{
|
||
|
System.out.println("mimetype is " + ((MimetypeJob)_job).mimetype());
|
||
|
}
|
||
|
|
||
|
if (job == _job)
|
||
|
job = null;
|
||
|
pbStart.setEnabled( true );
|
||
|
pbStop.setEnabled( false );
|
||
|
}
|
||
|
|
||
|
void slotSlaveConnected()
|
||
|
{
|
||
|
System.out.println("Slave connected.");
|
||
|
}
|
||
|
|
||
|
void slotSlaveError()
|
||
|
{
|
||
|
System.out.println("Error connected.");
|
||
|
slave = null;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
void UDSEntry & entry )
|
||
|
{
|
||
|
KIO.UDSEntry.ConstIterator it = entry.begin();
|
||
|
for( ; it != entry.end(); it++ ) {
|
||
|
switch ((it).m_uds) {
|
||
|
case KIO.UDS_FILE_TYPE:
|
||
|
kdDebug() << "File Type : " << (mode_t)((it).m_long) << endl;
|
||
|
if ( S_ISDIR( (mode_t)((it).m_long) ) )
|
||
|
{
|
||
|
kdDebug() << "is a dir" << endl;
|
||
|
}
|
||
|
break;
|
||
|
case KIO.UDS_ACCESS:
|
||
|
kdDebug() << "Access permissions : " << (mode_t)((it).m_long) << endl;
|
||
|
break;
|
||
|
case KIO.UDS_USER:
|
||
|
kdDebug() << "User : " << ((it).m_str.ascii() ) << endl;
|
||
|
break;
|
||
|
case KIO.UDS_GROUP:
|
||
|
kdDebug() << "Group : " << ((it).m_str.ascii() ) << endl;
|
||
|
break;
|
||
|
case KIO.UDS_NAME:
|
||
|
kdDebug() << "Name : " << ((it).m_str.ascii() ) << endl;
|
||
|
//m_strText = decodeFileName( (it).m_str );
|
||
|
break;
|
||
|
case KIO.UDS_URL:
|
||
|
kdDebug() << "URL : " << ((it).m_str.ascii() ) << endl;
|
||
|
break;
|
||
|
case KIO.UDS_MIME_TYPE:
|
||
|
kdDebug() << "MimeType : " << ((it).m_str.ascii() ) << endl;
|
||
|
break;
|
||
|
case KIO.UDS_LINK_DEST:
|
||
|
kdDebug() << "LinkDest : " << ((it).m_str.ascii() ) << endl;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void UDSEntryList& list) {
|
||
|
|
||
|
UDSEntryListConstIterator it=list.begin();
|
||
|
for (; it != list.end(); ++it) {
|
||
|
UDSEntry.ConstIterator it2 = (it).begin();
|
||
|
for( ; it2 != (it).end(); it2++ ) {
|
||
|
if ((it2).m_uds == UDS_NAME)
|
||
|
kdDebug() << "" << ( it2 ).m_str << endl;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
void slotData(Job job, byte[] data)
|
||
|
{
|
||
|
if (data.length == 0)
|
||
|
{
|
||
|
System.out.println("Data: <End>");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
String myString = new String(data);
|
||
|
System.out.println("Data: \"" + myString + "\"");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void slotDataReq(Job job, ByteArrayOutputStream data)
|
||
|
{
|
||
|
String[] fileDataArray =
|
||
|
{
|
||
|
"Hello world\n",
|
||
|
"This is a test file\n",
|
||
|
"You can safely delete it.\n",
|
||
|
null
|
||
|
};
|
||
|
String fileData = fileDataArray[putBuffer++];
|
||
|
|
||
|
if (fileData == null)
|
||
|
{
|
||
|
System.out.println("DataReq: <End>");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
data.write(fileData.getBytes());
|
||
|
} catch (java.io.IOException e) {
|
||
|
;
|
||
|
}
|
||
|
|
||
|
System.out.println("DataReq: \"" + fileData + "\"");
|
||
|
}
|
||
|
|
||
|
void stopJob() {
|
||
|
System.out.println("KioslaveTest.stopJob()");
|
||
|
job.kill();
|
||
|
job = null;
|
||
|
|
||
|
pbStop.setEnabled( false );
|
||
|
pbStart.setEnabled( true );
|
||
|
}
|
||
|
|
||
|
static String version = "v0.0.0 0000"; // :-)
|
||
|
static String description = "Test for kioslaves";
|
||
|
static String[][] options =
|
||
|
{
|
||
|
{ "s", null, null },
|
||
|
{ "src <src>", "Source URL", "" },
|
||
|
{ "d", null, null },
|
||
|
{ "dest <dest>", "Destination URL", "" },
|
||
|
{ "o", null, null },
|
||
|
{ "operation <operation>", "Operation (list,listrecursive,stat,get,copy,move,del,shred,mkdir)", "copy" },
|
||
|
{ "p", null, null },
|
||
|
{ "progress <progress>", "Progress Type (none,default,status)", "default" }
|
||
|
};
|
||
|
|
||
|
public static void main(String[] argv) {
|
||
|
KCmdLineArgs.init( argv, "kioslavetest", "KIOSlaveTest", description, version );
|
||
|
KCmdLineArgs.addCmdLineOptions( options );
|
||
|
KApplication app = new KApplication();
|
||
|
|
||
|
KCmdLineArgs args = KCmdLineArgs.parsedArgs();
|
||
|
|
||
|
String src = args.getOption("src");
|
||
|
String dest = args.getOption("dest");
|
||
|
|
||
|
int op = 0;
|
||
|
int pr = 0;
|
||
|
|
||
|
String tmps;
|
||
|
|
||
|
tmps = args.getOption("operation");
|
||
|
if ( tmps.equals("list")) {
|
||
|
op = KioslaveTest.List;
|
||
|
} else if ( tmps.equals("listrecursive")) {
|
||
|
op = KioslaveTest.ListRecursive;
|
||
|
} else if ( tmps.equals("stat")) {
|
||
|
op = KioslaveTest.Stat;
|
||
|
} else if ( tmps.equals("get")) {
|
||
|
op = KioslaveTest.Get;
|
||
|
} else if ( tmps.equals("copy")) {
|
||
|
op = KioslaveTest.Copy;
|
||
|
} else if ( tmps.equals("move")) {
|
||
|
op = KioslaveTest.Move;
|
||
|
} else if ( tmps.equals("del")) {
|
||
|
op = KioslaveTest.Delete;
|
||
|
} else if ( tmps.equals("shred")) {
|
||
|
op = KioslaveTest.Shred;
|
||
|
} else if ( tmps.equals("mkdir")) {
|
||
|
op = KioslaveTest.Mkdir;
|
||
|
} else KCmdLineArgs.usage("unknown operation");
|
||
|
|
||
|
tmps = args.getOption("progress");
|
||
|
if ( tmps.equals("none")) {
|
||
|
pr = KioslaveTest.ProgressNone;
|
||
|
} else if ( tmps.equals("default")) {
|
||
|
pr = KioslaveTest.ProgressDefault;
|
||
|
} else if ( tmps.equals("status")) {
|
||
|
pr = KioslaveTest.ProgressStatus;
|
||
|
} else KCmdLineArgs.usage("unknown progress mode");
|
||
|
|
||
|
args.clear(); // Free up memory
|
||
|
|
||
|
KioslaveTest test = new KioslaveTest( src, dest, op, pr );
|
||
|
test.show();
|
||
|
// Bug in KTMW / Qt / layouts ?
|
||
|
test.resize( test.sizeHint() );
|
||
|
|
||
|
app.setMainWidget(test);
|
||
|
app.exec();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static {
|
||
|
qtjava.initialize();
|
||
|
kdejava.initialize();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|