mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-15 04:55:22 +02:00
[207095] check for null datastore
This commit is contained in:
parent
40315bf19f
commit
1e7454b343
2 changed files with 153 additions and 83 deletions
|
@ -19,7 +19,8 @@
|
||||||
* David McKnight (IBM) - [196035] Wrapper SystemMessageExceptions for createFile and createFolder with RemoteFileSecurityException
|
* David McKnight (IBM) - [196035] Wrapper SystemMessageExceptions for createFile and createFolder with RemoteFileSecurityException
|
||||||
* Kevin Doyle (IBM) - [191548] Deleting Read-Only directory removes it from view and displays no error
|
* Kevin Doyle (IBM) - [191548] Deleting Read-Only directory removes it from view and displays no error
|
||||||
* Xuan Chen (IBM) - [202670] [Supertransfer] After doing a copy to a directory that contains folders some folders name's display "deleted"
|
* Xuan Chen (IBM) - [202670] [Supertransfer] After doing a copy to a directory that contains folders some folders name's display "deleted"
|
||||||
* Xuan Chen (IBM) - [190824] Incorrect result for DStore#getSeparator() function when parent is "/"
|
* Xuan Chen (IBM) - [190824] Incorrect result for DStore#getSeparator() function when parent is "/"
|
||||||
|
* David McKnight (IBM) - [207095] check for null datastore
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
package org.eclipse.rse.internal.services.dstore.files;
|
package org.eclipse.rse.internal.services.dstore.files;
|
||||||
|
@ -105,6 +106,7 @@ public class DStoreFileService extends AbstractDStoreService implements IFileSer
|
||||||
super.uninitService(monitor);
|
super.uninitService(monitor);
|
||||||
_fileElementMap.clear();
|
_fileElementMap.clear();
|
||||||
_dstoreFileMap.clear();
|
_dstoreFileMap.clear();
|
||||||
|
_uploadLogElement = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName()
|
public String getName()
|
||||||
|
@ -149,7 +151,10 @@ public class DStoreFileService extends AbstractDStoreService implements IFileSer
|
||||||
|
|
||||||
protected String getDataStoreRoot()
|
protected String getDataStoreRoot()
|
||||||
{
|
{
|
||||||
return getDataStore().getAttribute(DataStoreAttributes.A_LOCAL_PATH);
|
DataStore ds = getDataStore();
|
||||||
|
if (ds != null)
|
||||||
|
return ds.getAttribute(DataStoreAttributes.A_LOCAL_PATH);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -172,28 +177,46 @@ public class DStoreFileService extends AbstractDStoreService implements IFileSer
|
||||||
|
|
||||||
protected void setDataStoreRoot(String root)
|
protected void setDataStoreRoot(String root)
|
||||||
{
|
{
|
||||||
getDataStore().setAttribute(DataStoreAttributes.A_LOCAL_PATH, root);
|
DataStore ds = getDataStore();
|
||||||
|
if (ds != null)
|
||||||
|
ds.setAttribute(DataStoreAttributes.A_LOCAL_PATH, root);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DataElement findUploadLog()
|
protected DataElement findUploadLog()
|
||||||
{
|
{
|
||||||
DataElement minerInfo = getMinerElement();
|
DataElement minerInfo = getMinerElement();
|
||||||
if (_uploadLogElement == null || _uploadLogElement.getDataStore() != getDataStore())
|
DataStore ds = getDataStore();
|
||||||
|
if (_uploadLogElement == null || _uploadLogElement.getDataStore() != ds)
|
||||||
{
|
{
|
||||||
_uploadLogElement = getDataStore().find(minerInfo, DE.A_NAME, "universal.uploadlog", 2); //$NON-NLS-1$
|
if (ds != null)
|
||||||
|
{
|
||||||
|
_uploadLogElement = ds.find(minerInfo, DE.A_NAME, "universal.uploadlog", 2); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return _uploadLogElement;
|
return _uploadLogElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DataElement getAttributes(String fileNameFilter, boolean showHidden)
|
protected DataElement getAttributes(String fileNameFilter, boolean showHidden)
|
||||||
{
|
{
|
||||||
DataElement attributes = getDataStore().createTransientObject(_filterAttributes);
|
DataStore ds = getDataStore();
|
||||||
String version = IServiceConstants.VERSION_1;
|
if (ds != null)
|
||||||
StringBuffer buffer = new StringBuffer();
|
{
|
||||||
String filter = ((fileNameFilter == null) ? "*" : fileNameFilter); //$NON-NLS-1$
|
DataElement attributes = ds.createTransientObject(_filterAttributes);
|
||||||
buffer.append(version).append(IServiceConstants.TOKEN_SEPARATOR).append(filter).append(IServiceConstants.TOKEN_SEPARATOR).append(showHidden);
|
String version = IServiceConstants.VERSION_1;
|
||||||
attributes.setAttribute(DE.A_SOURCE, buffer.toString());
|
StringBuffer buffer = new StringBuffer();
|
||||||
return attributes;
|
String filter = ((fileNameFilter == null) ? "*" : fileNameFilter); //$NON-NLS-1$
|
||||||
|
buffer.append(version).append(IServiceConstants.TOKEN_SEPARATOR).append(filter).append(IServiceConstants.TOKEN_SEPARATOR).append(showHidden);
|
||||||
|
attributes.setAttribute(DE.A_SOURCE, buffer.toString());
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -776,10 +799,17 @@ public class DStoreFileService extends AbstractDStoreService implements IFileSer
|
||||||
buf.append(name);
|
buf.append(name);
|
||||||
de = getElementFor(buf.toString());
|
de = getElementFor(buf.toString());
|
||||||
}
|
}
|
||||||
dsQueryCommand(de, IUniversalDataStoreConstants.C_QUERY_GET_REMOTE_OBJECT, monitor);
|
|
||||||
//getFile call should also need to convert this DataElement into a HostFile using
|
// with 207095, it's possible to get here unconnected such that there is no element
|
||||||
//convertToHostFile() call. This way, this DataElement will be put into _fileMap.
|
if (de != null) {
|
||||||
return convertToHostFile(de);
|
dsQueryCommand(de, IUniversalDataStoreConstants.C_QUERY_GET_REMOTE_OBJECT, monitor);
|
||||||
|
//getFile call should also need to convert this DataElement into a HostFile using
|
||||||
|
//convertToHostFile() call. This way, this DataElement will be put into _fileMap.
|
||||||
|
return convertToHostFile(de);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1284,6 +1314,12 @@ public class DStoreFileService extends AbstractDStoreService implements IFileSer
|
||||||
waitForInitialize(null);
|
waitForInitialize(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DataStore ds = getDataStore();
|
||||||
|
|
||||||
|
// with 207095, it's possible to get here when disconnected and no dstore
|
||||||
|
if (ds == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
String normalizedPath = PathUtility.normalizeUnknown(path);
|
String normalizedPath = PathUtility.normalizeUnknown(path);
|
||||||
DataElement element = (DataElement)_fileElementMap.get(normalizedPath);
|
DataElement element = (DataElement)_fileElementMap.get(normalizedPath);
|
||||||
|
@ -1295,7 +1331,7 @@ public class DStoreFileService extends AbstractDStoreService implements IFileSer
|
||||||
if (element == null || element.isDeleted())
|
if (element == null || element.isDeleted())
|
||||||
{
|
{
|
||||||
DataElement universaltemp = getMinerElement();
|
DataElement universaltemp = getMinerElement();
|
||||||
element = getDataStore().createObject(universaltemp, IUniversalDataStoreConstants.UNIVERSAL_FILTER_DESCRIPTOR, normalizedPath, normalizedPath, "", false); //$NON-NLS-1$
|
element = ds.createObject(universaltemp, IUniversalDataStoreConstants.UNIVERSAL_FILTER_DESCRIPTOR, normalizedPath, normalizedPath, "", false); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
@ -1314,8 +1350,11 @@ public class DStoreFileService extends AbstractDStoreService implements IFileSer
|
||||||
protected IHostFile[] fetch(String remoteParent, String fileFilter, String queryType, IProgressMonitor monitor)
|
protected IHostFile[] fetch(String remoteParent, String fileFilter, String queryType, IProgressMonitor monitor)
|
||||||
{
|
{
|
||||||
DataStore ds = getDataStore();
|
DataStore ds = getDataStore();
|
||||||
|
if (ds == null)
|
||||||
|
{
|
||||||
|
return new IHostFile[0];
|
||||||
|
}
|
||||||
|
|
||||||
// create filter descriptor
|
// create filter descriptor
|
||||||
DataElement deObj = getElementFor(remoteParent);
|
DataElement deObj = getElementFor(remoteParent);
|
||||||
if (deObj == null)
|
if (deObj == null)
|
||||||
|
@ -1343,15 +1382,16 @@ public class DStoreFileService extends AbstractDStoreService implements IFileSer
|
||||||
String remotePath = parent + getSeparator(parent) + name;
|
String remotePath = parent + getSeparator(parent) + name;
|
||||||
DataElement de = getElementFor(remotePath);
|
DataElement de = getElementFor(remotePath);
|
||||||
DataStore ds = de.getDataStore();
|
DataStore ds = de.getDataStore();
|
||||||
|
if (ds != null)
|
||||||
|
|
||||||
DataElement setCmd = getCommandDescriptor(de, IUniversalDataStoreConstants.C_SET_LASTMODIFIED);
|
|
||||||
if (setCmd != null)
|
|
||||||
{
|
{
|
||||||
// first modify the source attribute to temporarily be the date field
|
DataElement setCmd = getCommandDescriptor(de, IUniversalDataStoreConstants.C_SET_LASTMODIFIED);
|
||||||
de.setAttribute(DE.A_SOURCE, timestamp + ""); //$NON-NLS-1$
|
if (setCmd != null)
|
||||||
ds.command(setCmd, de, true);
|
{
|
||||||
return true;
|
// first modify the source attribute to temporarily be the date field
|
||||||
|
de.setAttribute(DE.A_SOURCE, timestamp + ""); //$NON-NLS-1$
|
||||||
|
ds.command(setCmd, de, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -1363,23 +1403,24 @@ public class DStoreFileService extends AbstractDStoreService implements IFileSer
|
||||||
String remotePath = parent + getSeparator(parent) + name;
|
String remotePath = parent + getSeparator(parent) + name;
|
||||||
DataElement de = getElementFor(remotePath);
|
DataElement de = getElementFor(remotePath);
|
||||||
DataStore ds = de.getDataStore();
|
DataStore ds = de.getDataStore();
|
||||||
|
if (ds != null)
|
||||||
|
|
||||||
DataElement setCmd = getCommandDescriptor(de, IUniversalDataStoreConstants.C_SET_READONLY);
|
|
||||||
if (setCmd != null)
|
|
||||||
{
|
{
|
||||||
String flag = readOnly ? "true" : "false"; //$NON-NLS-1$ //$NON-NLS-2$
|
DataElement setCmd = getCommandDescriptor(de, IUniversalDataStoreConstants.C_SET_READONLY);
|
||||||
de.setAttribute(DE.A_SOURCE, flag);
|
if (setCmd != null)
|
||||||
DataElement status = ds.command(setCmd, de, true);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
getStatusMonitor(ds).waitForUpdate(status);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
{
|
||||||
|
String flag = readOnly ? "true" : "false"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
|
de.setAttribute(DE.A_SOURCE, flag);
|
||||||
|
DataElement status = ds.command(setCmd, de, true);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
getStatusMonitor(ds).waitForUpdate(status);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1394,20 +1435,22 @@ public class DStoreFileService extends AbstractDStoreService implements IFileSer
|
||||||
if (remoteEncoding == null) {
|
if (remoteEncoding == null) {
|
||||||
|
|
||||||
DataStore ds = getDataStore();
|
DataStore ds = getDataStore();
|
||||||
|
if (ds != null)
|
||||||
|
{
|
||||||
|
DataElement encodingElement = ds.createObject(null, IUniversalDataStoreConstants.UNIVERSAL_TEMP_DESCRIPTOR, ""); //$NON-NLS-1$
|
||||||
|
|
||||||
|
DataElement queryCmd = ds.localDescriptorQuery(encodingElement.getDescriptor(),IUniversalDataStoreConstants.C_SYSTEM_ENCODING);
|
||||||
|
|
||||||
DataElement encodingElement = ds.createObject(null, IUniversalDataStoreConstants.UNIVERSAL_TEMP_DESCRIPTOR, ""); //$NON-NLS-1$
|
DataElement status = ds.command(queryCmd, encodingElement, true);
|
||||||
|
|
||||||
DataElement queryCmd = ds.localDescriptorQuery(encodingElement.getDescriptor(),IUniversalDataStoreConstants.C_SYSTEM_ENCODING);
|
try {
|
||||||
|
getStatusMonitor(ds).waitForUpdate(status);
|
||||||
DataElement status = ds.command(queryCmd, encodingElement, true);
|
}
|
||||||
|
catch (Exception e) {
|
||||||
try {
|
}
|
||||||
getStatusMonitor(ds).waitForUpdate(status);
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteEncoding = encodingElement.getValue();
|
remoteEncoding = encodingElement.getValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return remoteEncoding;
|
return remoteEncoding;
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Martin Oberhuber (Wind River) - [186128][refactoring] Move IProgressMonitor last in public base classes
|
* Martin Oberhuber (Wind River) - [186128][refactoring] Move IProgressMonitor last in public base classes
|
||||||
* David McKnight (IBM) - [190803] Canceling a long-running dstore job prints "InterruptedException" to stdout
|
* David McKnight (IBM) - [190803] Canceling a long-running dstore job prints "InterruptedException" to stdout
|
||||||
|
* David McKnight (IBM) - [207095] check for null datastore
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
package org.eclipse.rse.services.dstore;
|
package org.eclipse.rse.services.dstore;
|
||||||
|
@ -72,17 +73,26 @@ public abstract class AbstractDStoreService implements IDStoreService
|
||||||
|
|
||||||
protected DataElement getMinerElement(String id)
|
protected DataElement getMinerElement(String id)
|
||||||
{
|
{
|
||||||
return getDataStore().findMinerInformation(id);
|
DataStore ds = getDataStore();
|
||||||
|
if (ds != null)
|
||||||
|
{
|
||||||
|
return ds.findMinerInformation(id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DataElement[] dsQueryCommand(DataElement subject, ArrayList args, String command, IProgressMonitor monitor)
|
protected DataElement[] dsQueryCommand(DataElement subject, ArrayList args, String command, IProgressMonitor monitor)
|
||||||
{
|
{
|
||||||
// query roots
|
// query roots
|
||||||
DataElement queryCmd = getCommandDescriptor(subject, command);
|
DataElement queryCmd = getCommandDescriptor(subject, command);
|
||||||
|
DataStore ds = getDataStore();
|
||||||
|
|
||||||
if (queryCmd != null)
|
if (queryCmd != null && ds != null)
|
||||||
{
|
{
|
||||||
DataElement status = getDataStore().command(queryCmd, args, subject, true);
|
DataElement status = ds.command(queryCmd, args, subject, true);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
DStoreStatusMonitor smon = getStatusMonitor(getDataStore());
|
DStoreStatusMonitor smon = getStatusMonitor(getDataStore());
|
||||||
|
@ -118,10 +128,11 @@ public abstract class AbstractDStoreService implements IDStoreService
|
||||||
{
|
{
|
||||||
// query roots
|
// query roots
|
||||||
DataElement queryCmd = getCommandDescriptor(subject, command);
|
DataElement queryCmd = getCommandDescriptor(subject, command);
|
||||||
|
DataStore ds = getDataStore();
|
||||||
|
|
||||||
if (queryCmd != null)
|
if (queryCmd != null && ds != null)
|
||||||
{
|
{
|
||||||
DataElement status = getDataStore().command(queryCmd, args, subject, true);
|
DataElement status = ds.command(queryCmd, args, subject, true);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
DStoreStatusMonitor smon = getStatusMonitor(getDataStore());
|
DStoreStatusMonitor smon = getStatusMonitor(getDataStore());
|
||||||
|
@ -147,10 +158,9 @@ public abstract class AbstractDStoreService implements IDStoreService
|
||||||
{
|
{
|
||||||
// query roots
|
// query roots
|
||||||
DataElement queryCmd = getCommandDescriptor(subject, command);
|
DataElement queryCmd = getCommandDescriptor(subject, command);
|
||||||
|
DataStore ds = getDataStore();
|
||||||
if (queryCmd != null)
|
if (queryCmd != null && ds != null)
|
||||||
{
|
{
|
||||||
DataStore ds = getDataStore();
|
|
||||||
DataElement status = ds.command(queryCmd, subject, true);
|
DataElement status = ds.command(queryCmd, subject, true);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -181,11 +191,11 @@ public abstract class AbstractDStoreService implements IDStoreService
|
||||||
protected DataElement dsStatusCommand(DataElement subject, String command, IProgressMonitor monitor)
|
protected DataElement dsStatusCommand(DataElement subject, String command, IProgressMonitor monitor)
|
||||||
{
|
{
|
||||||
// query roots
|
// query roots
|
||||||
DataElement queryCmd = getCommandDescriptor(subject, command);
|
DataElement queryCmd = getCommandDescriptor(subject, command);
|
||||||
|
DataStore ds = getDataStore();
|
||||||
if (queryCmd != null)
|
|
||||||
{
|
if (queryCmd != null && ds != null)
|
||||||
DataStore ds = getDataStore();
|
{
|
||||||
DataElement status = ds.command(queryCmd, subject, true);
|
DataElement status = ds.command(queryCmd, subject, true);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -214,20 +224,29 @@ public abstract class AbstractDStoreService implements IDStoreService
|
||||||
DataElement cmd = (DataElement)_cmdDescriptorMap.get(command);
|
DataElement cmd = (DataElement)_cmdDescriptorMap.get(command);
|
||||||
if (cmd == null || ds != cmd.getDataStore())
|
if (cmd == null || ds != cmd.getDataStore())
|
||||||
{
|
{
|
||||||
cmd = getDataStore().localDescriptorQuery(subject.getDescriptor(), command);
|
if (ds != null)
|
||||||
_cmdDescriptorMap.put(command, cmd);
|
{
|
||||||
|
cmd = ds.localDescriptorQuery(subject.getDescriptor(), command);
|
||||||
|
_cmdDescriptorMap.put(command, cmd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getServerVersion()
|
public int getServerVersion()
|
||||||
{
|
{
|
||||||
return getDataStore().getServerVersion();
|
DataStore ds = getDataStore();
|
||||||
|
if (ds != null)
|
||||||
|
return ds.getServerVersion();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getServerMinor()
|
public int getServerMinor()
|
||||||
{
|
{
|
||||||
return getDataStore().getServerMinor();
|
DataStore ds = getDataStore();
|
||||||
|
if (ds != null)
|
||||||
|
return ds.getServerMinor();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void checkHostJVM()
|
protected void checkHostJVM()
|
||||||
|
@ -261,8 +280,12 @@ public abstract class AbstractDStoreService implements IDStoreService
|
||||||
{
|
{
|
||||||
if (_initializeStatus != null)
|
if (_initializeStatus != null)
|
||||||
{
|
{
|
||||||
DStoreStatusMonitor smon = getStatusMonitor(getDataStore());
|
DataStore ds = getDataStore();
|
||||||
return smon.determineStatusDone(_initializeStatus);
|
if (ds != null)
|
||||||
|
{
|
||||||
|
DStoreStatusMonitor smon = getStatusMonitor(ds);
|
||||||
|
return smon.determineStatusDone(_initializeStatus);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -271,24 +294,28 @@ public abstract class AbstractDStoreService implements IDStoreService
|
||||||
{
|
{
|
||||||
if (_initializeStatus != null)
|
if (_initializeStatus != null)
|
||||||
{
|
{
|
||||||
DStoreStatusMonitor smon = getStatusMonitor(getDataStore());
|
DataStore ds = getDataStore();
|
||||||
try
|
if (ds != null)
|
||||||
{
|
{
|
||||||
smon.waitForUpdate(_initializeStatus, monitor, 100);
|
DStoreStatusMonitor smon = getStatusMonitor(getDataStore());
|
||||||
}
|
try
|
||||||
catch (InterruptedException e)
|
|
||||||
{
|
|
||||||
// cancel monitor if it's still not canceled
|
|
||||||
if (monitor != null && !monitor.isCanceled())
|
|
||||||
{
|
{
|
||||||
monitor.setCanceled(true);
|
smon.waitForUpdate(_initializeStatus, monitor, 100);
|
||||||
}
|
}
|
||||||
|
catch (InterruptedException e)
|
||||||
|
{
|
||||||
|
// cancel monitor if it's still not canceled
|
||||||
|
if (monitor != null && !monitor.isCanceled())
|
||||||
|
{
|
||||||
|
monitor.setCanceled(true);
|
||||||
|
}
|
||||||
|
|
||||||
//InterruptedException is used to report user cancellation, so no need to log
|
//InterruptedException is used to report user cancellation, so no need to log
|
||||||
//This should be reviewed (use OperationCanceledException) with bug #190750
|
//This should be reviewed (use OperationCanceledException) with bug #190750
|
||||||
}
|
}
|
||||||
|
|
||||||
getMinerElement();
|
getMinerElement();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue