feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.xml.internal.bind.api.impl;
|
||||
|
||||
import javax.lang.model.SourceVersion;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* Converts aribitrary strings into Java identifiers.
|
||||
*
|
||||
* @author
|
||||
* <a href="mailto:kohsuke.kawaguchi@sun.com">Kohsuke KAWAGUCHI</a>
|
||||
*/
|
||||
public interface NameConverter
|
||||
{
|
||||
/**
|
||||
* converts a string into an identifier suitable for classes.
|
||||
*
|
||||
* In general, this operation should generate "NamesLikeThis".
|
||||
*/
|
||||
String toClassName( String token );
|
||||
|
||||
/**
|
||||
* converts a string into an identifier suitable for interfaces.
|
||||
*
|
||||
* In general, this operation should generate "NamesLikeThis".
|
||||
* But for example, it can prepend every interface with 'I'.
|
||||
*/
|
||||
String toInterfaceName( String token );
|
||||
|
||||
/**
|
||||
* converts a string into an identifier suitable for properties.
|
||||
*
|
||||
* In general, this operation should generate "NamesLikeThis",
|
||||
* which will be used with known prefixes like "get" or "set".
|
||||
*/
|
||||
String toPropertyName( String token );
|
||||
|
||||
/**
|
||||
* converts a string into an identifier suitable for constants.
|
||||
*
|
||||
* In the standard Java naming convention, this operation should
|
||||
* generate "NAMES_LIKE_THIS".
|
||||
*/
|
||||
String toConstantName( String token );
|
||||
|
||||
/**
|
||||
* Converts a string into an identifier suitable for variables.
|
||||
*
|
||||
* In general it should generate "namesLikeThis".
|
||||
*/
|
||||
String toVariableName( String token );
|
||||
|
||||
/**
|
||||
* Converts a namespace URI into a package name.
|
||||
* This method should expect strings like
|
||||
* "http://foo.bar.zot/org", "urn:abc:def:ghi" "", or even "###"
|
||||
* (basically anything) and expected to return a package name,
|
||||
* liks "org.acme.foo".
|
||||
*
|
||||
*/
|
||||
String toPackageName( String namespaceUri );
|
||||
|
||||
/**
|
||||
* The name converter implemented by Code Model.
|
||||
*
|
||||
* This is the standard name conversion for JAXB.
|
||||
*/
|
||||
public static final NameConverter standard = new Standard();
|
||||
|
||||
static class Standard extends NameUtil implements NameConverter {
|
||||
public String toClassName(String s) {
|
||||
return toMixedCaseName(toWordList(s), true);
|
||||
}
|
||||
public String toVariableName(String s) {
|
||||
return toMixedCaseName(toWordList(s), false);
|
||||
}
|
||||
public String toInterfaceName( String token ) {
|
||||
return toClassName(token);
|
||||
}
|
||||
public String toPropertyName(String s) {
|
||||
String prop = toClassName(s);
|
||||
// property name "Class" with collide with Object.getClass,
|
||||
// so escape this.
|
||||
if(prop.equals("Class"))
|
||||
prop = "Clazz";
|
||||
return prop;
|
||||
}
|
||||
public String toConstantName( String token ) {
|
||||
return super.toConstantName(token);
|
||||
}
|
||||
/**
|
||||
* Computes a Java package name from a namespace URI,
|
||||
* as specified in the spec.
|
||||
*
|
||||
* @return
|
||||
* null if it fails to derive a package name.
|
||||
*/
|
||||
public String toPackageName( String nsUri ) {
|
||||
// remove scheme and :, if present
|
||||
// spec only requires us to remove 'http' and 'urn'...
|
||||
int idx = nsUri.indexOf(':');
|
||||
String scheme = "";
|
||||
if(idx>=0) {
|
||||
scheme = nsUri.substring(0,idx);
|
||||
if( scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("urn") )
|
||||
nsUri = nsUri.substring(idx+1);
|
||||
}
|
||||
|
||||
// tokenize string
|
||||
ArrayList<String> tokens = tokenize( nsUri, "/: " );
|
||||
if( tokens.size() == 0 ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// remove trailing file type, if necessary
|
||||
if( tokens.size() > 1 ) {
|
||||
// for uri's like "www.foo.com" and "foo.com", there is no trailing
|
||||
// file, so there's no need to look at the last '.' and substring
|
||||
// otherwise, we loose the "com" (which would be wrong)
|
||||
String lastToken = tokens.get( tokens.size()-1 );
|
||||
idx = lastToken.lastIndexOf( '.' );
|
||||
if( idx > 0 ) {
|
||||
lastToken = lastToken.substring( 0, idx );
|
||||
tokens.set( tokens.size()-1, lastToken );
|
||||
}
|
||||
}
|
||||
|
||||
// tokenize domain name and reverse. Also remove :port if it exists
|
||||
String domain = tokens.get( 0 );
|
||||
idx = domain.indexOf(':');
|
||||
if( idx >= 0) domain = domain.substring(0, idx);
|
||||
ArrayList<String> r = reverse( tokenize( domain, scheme.equals("urn")?".-":"." ) );
|
||||
if( r.get( r.size()-1 ).equalsIgnoreCase( "www" ) ) {
|
||||
// remove leading www
|
||||
r.remove( r.size()-1 );
|
||||
}
|
||||
|
||||
// replace the domain name with tokenized items
|
||||
tokens.addAll( 1, r );
|
||||
tokens.remove( 0 );
|
||||
|
||||
// iterate through the tokens and apply xml->java name algorithm
|
||||
for( int i = 0; i < tokens.size(); i++ ) {
|
||||
|
||||
// get the token and remove illegal chars
|
||||
String token = tokens.get( i );
|
||||
token = removeIllegalIdentifierChars( token );
|
||||
|
||||
// this will check for reserved keywords
|
||||
if (SourceVersion.isKeyword(token.toLowerCase())) {
|
||||
token = '_' + token;
|
||||
}
|
||||
|
||||
tokens.set( i, token.toLowerCase() );
|
||||
}
|
||||
|
||||
// concat all the pieces and return it
|
||||
return combine( tokens, '.' );
|
||||
}
|
||||
|
||||
|
||||
private static String removeIllegalIdentifierChars(String token) {
|
||||
StringBuilder newToken = new StringBuilder(token.length() + 1); // max expected length
|
||||
for( int i = 0; i < token.length(); i++ ) {
|
||||
char c = token.charAt( i );
|
||||
if (i == 0 && !Character.isJavaIdentifierStart(c)) { // c can't be used as FIRST char
|
||||
newToken.append('_');
|
||||
}
|
||||
if (!Character.isJavaIdentifierPart(c)) { // c can't be used
|
||||
newToken.append('_');
|
||||
} else {
|
||||
newToken.append(c); // c is valid
|
||||
}
|
||||
}
|
||||
return newToken.toString();
|
||||
}
|
||||
|
||||
|
||||
private static ArrayList<String> tokenize( String str, String sep ) {
|
||||
StringTokenizer tokens = new StringTokenizer(str,sep);
|
||||
ArrayList<String> r = new ArrayList<String>();
|
||||
|
||||
while(tokens.hasMoreTokens())
|
||||
r.add( tokens.nextToken() );
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
private static <T> ArrayList<T> reverse( List<T> a ) {
|
||||
ArrayList<T> r = new ArrayList<T>();
|
||||
|
||||
for( int i=a.size()-1; i>=0; i-- )
|
||||
r.add( a.get(i) );
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
private static String combine( List r, char sep ) {
|
||||
StringBuilder buf = new StringBuilder(r.get(0).toString());
|
||||
|
||||
for( int i=1; i<r.size(); i++ ) {
|
||||
buf.append(sep);
|
||||
buf.append(r.get(i));
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JAX-PRC compatible name converter implementation.
|
||||
*
|
||||
* The only difference is that we treat '_' as a valid character
|
||||
* and not as a word separator.
|
||||
*/
|
||||
public static final NameConverter jaxrpcCompatible = new Standard() {
|
||||
protected boolean isPunct(char c) {
|
||||
return (c == '.' || c == '-' || c == ';' /*|| c == '_'*/ || c == '\u00b7'
|
||||
|| c == '\u0387' || c == '\u06dd' || c == '\u06de');
|
||||
}
|
||||
protected boolean isLetter(char c) {
|
||||
return super.isLetter(c) || c=='_';
|
||||
}
|
||||
|
||||
protected int classify(char c0) {
|
||||
if(c0=='_') return NameUtil.OTHER_LETTER;
|
||||
return super.classify(c0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Smarter converter used for RELAX NG support.
|
||||
*/
|
||||
public static final NameConverter smart = new Standard() {
|
||||
public String toConstantName( String token ) {
|
||||
String name = super.toConstantName(token);
|
||||
if(!SourceVersion.isKeyword(name))
|
||||
return name;
|
||||
else
|
||||
return '_'+name;
|
||||
}
|
||||
};
|
||||
}
|
||||
328
jdkSrc/jdk8/com/sun/xml/internal/bind/api/impl/NameUtil.java
Normal file
328
jdkSrc/jdk8/com/sun/xml/internal/bind/api/impl/NameUtil.java
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.xml.internal.bind.api.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Methods that convert strings into various formats.
|
||||
*
|
||||
* <p>
|
||||
* What JAX-RPC name binding tells us is that even such basic method
|
||||
* like "isLetter" can be different depending on the situation.
|
||||
*
|
||||
* For this reason, a whole lot of methods are made non-static,
|
||||
* even though they look like they should be static.
|
||||
*/
|
||||
class NameUtil {
|
||||
protected boolean isPunct(char c) {
|
||||
return c == '-' || c == '.' || c == ':' || c == '_' || c == '\u00b7' || c == '\u0387' || c == '\u06dd' || c == '\u06de';
|
||||
}
|
||||
|
||||
protected static boolean isDigit(char c) {
|
||||
return c >= '0' && c <= '9' || Character.isDigit(c);
|
||||
}
|
||||
|
||||
protected static boolean isUpper(char c) {
|
||||
return c >= 'A' && c <= 'Z' || Character.isUpperCase(c);
|
||||
}
|
||||
|
||||
protected static boolean isLower(char c) {
|
||||
return c >= 'a' && c <= 'z' || Character.isLowerCase(c);
|
||||
}
|
||||
|
||||
protected boolean isLetter(char c) {
|
||||
return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || Character.isLetter(c);
|
||||
}
|
||||
|
||||
private String toLowerCase(String s)
|
||||
{
|
||||
return s.toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
private String toUpperCase(char c)
|
||||
{
|
||||
return String.valueOf(c).toUpperCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
private String toUpperCase(String s)
|
||||
{
|
||||
return s.toUpperCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Capitalizes the first character of the specified string,
|
||||
* and de-capitalize the rest of characters.
|
||||
*/
|
||||
public String capitalize(String s) {
|
||||
if (!isLower(s.charAt(0)))
|
||||
return s;
|
||||
StringBuilder sb = new StringBuilder(s.length());
|
||||
sb.append(toUpperCase(s.charAt(0)));
|
||||
sb.append(toLowerCase(s.substring(1)));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// Precondition: s[start] is not punctuation
|
||||
private int nextBreak(String s, int start) {
|
||||
int n = s.length();
|
||||
|
||||
char c1 = s.charAt(start);
|
||||
int t1 = classify(c1);
|
||||
|
||||
for (int i=start+1; i<n; i++) {
|
||||
// shift (c1,t1) into (c0,t0)
|
||||
// char c0 = c1; --- conceptually, but c0 won't be used
|
||||
int t0 = t1;
|
||||
|
||||
c1 = s.charAt(i);
|
||||
t1 = classify(c1);
|
||||
|
||||
switch(actionTable[t0*5+t1]) {
|
||||
case ACTION_CHECK_PUNCT:
|
||||
if(isPunct(c1)) return i;
|
||||
break;
|
||||
case ACTION_CHECK_C2:
|
||||
if (i < n-1) {
|
||||
char c2 = s.charAt(i+1);
|
||||
if (isLower(c2))
|
||||
return i;
|
||||
}
|
||||
break;
|
||||
case ACTION_BREAK:
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// the 5-category classification that we use in this code
|
||||
// to find work breaks
|
||||
static protected final int UPPER_LETTER = 0;
|
||||
static protected final int LOWER_LETTER = 1;
|
||||
static protected final int OTHER_LETTER = 2;
|
||||
static protected final int DIGIT = 3;
|
||||
static protected final int OTHER = 4;
|
||||
|
||||
/**
|
||||
* Look up table for actions.
|
||||
* type0*5+type1 would yield the action to be taken.
|
||||
*/
|
||||
private static final byte[] actionTable = new byte[5*5];
|
||||
|
||||
// action constants. see nextBreak for the meaning
|
||||
static private final byte ACTION_CHECK_PUNCT = 0;
|
||||
static private final byte ACTION_CHECK_C2 = 1;
|
||||
static private final byte ACTION_BREAK = 2;
|
||||
static private final byte ACTION_NOBREAK = 3;
|
||||
|
||||
/**
|
||||
* Decide the action to be taken given
|
||||
* the classification of the preceding character 't0' and
|
||||
* the classification of the next character 't1'.
|
||||
*/
|
||||
private static byte decideAction( int t0, int t1 ) {
|
||||
if(t0==OTHER && t1==OTHER) return ACTION_CHECK_PUNCT;
|
||||
if(!xor(t0==DIGIT,t1==DIGIT)) return ACTION_BREAK;
|
||||
if(t0==LOWER_LETTER && t1!=LOWER_LETTER) return ACTION_BREAK;
|
||||
if(!xor(t0<=OTHER_LETTER,t1<=OTHER_LETTER)) return ACTION_BREAK;
|
||||
if(!xor(t0==OTHER_LETTER,t1==OTHER_LETTER)) return ACTION_BREAK;
|
||||
|
||||
if(t0==UPPER_LETTER && t1==UPPER_LETTER) return ACTION_CHECK_C2;
|
||||
|
||||
return ACTION_NOBREAK;
|
||||
}
|
||||
|
||||
private static boolean xor(boolean x,boolean y) {
|
||||
return (x&&y) || (!x&&!y);
|
||||
}
|
||||
|
||||
static {
|
||||
// initialize the action table
|
||||
for( int t0=0; t0<5; t0++ )
|
||||
for( int t1=0; t1<5; t1++ )
|
||||
actionTable[t0*5+t1] = decideAction(t0,t1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Classify a character into 5 categories that determine the word break.
|
||||
*/
|
||||
protected int classify(char c0) {
|
||||
switch(Character.getType(c0)) {
|
||||
case Character.UPPERCASE_LETTER: return UPPER_LETTER;
|
||||
case Character.LOWERCASE_LETTER: return LOWER_LETTER;
|
||||
case Character.TITLECASE_LETTER:
|
||||
case Character.MODIFIER_LETTER:
|
||||
case Character.OTHER_LETTER: return OTHER_LETTER;
|
||||
case Character.DECIMAL_DIGIT_NUMBER: return DIGIT;
|
||||
default: return OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tokenizes a string into words and capitalizes the first
|
||||
* character of each word.
|
||||
*
|
||||
* <p>
|
||||
* This method uses a change in character type as a splitter
|
||||
* of two words. For example, "abc100ghi" will be splitted into
|
||||
* {"Abc", "100","Ghi"}.
|
||||
*/
|
||||
public List<String> toWordList(String s) {
|
||||
ArrayList<String> ss = new ArrayList<String>();
|
||||
int n = s.length();
|
||||
for (int i = 0; i < n;) {
|
||||
|
||||
// Skip punctuation
|
||||
while (i < n) {
|
||||
if (!isPunct(s.charAt(i)))
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
if (i >= n) break;
|
||||
|
||||
// Find next break and collect word
|
||||
int b = nextBreak(s, i);
|
||||
String w = (b == -1) ? s.substring(i) : s.substring(i, b);
|
||||
ss.add(escape(capitalize(w)));
|
||||
if (b == -1) break;
|
||||
i = b;
|
||||
}
|
||||
|
||||
// we can't guarantee a valid Java identifier anyway,
|
||||
// so there's not much point in rejecting things in this way.
|
||||
// if (ss.size() == 0)
|
||||
// throw new IllegalArgumentException("Zero-length identifier");
|
||||
return ss;
|
||||
}
|
||||
|
||||
protected String toMixedCaseName(List<String> ss, boolean startUpper) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(!ss.isEmpty()) {
|
||||
sb.append(startUpper ? ss.get(0) : toLowerCase(ss.get(0)));
|
||||
for (int i = 1; i < ss.size(); i++)
|
||||
sb.append(ss.get(i));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected String toMixedCaseVariableName(String[] ss,
|
||||
boolean startUpper,
|
||||
boolean cdrUpper) {
|
||||
if (cdrUpper)
|
||||
for (int i = 1; i < ss.length; i++)
|
||||
ss[i] = capitalize(ss[i]);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if( ss.length>0 ) {
|
||||
sb.append(startUpper ? ss[0] : toLowerCase(ss[0]));
|
||||
for (int i = 1; i < ss.length; i++)
|
||||
sb.append(ss[i]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Formats a string into "THIS_KIND_OF_FORMAT_ABC_DEF".
|
||||
*
|
||||
* @return
|
||||
* Always return a string but there's no guarantee that
|
||||
* the generated code is a valid Java identifier.
|
||||
*/
|
||||
public String toConstantName(String s) {
|
||||
return toConstantName(toWordList(s));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a string into "THIS_KIND_OF_FORMAT_ABC_DEF".
|
||||
*
|
||||
* @return
|
||||
* Always return a string but there's no guarantee that
|
||||
* the generated code is a valid Java identifier.
|
||||
*/
|
||||
public String toConstantName(List<String> ss) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if( !ss.isEmpty() ) {
|
||||
sb.append(toUpperCase(ss.get(0)));
|
||||
for (int i = 1; i < ss.size(); i++) {
|
||||
sb.append('_');
|
||||
sb.append(toUpperCase(ss.get(i)));
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Escapes characters is the given string so that they can be
|
||||
* printed by only using US-ASCII characters.
|
||||
*
|
||||
* The escaped characters will be appended to the given
|
||||
* StringBuffer.
|
||||
*
|
||||
* @param sb
|
||||
* StringBuffer that receives escaped string.
|
||||
* @param s
|
||||
* String to be escaped. <code>s.substring(start)</code>
|
||||
* will be escaped and copied to the string buffer.
|
||||
*/
|
||||
public static void escape(StringBuilder sb, String s, int start) {
|
||||
int n = s.length();
|
||||
for (int i = start; i < n; i++) {
|
||||
char c = s.charAt(i);
|
||||
if (Character.isJavaIdentifierPart(c))
|
||||
sb.append(c);
|
||||
else {
|
||||
sb.append('_');
|
||||
if (c <= '\u000f') sb.append("000");
|
||||
else if (c <= '\u00ff') sb.append("00");
|
||||
else if (c <= '\u0fff') sb.append('0');
|
||||
sb.append(Integer.toString(c, 16));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes characters that are unusable as Java identifiers
|
||||
* by replacing unsafe characters with safe characters.
|
||||
*/
|
||||
private static String escape(String s) {
|
||||
int n = s.length();
|
||||
for (int i = 0; i < n; i++)
|
||||
if (!Character.isJavaIdentifierPart(s.charAt(i))) {
|
||||
StringBuilder sb = new StringBuilder(s.substring(0, i));
|
||||
escape(sb, s, i);
|
||||
return sb.toString();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user