--- /dev/null
+package com.enqualcomm.support.utils;//
+// https://github.com/WelkinXie/AESCipher-Java
+//
+
+import android.text.TextUtils;
+import android.util.Base64;
+
+import com.ic.Base64.Base64Decoder;
+import com.ic.Base64.Base64Encoder;
+
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import static android.util.Base64.NO_WRAP;
+
+public class AESCipher {
+
+ private final static String HEX = "0123456789ABCDEF";
+ private static final String CBC_PKCS5_PADDING = "AES/CBC/PKCS5Padding";//AES是加密方式 CBC是工作模式 PKCS5Padding是填充模式
+ private static final String AES = "AES";//AES 加密
+ private static final String SHA1PRNG="SHA1PRNG";//// SHA1PRNG 强随机种子算法, 要区别4.2以上版本的调用方法
+ /*
+ * 生成随机数,可以当做动态的密钥 加密和解密的密钥必须一致,不然将不能解密
+ */
+ public static String generateKey() {
+ try {
+ SecureRandom localSecureRandom = SecureRandom.getInstance(SHA1PRNG);
+ byte[] bytes_key = new byte[20];
+ localSecureRandom.nextBytes(bytes_key);
+ String str_key = toHex(bytes_key);
+ return str_key;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+ // 对密钥进行处理
+ private static byte[] getRawKey(byte[] seed) throws Exception {
+ KeyGenerator kgen = KeyGenerator.getInstance(AES);
+ //for android
+ SecureRandom sr = null;
+ // 在4.2以上版本中,SecureRandom获取方式发生了改变
+ if (android.os.Build.VERSION.SDK_INT >= 17) {
+ sr = SecureRandom.getInstance(SHA1PRNG, "Crypto");
+ } else {
+ sr = SecureRandom.getInstance(SHA1PRNG);
+ }
+ // for Java
+ // secureRandom = SecureRandom.getInstance(SHA1PRNG);
+ sr.setSeed(seed);
+ kgen.init(128, sr); //256 bits or 128 bits,192bits
+ //AES中128位密钥版本有10个加密循环,192比特密钥版本有12个加密循环,256比特密钥版本则有14个加密循环。
+ SecretKey skey = kgen.generateKey();
+ byte[] raw = skey.getEncoded();
+ return raw;
+ }
+
+ /*
+ * 加密
+ */
+ public static String encrypt(String key, String cleartext) {
+ if (TextUtils.isEmpty(cleartext)) {
+ return cleartext;
+ }
+ try {
+ byte[] result = encrypt(key, cleartext.getBytes());
+ return Base64Encoder.encode(result);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /*
+ * 加密
+ */
+ private static byte[] encrypt(String key, byte[] clear) throws Exception {
+ byte[] raw = getRawKey(key.getBytes());
+ SecretKeySpec skeySpec = new SecretKeySpec(raw, AES);
+ Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING);
+ cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]));
+ byte[] encrypted = cipher.doFinal(clear);
+ return encrypted;
+ }
+
+
+ /*
+ * 解密
+ */
+ public static String decrypt(String key, String encrypted) {
+ if (TextUtils.isEmpty(encrypted)) {
+ return encrypted;
+ }
+ try {
+ byte[] enc = Base64Decoder.decodeToBytes(encrypted);
+ byte[] result = decrypt(key, enc);
+ return new String(result);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /*
+ * 解密
+ */
+ private static byte[] decrypt(String key, byte[] encrypted) throws Exception {
+ byte[] raw = getRawKey(key.getBytes());
+ SecretKeySpec skeySpec = new SecretKeySpec(raw, AES);
+ Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING);
+ cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]));
+ byte[] decrypted = cipher.doFinal(encrypted);
+ return decrypted;
+ }
+
+ //二进制转字符
+ public static String toHex(byte[] buf) {
+ if (buf == null)
+ return "";
+ StringBuffer result = new StringBuffer(2 * buf.length);
+ for (int i = 0; i < buf.length; i++) {
+ appendHex(result, buf[i]);
+ }
+ return result.toString();
+ }
+
+ private static void appendHex(StringBuffer sb, byte b) {
+ sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
+ }
+
+ public void test(){
+
+ int testMaxCount = 1000;//测试的最大数据条数
+ //添加测试数据
+
+ //FastJson生成json数据
+ String jsonData = "JsonUtils.objectToJsonForFastJson(personList)";
+ ICLogger.e( "AES加密前json数据 ---->" + jsonData);
+ ICLogger.e("AES加密前json数据长度 ---->" + jsonData.length());
+
+ //生成一个动态key
+ String secretKey = generateKey();
+ ICLogger.e( "AES动态secretKey ---->" + secretKey);
+
+ //AES加密
+ long start = System.currentTimeMillis();
+ String encryStr = encrypt(secretKey, jsonData);
+ long end = System.currentTimeMillis();
+ ICLogger.e("AES加密耗时 cost time---->" + (end - start));
+ ICLogger.e( "AES加密后json数据 ---->" + encryStr);
+ ICLogger.e("AES加密后json数据长度 ---->" + encryStr.length());
+
+ //AES解密
+ start = System.currentTimeMillis();
+ String decryStr = decrypt(secretKey, encryStr);
+ end = System.currentTimeMillis();
+ ICLogger.e( "AES解密耗时 cost time---->" + (end - start));
+ ICLogger.e("AES解密后json数据 ---->" + decryStr);
+ }
+/****************************************************************/
+//KEY: "1Gator@3Xqaz@LZC"
+//IV: "1234567890123456"
+ private static final String AES_KEY = "1Gator@3Xqaz@LZC";
+ private static final String IV_STRING = "1234567890123456";
+ private static final String charset = "UTF-8";
+
+ public static String aesEncryptStringFixedKey(String content){
+
+ try{
+ return aesEncryptString(content,AES_KEY);
+ }catch(Exception e){
+ ICLogger.e(e.getMessage(),e);
+ }
+ return null;
+ }
+
+ public static String aesDecryptStringFixedKey(String content){
+
+ try{
+ return aesDecryptString(content,AES_KEY);
+ }catch(Exception e){
+ ICLogger.e(e.getMessage(),e);
+ }
+ return null;
+ }
+
+
+ public static String aesEncryptString(String content, String key) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
+ byte[] contentBytes = content.getBytes(charset);
+ byte[] keyBytes = key.getBytes(charset);
+ byte[] encryptedBytes = aesEncryptBytes(contentBytes, keyBytes);
+ return Base64.encodeToString(encryptedBytes,NO_WRAP);
+ }
+
+ public static String aesDecryptString(String content, String key) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
+
+ byte[] encryptedBytes = Base64.decode(content,NO_WRAP);
+ byte[] keyBytes = key.getBytes(charset);
+ byte[] decryptedBytes = aesDecryptBytes(encryptedBytes, keyBytes);
+ return new String(decryptedBytes, charset);
+ }
+
+ public static byte[] aesEncryptBytes(byte[] contentBytes, byte[] keyBytes) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
+ return cipherOperation(contentBytes, keyBytes, Cipher.ENCRYPT_MODE);
+ }
+
+ public static byte[] aesDecryptBytes(byte[] contentBytes, byte[] keyBytes) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
+ return cipherOperation(contentBytes, keyBytes, Cipher.DECRYPT_MODE);
+ }
+
+ private static byte[] cipherOperation(byte[] contentBytes, byte[] keyBytes, int mode) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
+ SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
+
+ byte[] initParam = IV_STRING.getBytes(charset);
+ IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
+
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ cipher.init(mode, secretKey, ivParameterSpec);
+
+ return cipher.doFinal(contentBytes);
+ }
+ public static void test2(){
+
+ //FastJson生成json数据
+ String jsonData = "JsonUtils.objectToJsonForFastJson(personList)";
+ ICLogger.e( "AES加密前json数据 ---->" + jsonData);
+ ICLogger.e("AES加密前json数据长度 ---->" + jsonData.length());
+
+ //AES加密
+ long start = System.currentTimeMillis();
+ String encryStr = aesEncryptStringFixedKey(jsonData);
+ long end = System.currentTimeMillis();
+ ICLogger.e("AES加密耗时 cost time---->" + (end - start));
+ ICLogger.e( "AES加密后json数据 ---->" + encryStr);
+ ICLogger.e("AES加密后json数据长度 ---->" + encryStr.length());
+
+ //AES解密
+ start = System.currentTimeMillis();
+ String decryStr = aesDecryptStringFixedKey(encryStr);
+ end = System.currentTimeMillis();
+ ICLogger.e( "AES解密耗时 cost time---->" + (end - start));
+ ICLogger.e("AES解密后json数据 ---->" + decryStr);
+
+ }
+ /*******************************************************/
+
+}
\ No newline at end of file
--- /dev/null
+package com.ic.Base64;\r
+\r
+import android.text.TextUtils;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.FilterInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+\r
+public class Base64Decoder extends FilterInputStream {\r
+\r
+ private static final char[] chars = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',\r
+ 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };\r
+\r
+ // A mapping between char values and six-bit integers\r
+ private static final int[] ints = new int[128];\r
+ static {\r
+ for (int i = 0; i < 64; i++) {\r
+ ints[chars[i]] = i;\r
+ }\r
+ }\r
+\r
+ private int charCount;\r
+ private int carryOver;\r
+\r
+ /***\r
+ * Constructs a new Base64 decoder that reads input from the given\r
+ * InputStream.\r
+ *\r
+ * @param in\r
+ * the input stream\r
+ */\r
+ private Base64Decoder(InputStream in) {\r
+ super(in);\r
+ }\r
+\r
+ /***\r
+ * Returns the next decoded character from the stream, or -1 if end of\r
+ * stream was reached.\r
+ *\r
+ * @return the decoded character, or -1 if the end of the input stream is\r
+ * reached\r
+ * @exception IOException\r
+ * if an I/O error occurs\r
+ */\r
+ public int read() throws IOException {\r
+ // Read the next non-whitespace character\r
+ int x;\r
+ do {\r
+ x = in.read();\r
+ if (x == -1) {\r
+ return -1;\r
+ }\r
+ } while (Character.isWhitespace((char) x));\r
+ charCount++;\r
+\r
+ // The '=' sign is just padding\r
+ if (x == '=') {\r
+ return -1; // effective end of stream\r
+ }\r
+\r
+ // Convert from raw form to 6-bit form\r
+ x = ints[x];\r
+\r
+ // Calculate which character we're decoding now\r
+ int mode = (charCount - 1) % 4;\r
+\r
+ // First char save all six bits, go for another\r
+ if (mode == 0) {\r
+ carryOver = x & 63;\r
+ return read();\r
+ }\r
+ // Second char use previous six bits and first two new bits,\r
+ // save last four bits\r
+ else if (mode == 1) {\r
+ int decoded = ((carryOver << 2) + (x >> 4)) & 255;\r
+ carryOver = x & 15;\r
+ return decoded;\r
+ }\r
+ // Third char use previous four bits and first four new bits,\r
+ // save last two bits\r
+ else if (mode == 2) {\r
+ int decoded = ((carryOver << 4) + (x >> 2)) & 255;\r
+ carryOver = x & 3;\r
+ return decoded;\r
+ }\r
+ // Fourth char use previous two bits and all six new bits\r
+ else if (mode == 3) {\r
+ int decoded = ((carryOver << 6) + x) & 255;\r
+ return decoded;\r
+ }\r
+ return -1; // can't actually reach this line\r
+ }\r
+\r
+ /***\r
+ * Reads decoded data into an array of bytes and returns the actual number\r
+ * of bytes read, or -1 if end of stream was reached.\r
+ *\r
+ * @param buf\r
+ * the buffer into which the data is read\r
+ * @param off\r
+ * the start offset of the data\r
+ * @param len\r
+ * the maximum number of bytes to read\r
+ * @return the actual number of bytes read, or -1 if the end of the input\r
+ * stream is reached\r
+ * @exception IOException\r
+ * if an I/O error occurs\r
+ */\r
+ public int read(byte[] buf, int off, int len) throws IOException {\r
+ if (buf.length < (len + off - 1)) {\r
+ throw new IOException("The input buffer is too small: " + len + " bytes requested starting at offset " + off + " while the buffer " + " is only " + buf.length + " bytes long.");\r
+ }\r
+\r
+ // This could of course be optimized\r
+ int i;\r
+ for (i = 0; i < len; i++) {\r
+ int x = read();\r
+ if (x == -1 && i == 0) { // an immediate -1 returns -1\r
+ return -1;\r
+ } else if (x == -1) { // a later -1 returns the chars read so far\r
+ break;\r
+ }\r
+ buf[off + i] = (byte) x;\r
+ }\r
+ return i;\r
+ }\r
+\r
+ /***\r
+ * Returns the decoded form of the given encoded string, as a String. Note\r
+ * that not all binary data can be represented as a String, so this method\r
+ * should only be used for encoded String data. Use decodeToBytes()\r
+ * otherwise.\r
+ *\r
+ * @param encoded\r
+ * the string to decode\r
+ * @return the decoded form of the encoded string\r
+ */\r
+ public static String decode(String encoded) {\r
+ if (TextUtils.isEmpty(encoded)) {\r
+ return "";\r
+ }\r
+ return new String(decodeToBytes(encoded));\r
+ }\r
+\r
+ /***\r
+ * Returns the decoded form of the given encoded string, as bytes.\r
+ *\r
+ * @param encoded\r
+ * the string to decode\r
+ * @return the decoded form of the encoded string\r
+ */\r
+ public static byte[] decodeToBytes(String encoded) {\r
+ byte[] bytes = encoded.getBytes();\r
+ Base64Decoder in = new Base64Decoder(new ByteArrayInputStream(bytes));\r
+ ByteArrayOutputStream out = new ByteArrayOutputStream((int) (bytes.length * 0.75));\r
+ try {\r
+ byte[] buf = new byte[4 * 1024]; // 4K buffer\r
+ int bytesRead;\r
+ while ((bytesRead = in.read(buf)) != -1) {\r
+ out.write(buf, 0, bytesRead);\r
+ }\r
+ return out.toByteArray();\r
+ } catch (IOException e) {\r
+ throw new RuntimeException(e);\r
+ } finally {\r
+ try {\r
+ in.close();\r
+ } catch (IOException e) {\r
+ throw new RuntimeException(e);\r
+ }\r
+ try {\r
+ out.close();\r
+ } catch (IOException e) {\r
+ throw new RuntimeException(e);\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
--- /dev/null
+package com.ic.Base64;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.FilterOutputStream;\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+public class Base64Encoder extends FilterOutputStream {\r
+\r
+ private static final char[] chars = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',\r
+ 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };\r
+\r
+ private int charCount;\r
+ private int carryOver;\r
+ // 是否每76字节换行\r
+ private boolean isWrapBreak = true;\r
+\r
+ /***\r
+ * Constructs a new Base64 encoder that writes output to the given\r
+ * OutputStream.\r
+ *\r
+ * @param out\r
+ * the output stream\r
+ */\r
+ private Base64Encoder(OutputStream out) {\r
+ super(out);\r
+ }\r
+\r
+ /***\r
+ * Constructs a new Base64 encoder that writes output to the given\r
+ * OutputStream.\r
+ *\r
+ * @param out\r
+ * the output stream\r
+ */\r
+ private Base64Encoder(OutputStream out, boolean isWrapBreak) {\r
+ this(out);\r
+ this.isWrapBreak = isWrapBreak;\r
+ }\r
+\r
+ /***\r
+ * Writes the given byte to the output stream in an encoded form.\r
+ *\r
+ * @exception IOException\r
+ * if an I/O error occurs\r
+ */\r
+ public void write(int b) throws IOException {\r
+ // Take 24-bits from three octets, translate into four encoded chars\r
+ // Break lines at 76 chars\r
+ // If necessary, pad with 0 bits on the right at the end\r
+ // Use = signs as padding at the end to ensure encodedLength % 4 == 0\r
+\r
+ // Remove the sign bit,\r
+ // thanks to Christian Schweingruber <chrigu@lorraine.ch>\r
+ if (b < 0) {\r
+ b += 256;\r
+ }\r
+\r
+ // First byte use first six bits, save last two bits\r
+ if (charCount % 3 == 0) {\r
+ int lookup = b >> 2;\r
+ carryOver = b & 3; // last two bits\r
+ out.write(chars[lookup]);\r
+ }\r
+ // Second byte use previous two bits and first four new bits,\r
+ // save last four bits\r
+ else if (charCount % 3 == 1) {\r
+ int lookup = ((carryOver << 4) + (b >> 4)) & 63;\r
+ carryOver = b & 15; // last four bits\r
+ out.write(chars[lookup]);\r
+ }\r
+ // Third byte use previous four bits and first two new bits,\r
+ // then use last six new bits\r
+ else if (charCount % 3 == 2) {\r
+ int lookup = ((carryOver << 2) + (b >> 6)) & 63;\r
+ out.write(chars[lookup]);\r
+ lookup = b & 63; // last six bits\r
+ out.write(chars[lookup]);\r
+ carryOver = 0;\r
+ }\r
+ charCount++;\r
+\r
+ // Add newline every 76 output chars (that's 57 input chars)\r
+ if (this.isWrapBreak && charCount % 57 == 0) {\r
+ out.write('\n');\r
+ }\r
+ }\r
+\r
+ /***\r
+ * Writes the given byte array to the output stream in an encoded form.\r
+ *\r
+ * @param buf\r
+ * the data to be written\r
+ * @param off\r
+ * the start offset of the data\r
+ * @param len\r
+ * the length of the data\r
+ * @exception IOException\r
+ * if an I/O error occurs\r
+ */\r
+ public void write(byte[] buf, int off, int len) throws IOException {\r
+ // This could of course be optimized\r
+ for (int i = 0; i < len; i++) {\r
+ write(buf[off + i]);\r
+ }\r
+ }\r
+\r
+ /***\r
+ * Closes the stream, this MUST be called to ensure proper padding is\r
+ * written to the end of the output stream.\r
+ *\r
+ * @exception IOException\r
+ * if an I/O error occurs\r
+ */\r
+ public void close() throws IOException {\r
+ // Handle leftover bytes\r
+ if (charCount % 3 == 1) { // one leftover\r
+ int lookup = (carryOver << 4) & 63;\r
+ out.write(chars[lookup]);\r
+ out.write('=');\r
+ out.write('=');\r
+ } else if (charCount % 3 == 2) { // two leftovers\r
+ int lookup = (carryOver << 2) & 63;\r
+ out.write(chars[lookup]);\r
+ out.write('=');\r
+ }\r
+ super.close();\r
+ }\r
+\r
+ /***\r
+ * Returns the encoded form of the given unencoded string.<br>\r
+ * 默认是否每76字节换行\r
+ *\r
+ * @param bytes\r
+ * the bytes to encode\r
+ * @return the encoded form of the unencoded string\r
+ * @throws IOException\r
+ */\r
+ public static String encode(byte[] bytes) {\r
+ return encode(bytes, true);\r
+ }\r
+\r
+ /***\r
+ * Returns the encoded form of the given unencoded string.\r
+ *\r
+ * @param bytes\r
+ * the bytes to encode\r
+ * @param isWrapBreak\r
+ * 是否每76字节换行\r
+ * @return the encoded form of the unencoded string\r
+ * @throws IOException\r
+ */\r
+ public static String encode(byte[] bytes, boolean isWrapBreak) {\r
+ ByteArrayOutputStream out = new ByteArrayOutputStream((int) (bytes.length * 1.4));\r
+ Base64Encoder encodedOut = new Base64Encoder(out, isWrapBreak);\r
+ try {\r
+ encodedOut.write(bytes);\r
+ } catch (IOException e) {\r
+ throw new RuntimeException(e);\r
+ } finally {\r
+ try {\r
+ encodedOut.close();\r
+ } catch (IOException e) {\r
+ throw new RuntimeException(e);\r
+ }\r
+ }\r
+ return out.toString();\r
+ }\r
+\r
+ // public static void main(String[] args) throws Exception {\r
+ // if (args.length != 1) {\r
+ // System.err\r
+ // .println("Usage: java com.oreilly.servlet.Base64Encoder fileToEncode");\r
+ // return;\r
+ // }\r
+ // Base64Encoder encoder = null;\r
+ // BufferedInputStream in = null;\r
+ // try {\r
+ // encoder = new Base64Encoder(System.out);\r
+ // in = new BufferedInputStream(new FileInputStream(args[0]));\r
+ //\r
+ // byte[] buf = new byte[4 * 1024]; // 4K buffer\r
+ // int bytesRead;\r
+ // while ((bytesRead = in.read(buf)) != -1) {\r
+ // encoder.write(buf, 0, bytesRead);\r
+ // }\r
+ // } finally {\r
+ // if (in != null)\r
+ // in.close();\r
+ // if (encoder != null)\r
+ // encoder.close();\r
+ // }\r
+ // }\r
+}\r
+\r