me.xhsun.guildwars2wrapper.GuildWars2Utility Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gw2wrapper Show documentation
Show all versions of gw2wrapper Show documentation
Guild Wars 2 API wrapper for Android
package me.xhsun.guildwars2wrapper;
import java.nio.ByteBuffer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Class that contains some utility functions
*
* @author xhsun
* @since 2017-06-28
*/
public class GuildWars2Utility {
private static final String TAG = "<[^>]*>";
private static final String FORMAT = "(?<=\\[)(&*)([^\\]]+)(?=\\])";
private static final int ITEM_INDEX = 0;
private static final byte TERMINAL = 0x00;
//Chat link header supported: item, map, skill, trait, recipe, wardrobe, outfit
private static final byte[] HEADER = new byte[]{0x02, 0x04, 0x06, 0x07, 0x09, 0x0A, 0x0B};
public static final int CHAT_LINK_ERROR = -1;
public static final int CHAT_LINK_NOT_SUPPORTED_ERROR = -2;
/**
* get first id in a combined id, ie, get 7 from 7-54
*
* useful for getting wvw map id from wvw objective id, or
* getting back story question id from back story answer id
*
* @param id combined id in the form of [0-9]+-\b+
* @return root id | -1 for invalid input
*/
public static int getRootID(String id) {
if (id == null || id.equals("")) return -1;
String[] strIds;
if (id.contains("-")) strIds = id.split("-");
else return -1;
try {
return Integer.parseInt(strIds[0]);
} catch (NumberFormatException e) {
return -1;
}
}
/**
* parse given coin value into number of gold, sliver, and copper
*
* @param value coin value
* @return results in array: index 0 is number of gold, index 1 is number of sliver, index 2 is number of copper
*/
public static long[] parseCoins(long value) {
long[] result = new long[3];
if (value < 0) return result;
long temp = value;
result[2] = temp % 100;
temp = temp / 100;
result[1] = temp % 100;
result[0] = temp / 100;
return result;
}
/**
* Remove HTML tag(s) and the string enclosed by HTML tags
* (ie, {@literal "this, that"} becomes ", that")
*
* @param value string to remove HTML from
* @return string without HTML
*/
public static String removeSimpleHTML(String value) {
String rex = "<^[>]+/>|" + TAG + "[^<]*[^>]*>|" + TAG;
if (value == null || value.equals("")) return "";
return value.replaceAll(rex, "");
}
/**
* remove HTML tag(s) from given string
* ie, "this, that" becomes "this, that"
*
* @param value string to strip HTML tag from
* @return string without HTML tag
*/
public static String removeHTMLTag(String value) {
if (value == null || value.equals("")) return "";
return value.replaceAll(TAG, "");
}
/**
* extract id from given chat link
* Currently supported chat link type:
* item, map, skill, trait, recipe, wardrobe, outfit
*
* @param chat_link chat link
* @return id |
* {@link GuildWars2Utility#CHAT_LINK_ERROR} if something is wrong with the given chat link |
* {@link GuildWars2Utility#CHAT_LINK_NOT_SUPPORTED_ERROR} if given chat link isn't supported
*/
public static int chatLinkToID(String chat_link) {
String striped;
byte[] decoded, id = new byte[4];
int i, j = 3;
boolean isFind = false;
if (chat_link == null || chat_link.equals("")) return CHAT_LINK_ERROR;
//strip Base64 string from chat link
Pattern pattern = Pattern.compile(FORMAT);
Matcher matcher = pattern.matcher(chat_link);
if (matcher.find()) striped = matcher.group(2);
else return CHAT_LINK_ERROR;
try {//decode the Base64 string
decoded = _decode(striped.getBytes());
if (decoded == null) return CHAT_LINK_ERROR;
} catch (IllegalArgumentException e) {
return CHAT_LINK_ERROR;
}
//see if the chat link is the one supported
for (i = 0; i < HEADER.length; i++) {
if (HEADER[i] == decoded[0]) {
isFind = true;
break;
}
}
if (!isFind) return CHAT_LINK_NOT_SUPPORTED_ERROR;
//extract id
for (i = (i == ITEM_INDEX) ? 2 : 1; i < decoded.length; i++) {
if (decoded[i] == TERMINAL || j < 0) break;
id[j--] = decoded[i];
}
return ByteBuffer.wrap(id).getInt();
}
/**
* This method come from this library
* I use this method to decoding Base64 string, so that I don't have to relying on javax library or android library
*
* @param byteData Base64 string in byte array
* @return decoded byte array
* @throws IllegalArgumentException invalid input
*/
private static byte[] _decode(byte[] byteData) throws IllegalArgumentException {
/* If we received a null argument, exit this method. */
if (byteData == null) {
throw new IllegalArgumentException("byteData cannot be null");
}
/*
* Declare working variables including an array of bytes that will
* contain the decoded data to be returned to the caller. Note that the
* decoded array is about 3/4 smaller than the input. This is because
* every group of 4 bytes is being encoded into 3 bytes.
*/
int iSrcIdx; // index into source (byteData)
int reviSrcIdx; // index from end of the src array (byteData)
int iDestIdx; // index into destination (byteDest)
byte[] byteTemp = new byte[byteData.length];
/*
* remove any '=' chars from the end of the byteData they would have
* been padding to make it up to groups of 4 bytes note that I don't
* need to remove it just make sure that when progressing throug array
* we don't go past reviSrcIdx ;-)
*/
for (reviSrcIdx = byteData.length; reviSrcIdx - 1 > 0 && byteData[reviSrcIdx - 1] == '='; reviSrcIdx--) {
// do nothing. I'm just interested in value of reviSrcIdx
}
/* sanity check */
if (reviSrcIdx - 1 == 0) {
return null; /* ie all padding */
}
/*
* Set byteDest, this is smaller than byteData due to 4 -> 3 byte munge.
* Note that this is an integer division! This fact is used in the logic
* l8r. to make sure we don't fall out of the array and create an
* OutOfBoundsException and also in handling the remainder
*/
byte byteDest[] = new byte[((reviSrcIdx * 3) / 4)];
/*
* Convert from Base64 alphabet to encoded data (The Base64 alphabet is
* completely documented in RFC 1521.) The order of the testing is
* important as I use the '<' operator which looks at the hex value of
* these ASCII chars. So convert from the smallest up
*
* do all of this in a new array so as not to edit the original input
*/
for (iSrcIdx = 0; iSrcIdx < reviSrcIdx; iSrcIdx++) {
if (byteData[iSrcIdx] == '+')
byteTemp[iSrcIdx] = 62;
else if (byteData[iSrcIdx] == '/')
byteTemp[iSrcIdx] = 63;
else if (byteData[iSrcIdx] < '0' + 10)
byteTemp[iSrcIdx] = (byte) (byteData[iSrcIdx] + 52 - '0');
else if (byteData[iSrcIdx] < ('A' + 26))
byteTemp[iSrcIdx] = (byte) (byteData[iSrcIdx] - 'A');
else if (byteData[iSrcIdx] < 'a' + 26)
byteTemp[iSrcIdx] = (byte) (byteData[iSrcIdx] + 26 - 'a');
}
/*
* 4bytes -> 3bytes munge Walk through the input array, 32 bits at a
* time, converting them from 4 groups of 6 to 3 groups of 8 removing
* the two unset most significant bits of each sorce byte as this was
* filler, as per Base64 spec. stop before potential buffer overun on
* byteDest, remember that byteDest is 3/4 (integer division) the size
* of input and won't necessary divide exactly (ie iDestIdx must be <
* (integer div byteDest.length / 3)*3 see
* http://www.javaworld.com/javaworld/javatips/jw-javatip36-p2.html for
* example
*/
for (iSrcIdx = 0, iDestIdx = 0; iSrcIdx < reviSrcIdx
&& iDestIdx < ((byteDest.length / 3) * 3); iSrcIdx += 4) {
byteDest[iDestIdx++] = (byte) ((byteTemp[iSrcIdx] << 2) & 0xFC | (byteTemp[iSrcIdx + 1] >>> 4) & 0x03);
byteDest[iDestIdx++] = (byte) ((byteTemp[iSrcIdx + 1] << 4) & 0xF0 | (byteTemp[iSrcIdx + 2] >>> 2) & 0x0F);
byteDest[iDestIdx++] = (byte) ((byteTemp[iSrcIdx + 2] << 6) & 0xC0 | byteTemp[iSrcIdx + 3] & 0x3F);
}
/*
* tidy up any remainders if iDestIdx >= ((byteDest.length / 3)*3) but
* iSrcIdx < reviSrcIdx then we have at most 2 extra destination bytes
* to fill and posiblr 3 input bytes yet to process
*/
if (iSrcIdx < reviSrcIdx) {
if (iSrcIdx < reviSrcIdx - 2) {
// "3 input bytes left"
byteDest[iDestIdx++] = (byte) ((byteTemp[iSrcIdx] << 2) & 0xFC | (byteTemp[iSrcIdx + 1] >>> 4) & 0x03);
byteDest[iDestIdx++] = (byte) ((byteTemp[iSrcIdx + 1] << 4) & 0xF0 | (byteTemp[iSrcIdx + 2] >>> 2) & 0x0F);
} else if (iSrcIdx < reviSrcIdx - 1) {
// "2 input bytes left"
byteDest[iDestIdx++] = (byte) ((byteTemp[iSrcIdx] << 2) & 0xFC | (byteTemp[iSrcIdx + 1] >>> 4) & 0x03);
}
/*
* wont have just one input byte left (unless input wasn't base64
* encoded ) due to the for loop steps and array sizes, after "="
* pad removed, but for compleatness
*/
else {
throw new IllegalArgumentException("Warning: 1 input bytes left to process. This was not Base64 input");
}
}
return byteDest;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy