mirror of
https://github.com/gnustep/libs-base.git
synced 2025-05-10 16:20:42 +00:00
immutable proxy class added.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@12417 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
cb9ad2a35d
commit
f24a9196b0
5 changed files with 503 additions and 3 deletions
11
ChangeLog
11
ChangeLog
|
@ -1,3 +1,14 @@
|
|||
2002-02-06 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Headers/Foundation/NSString.m: Additioanl method -immutableProxy
|
||||
* Source/NSString.m: ditto
|
||||
* Source/GSString.m: New classes for immutable proxy, one generic
|
||||
version, and one efficient version for accessing the main GNUstep
|
||||
mutable striong objects.
|
||||
* Source/GSAttributedString: use immutable proxy.
|
||||
New code to provide a proxy for a mutable string which will provide
|
||||
read-only access to it.
|
||||
|
||||
2002-02-05 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Source/NSString.m: ([-rangeOfCharacterFromSet:options:range:])
|
||||
|
|
|
@ -380,6 +380,7 @@ extern struct objc_class _NSConstantStringClassReference;
|
|||
@end
|
||||
|
||||
@interface NSMutableString (GSString)
|
||||
- (NSString*) immutableProxy;
|
||||
- (void) removeSuffix: (NSString*)_suffix;
|
||||
- (void) removePrefix: (NSString*)_prefix;
|
||||
- (void) replaceString: (NSString*)_replace
|
||||
|
|
|
@ -75,6 +75,7 @@
|
|||
{
|
||||
NSMutableString *_textChars;
|
||||
NSMutableArray *_infoArray;
|
||||
NSString *_textProxy;
|
||||
}
|
||||
|
||||
- (id) initWithString: (NSString*)aString
|
||||
|
@ -580,7 +581,11 @@ SANITY();
|
|||
|
||||
- (NSString*) string
|
||||
{
|
||||
return _textChars;
|
||||
if (_textProxy == nil)
|
||||
{
|
||||
_textProxy = RETAIN([_textChars immutableProxy]);
|
||||
}
|
||||
return _textProxy;
|
||||
}
|
||||
|
||||
- (NSDictionary*) attributesAtIndex: (unsigned)index
|
||||
|
@ -845,6 +850,7 @@ SANITY();
|
|||
|
||||
- (void) dealloc
|
||||
{
|
||||
TEST_RELEASE(_textProxy);
|
||||
RELEASE(_textChars);
|
||||
RELEASE(_infoArray);
|
||||
[super dealloc];
|
||||
|
|
|
@ -209,6 +209,7 @@ static Class GSUnicodeInlineStringClass = 0;
|
|||
static Class GSMutableStringClass = 0;
|
||||
static Class NSConstantStringClass = 0;
|
||||
|
||||
static SEL cMemberSel;
|
||||
static SEL convertSel;
|
||||
static BOOL (*convertImp)(id, SEL, NSStringEncoding);
|
||||
static SEL equalSel;
|
||||
|
@ -220,7 +221,7 @@ static NSStringEncoding defEnc = 0;
|
|||
|
||||
/*
|
||||
* The setup() function is called when any concrete string class is
|
||||
* initialized, and cached classes and some method implementations.
|
||||
* initialized, and caches classes and some method implementations.
|
||||
*/
|
||||
static void
|
||||
setup()
|
||||
|
@ -254,6 +255,7 @@ setup()
|
|||
* cases where we want to use the implementation
|
||||
* provided in the abstract rolot cllass of the cluster.
|
||||
*/
|
||||
cMemberSel = @selector(characterIsMember:);
|
||||
convertSel = @selector(canBeConvertedToEncoding:);
|
||||
convertImp = (BOOL (*)(id, SEL, NSStringEncoding))
|
||||
[NSStringClass instanceMethodForSelector: convertSel];
|
||||
|
@ -1310,6 +1312,96 @@ rangeOfSequence_u(ivars self, unsigned anIndex)
|
|||
return (NSRange){start, end-start};
|
||||
}
|
||||
|
||||
static inline NSRange
|
||||
rangeOfCharacter_c(ivars self, NSCharacterSet *aSet, unsigned mask,
|
||||
NSRange aRange)
|
||||
{
|
||||
int i;
|
||||
int start;
|
||||
int stop;
|
||||
int step;
|
||||
NSRange range;
|
||||
BOOL (*mImp)(id, SEL, unichar);
|
||||
|
||||
if (aSet == nil)
|
||||
[NSException raise: NSInvalidArgumentException format: @"range of nil"];
|
||||
i = self->_count;
|
||||
|
||||
if ((mask & NSBackwardsSearch) == NSBackwardsSearch)
|
||||
{
|
||||
start = NSMaxRange(aRange)-1; stop = aRange.location-1; step = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = aRange.location; stop = NSMaxRange(aRange); step = 1;
|
||||
}
|
||||
range.location = NSNotFound;
|
||||
range.length = 0;
|
||||
|
||||
mImp = (BOOL(*)(id,SEL,unichar))
|
||||
[aSet methodForSelector: cMemberSel];
|
||||
|
||||
for (i = start; i != stop; i += step)
|
||||
{
|
||||
unichar letter = self->_contents.c[i];
|
||||
|
||||
if (letter > 127)
|
||||
{
|
||||
letter = encode_chartouni(letter, defEnc);
|
||||
}
|
||||
if ((*mImp)(aSet, cMemberSel, letter))
|
||||
{
|
||||
range = NSMakeRange(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
static inline NSRange
|
||||
rangeOfCharacter_u(ivars self, NSCharacterSet *aSet, unsigned mask,
|
||||
NSRange aRange)
|
||||
{
|
||||
int i;
|
||||
int start;
|
||||
int stop;
|
||||
int step;
|
||||
NSRange range;
|
||||
BOOL (*mImp)(id, SEL, unichar);
|
||||
|
||||
if (aSet == nil)
|
||||
[NSException raise: NSInvalidArgumentException format: @"range of nil"];
|
||||
i = self->_count;
|
||||
|
||||
if ((mask & NSBackwardsSearch) == NSBackwardsSearch)
|
||||
{
|
||||
start = NSMaxRange(aRange)-1; stop = aRange.location-1; step = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = aRange.location; stop = NSMaxRange(aRange); step = 1;
|
||||
}
|
||||
range.location = NSNotFound;
|
||||
range.length = 0;
|
||||
|
||||
mImp = (BOOL(*)(id,SEL,unichar))
|
||||
[aSet methodForSelector: cMemberSel];
|
||||
|
||||
for (i = start; i != stop; i += step)
|
||||
{
|
||||
unichar letter = self->_contents.u[i];
|
||||
|
||||
if ((*mImp)(aSet, cMemberSel, letter))
|
||||
{
|
||||
range = NSMakeRange(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
static inline NSRange
|
||||
rangeOfString_c(ivars self, NSString *aString, unsigned mask, NSRange aRange)
|
||||
{
|
||||
|
@ -1766,6 +1858,14 @@ transmute(ivars self, NSString *aString)
|
|||
return rangeOfSequence_c((ivars)self, anIndex);
|
||||
}
|
||||
|
||||
- (NSRange) rangeOfCharacterFromSet: (NSCharacterSet*)aSet
|
||||
options: (unsigned)mask
|
||||
range: (NSRange)aRange
|
||||
{
|
||||
GS_RANGE_CHECK(aRange, _count);
|
||||
return rangeOfCharacter_c((ivars)self, aSet, mask, aRange);
|
||||
}
|
||||
|
||||
- (NSRange) rangeOfString: (NSString*)aString
|
||||
options: (unsigned)mask
|
||||
range: (NSRange)aRange
|
||||
|
@ -2060,6 +2160,14 @@ transmute(ivars self, NSString *aString)
|
|||
return rangeOfSequence_u((ivars)self, anIndex);
|
||||
}
|
||||
|
||||
- (NSRange) rangeOfCharacterFromSet: (NSCharacterSet*)aSet
|
||||
options: (unsigned)mask
|
||||
range: (NSRange)aRange
|
||||
{
|
||||
GS_RANGE_CHECK(aRange, _count);
|
||||
return rangeOfCharacter_u((ivars)self, aSet, mask, aRange);
|
||||
}
|
||||
|
||||
- (NSRange) rangeOfString: (NSString*)aString
|
||||
options: (unsigned)mask
|
||||
range: (NSRange)aRange
|
||||
|
@ -2162,7 +2270,7 @@ transmute(ivars self, NSString *aString)
|
|||
* The GSMutableStrinc class shares a common initial ivar layout with
|
||||
* the GSString class, but adds a few of its own. It uses _flags.wide
|
||||
* to determine whether it should use 8-bit or 16-bit characters and
|
||||
* is caapable of changing that flag (and its underlying storage) to
|
||||
* is capable of changing that flag (and its underlying storage) to
|
||||
* move from an 8-bit to a 16-bit representation is that should be
|
||||
* necessary because wide characters have been placed in the string.
|
||||
*/
|
||||
|
@ -2602,6 +2710,17 @@ transmute(ivars self, NSString *aString)
|
|||
return rangeOfSequence_c((ivars)self, anIndex);
|
||||
}
|
||||
|
||||
- (NSRange) rangeOfCharacterFromSet: (NSCharacterSet*)aSet
|
||||
options: (unsigned)mask
|
||||
range: (NSRange)aRange
|
||||
{
|
||||
GS_RANGE_CHECK(aRange, _count);
|
||||
if (_flags.wide == 1)
|
||||
return rangeOfCharacter_u((ivars)self, aSet, mask, aRange);
|
||||
else
|
||||
return rangeOfCharacter_c((ivars)self, aSet, mask, aRange);
|
||||
}
|
||||
|
||||
- (NSRange) rangeOfString: (NSString*)aString
|
||||
options: (unsigned)mask
|
||||
range: (NSRange)aRange
|
||||
|
@ -2839,6 +2958,336 @@ transmute(ivars self, NSString *aString)
|
|||
|
||||
|
||||
|
||||
@interface NSImmutableString: NSString
|
||||
{
|
||||
id _parent;
|
||||
}
|
||||
- (id) initWithString: (NSString*)parent;
|
||||
@end
|
||||
|
||||
@interface GSImmutableString: NSImmutableString
|
||||
@end
|
||||
|
||||
@implementation NSImmutableString
|
||||
|
||||
- (BOOL) canBeConvertedToEncoding: (NSStringEncoding)enc
|
||||
{
|
||||
return [_parent canBeConvertedToEncoding: enc];
|
||||
}
|
||||
|
||||
- (unichar) characterAtIndex: (unsigned int)index
|
||||
{
|
||||
return [_parent characterAtIndex: index];
|
||||
}
|
||||
|
||||
- (NSComparisonResult) compare: (NSString*)aString
|
||||
options: (unsigned int)mask
|
||||
range: (NSRange)aRange
|
||||
{
|
||||
return [_parent compare: aString options: mask range: aRange];
|
||||
}
|
||||
|
||||
- (const char *) cString
|
||||
{
|
||||
return [_parent cString];
|
||||
}
|
||||
|
||||
- (unsigned int) cStringLength
|
||||
{
|
||||
return [_parent cStringLength];
|
||||
}
|
||||
|
||||
- (NSData*) dataUsingEncoding: (NSStringEncoding)encoding
|
||||
allowLossyConversion: (BOOL)flag
|
||||
{
|
||||
return [_parent dataUsingEncoding: encoding allowLossyConversion: flag];
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
RELEASE(_parent);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) encodeWithCoder: (NSCoder*)aCoder
|
||||
{
|
||||
[_parent encodeWithCoder: aCoder];
|
||||
}
|
||||
|
||||
- (NSStringEncoding) fastestEncoding
|
||||
{
|
||||
return [_parent fastestEncoding];
|
||||
}
|
||||
|
||||
- (void) getCharacters: (unichar*)buffer
|
||||
{
|
||||
return [_parent getCharacters: buffer];
|
||||
}
|
||||
|
||||
- (void) getCharacters: (unichar*)buffer range: (NSRange)aRange
|
||||
{
|
||||
return [_parent getCharacters: buffer range: aRange];
|
||||
}
|
||||
|
||||
- (void) getCString: (char*)buffer
|
||||
{
|
||||
[_parent getCString: buffer];
|
||||
}
|
||||
|
||||
- (void) getCString: (char*)buffer
|
||||
maxLength: (unsigned int)maxLength
|
||||
{
|
||||
[_parent getCString: buffer maxLength: maxLength];
|
||||
}
|
||||
|
||||
- (void) getCString: (char*)buffer
|
||||
maxLength: (unsigned int)maxLength
|
||||
range: (NSRange)aRange
|
||||
remainingRange: (NSRange*)leftoverRange
|
||||
{
|
||||
[_parent getCString: buffer
|
||||
maxLength: maxLength
|
||||
range: aRange
|
||||
remainingRange: leftoverRange];
|
||||
}
|
||||
|
||||
- (unsigned) hash
|
||||
{
|
||||
return [_parent hash];
|
||||
}
|
||||
|
||||
- (id) initWithString: (NSString*)parent
|
||||
{
|
||||
_parent = RETAIN(parent);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL) isEqual: (id)anObject
|
||||
{
|
||||
return [_parent isEqual: anObject];
|
||||
}
|
||||
|
||||
- (BOOL) isEqualToString: (NSString*)anObject
|
||||
{
|
||||
return [_parent isEqualToString: anObject];
|
||||
}
|
||||
|
||||
- (unsigned int) length
|
||||
{
|
||||
return [_parent length];
|
||||
}
|
||||
|
||||
- (const char*) lossyCString
|
||||
{
|
||||
return [_parent lossyCString];
|
||||
}
|
||||
|
||||
- (NSRange) rangeOfComposedCharacterSequenceAtIndex: (unsigned)anIndex
|
||||
{
|
||||
return [_parent rangeOfComposedCharacterSequenceAtIndex: anIndex];
|
||||
}
|
||||
|
||||
- (NSRange) rangeOfCharacterFromSet: (NSCharacterSet*)aSet
|
||||
options: (unsigned)mask
|
||||
range: (NSRange)aRange
|
||||
{
|
||||
return [_parent rangeOfCharacterFromSet: aSet options: mask range: aRange];
|
||||
}
|
||||
|
||||
- (NSRange) rangeOfString: (NSString*)aString
|
||||
options: (unsigned)mask
|
||||
range: (NSRange)aRange
|
||||
{
|
||||
return [_parent rangeOfString: aString options: mask range: aRange];
|
||||
}
|
||||
|
||||
- (NSStringEncoding) smallestEncoding
|
||||
{
|
||||
return [_parent smallestEncoding];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation GSImmutableString
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
setup();
|
||||
}
|
||||
|
||||
- (BOOL) canBeConvertedToEncoding: (NSStringEncoding)enc
|
||||
{
|
||||
if (((ivars)_parent)->_flags.wide == 1)
|
||||
return canBeConvertedToEncoding_u((ivars)_parent, enc);
|
||||
else
|
||||
return canBeConvertedToEncoding_c((ivars)_parent, enc);
|
||||
}
|
||||
|
||||
- (unichar) characterAtIndex: (unsigned int)index
|
||||
{
|
||||
if (((ivars)_parent)->_flags.wide == 1)
|
||||
return characterAtIndex_u((ivars)_parent, index);
|
||||
else
|
||||
return characterAtIndex_c((ivars)_parent, index);
|
||||
}
|
||||
|
||||
- (NSComparisonResult) compare: (NSString*)aString
|
||||
options: (unsigned int)mask
|
||||
range: (NSRange)aRange
|
||||
{
|
||||
if (((ivars)_parent)->_flags.wide == 1)
|
||||
return compare_u((ivars)_parent, aString, mask, aRange);
|
||||
else
|
||||
return compare_c((ivars)_parent, aString, mask, aRange);
|
||||
}
|
||||
|
||||
- (const char *) cString
|
||||
{
|
||||
if (((ivars)_parent)->_flags.wide == 1)
|
||||
return cString_u((ivars)_parent);
|
||||
else
|
||||
return cString_c((ivars)_parent);
|
||||
}
|
||||
|
||||
- (unsigned int) cStringLength
|
||||
{
|
||||
if (((ivars)_parent)->_flags.wide == 1)
|
||||
return cStringLength_u((ivars)_parent);
|
||||
else
|
||||
return cStringLength_c((ivars)_parent);
|
||||
}
|
||||
|
||||
- (NSData*) dataUsingEncoding: (NSStringEncoding)encoding
|
||||
allowLossyConversion: (BOOL)flag
|
||||
{
|
||||
if (((ivars)_parent)->_flags.wide == 1)
|
||||
return dataUsingEncoding_u((ivars)_parent, encoding, flag);
|
||||
else
|
||||
return dataUsingEncoding_c((ivars)_parent, encoding, flag);
|
||||
}
|
||||
|
||||
- (void) encodeWithCoder: (NSCoder*)aCoder
|
||||
{
|
||||
[_parent encodeWithCoder: aCoder];
|
||||
}
|
||||
|
||||
- (NSStringEncoding) fastestEncoding
|
||||
{
|
||||
if (((ivars)_parent)->_flags.wide == 1)
|
||||
return NSUnicodeStringEncoding;
|
||||
else
|
||||
return defEnc;
|
||||
}
|
||||
|
||||
- (void) getCharacters: (unichar*)buffer
|
||||
{
|
||||
if (((ivars)_parent)->_flags.wide == 1)
|
||||
{
|
||||
getCharacters_u((ivars)_parent, buffer,
|
||||
(NSRange){0, ((ivars)_parent)->_count});
|
||||
}
|
||||
else
|
||||
{
|
||||
getCharacters_c((ivars)_parent, buffer,
|
||||
(NSRange){0, ((ivars)_parent)->_count});
|
||||
}
|
||||
}
|
||||
|
||||
- (void) getCharacters: (unichar*)buffer range: (NSRange)aRange
|
||||
{
|
||||
GS_RANGE_CHECK(aRange, ((ivars)_parent)->_count);
|
||||
if (((ivars)_parent)->_flags.wide == 1)
|
||||
{
|
||||
getCharacters_u((ivars)_parent, buffer, aRange);
|
||||
}
|
||||
else
|
||||
{
|
||||
getCharacters_c((ivars)_parent, buffer, aRange);
|
||||
}
|
||||
}
|
||||
|
||||
- (unsigned) hash
|
||||
{
|
||||
if (((ivars)_parent)->_flags.hash == 0)
|
||||
{
|
||||
((ivars)_parent)->_flags.hash = (*hashImp)((id)_parent, hashSel);
|
||||
}
|
||||
return ((ivars)_parent)->_flags.hash;
|
||||
}
|
||||
|
||||
- (BOOL) isEqual: (id)anObject
|
||||
{
|
||||
if (((ivars)_parent)->_flags.wide == 1)
|
||||
return isEqual_u((ivars)_parent, anObject);
|
||||
else
|
||||
return isEqual_c((ivars)_parent, anObject);
|
||||
}
|
||||
|
||||
- (BOOL) isEqualToString: (NSString*)anObject
|
||||
{
|
||||
if (((ivars)_parent)->_flags.wide == 1)
|
||||
return isEqual_u((ivars)_parent, anObject);
|
||||
else
|
||||
return isEqual_c((ivars)_parent, anObject);
|
||||
}
|
||||
|
||||
- (unsigned int) length
|
||||
{
|
||||
return ((ivars)_parent)->_count;
|
||||
}
|
||||
|
||||
- (const char*) lossyCString
|
||||
{
|
||||
if (((ivars)_parent)->_flags.wide == 1)
|
||||
return lossyCString_u((ivars)_parent);
|
||||
else
|
||||
return lossyCString_c((ivars)_parent);
|
||||
}
|
||||
|
||||
- (NSRange) rangeOfComposedCharacterSequenceAtIndex: (unsigned)anIndex
|
||||
{
|
||||
if (((ivars)_parent)->_flags.wide == 1)
|
||||
return rangeOfSequence_u((ivars)_parent, anIndex);
|
||||
else
|
||||
return rangeOfSequence_c((ivars)_parent, anIndex);
|
||||
}
|
||||
|
||||
- (NSRange) rangeOfCharacterFromSet: (NSCharacterSet*)aSet
|
||||
options: (unsigned)mask
|
||||
range: (NSRange)aRange
|
||||
{
|
||||
GS_RANGE_CHECK(aRange, ((ivars)_parent)->_count);
|
||||
if (((ivars)_parent)->_flags.wide == 1)
|
||||
return rangeOfCharacter_u((ivars)_parent, aSet, mask, aRange);
|
||||
else
|
||||
return rangeOfCharacter_c((ivars)_parent, aSet, mask, aRange);
|
||||
}
|
||||
|
||||
- (NSRange) rangeOfString: (NSString*)aString
|
||||
options: (unsigned)mask
|
||||
range: (NSRange)aRange
|
||||
{
|
||||
if (((ivars)_parent)->_flags.wide == 1)
|
||||
return rangeOfString_u((ivars)_parent, aString, mask, aRange);
|
||||
else
|
||||
return rangeOfString_c((ivars)_parent, aString, mask, aRange);
|
||||
}
|
||||
|
||||
- (NSStringEncoding) smallestEncoding
|
||||
{
|
||||
if (((ivars)_parent)->_flags.wide == 1)
|
||||
{
|
||||
return NSUnicodeStringEncoding;
|
||||
}
|
||||
else
|
||||
return defEnc;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* The NXConstantString class is used by the compiler for constant
|
||||
* strings, as such its ivar layout is determined by the compiler
|
||||
|
|
|
@ -3780,6 +3780,31 @@ handle_printf_atsign (FILE *stream,
|
|||
@end
|
||||
|
||||
@implementation NSMutableString (GNUstep)
|
||||
@class NSImmutableString;
|
||||
@class GSImmutableString;
|
||||
/**
|
||||
* Returns a proxy to the receiver which will allow access to the
|
||||
* receiver as an NSString, but which will not allow any of the
|
||||
* extra NSMutableString methods to be used. You can use this method
|
||||
* to provide other code with read-only access to a mutable string
|
||||
* you own.
|
||||
*/
|
||||
- (NSString*) immutableProxy
|
||||
{
|
||||
if ([self isKindOfClass: GSMutableStringClass])
|
||||
{
|
||||
return AUTORELEASE([[GSImmutableString alloc] initWithString: self]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return AUTORELEASE([[NSImmutableString alloc] initWithString: self]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified suffix from the string. Raises an exception
|
||||
* if the prefix is not present.
|
||||
*/
|
||||
- (void) removeSuffix: (NSString*)_suffix
|
||||
{
|
||||
NSCAssert2([self hasSuffix: _suffix],
|
||||
|
@ -3788,6 +3813,10 @@ handle_printf_atsign (FILE *stream,
|
|||
NSMakeRange([self length] - [_suffix length], [_suffix length])];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified prefx from the string. Raises an exception
|
||||
* if the prefix is not present.
|
||||
*/
|
||||
- (void) removePrefix: (NSString*)_prefix;
|
||||
{
|
||||
NSCAssert2([self hasPrefix: _prefix],
|
||||
|
@ -3795,6 +3824,10 @@ handle_printf_atsign (FILE *stream,
|
|||
[self deleteCharactersInRange: NSMakeRange(0, [_prefix length])];
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all occurrances of the string replace with the string by
|
||||
* in the receiver.
|
||||
*/
|
||||
- (void) replaceString: (NSString*)replace
|
||||
withString: (NSString*)by
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue