This commit is contained in:
simon 2020-10-15 12:36:33 +03:00
parent ed7e379adb
commit 93db8c4c0e
16 changed files with 2200 additions and 28 deletions

View file

@ -56,6 +56,55 @@ public class NetworkUtils {
return e.getMessage(); return e.getMessage();
} }
} }
public static String sendPostDataRequest(String urlText, InputStream data){
try {
log.info("POST : " + urlText);
HttpURLConnection conn = getHttpURLConnection(urlText);
conn.setDoInput(true);
conn.setDoOutput(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("Accept","*/*");
conn.setRequestProperty("User-Agent", "OsmAnd"); //$NON-NLS-1$ //$NON-NLS-2$
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
OutputStream ous = conn.getOutputStream();
ous.write(("--" + BOUNDARY + "\r\n").getBytes());
ous.write(("content-disposition: form-data; name=\"" + "file" + "\"; filename=\"" + "image1" + "\"\r\n").getBytes()); //$NON-NLS-1$ //$NON-NLS-2$
ous.write(("Content-Type: application/octet-stream\r\n\r\n").getBytes()); //$NON-NLS-1$
Algorithms.streamCopy(data,ous);
ous.write(("\r\n--" + BOUNDARY + "--\r\n").getBytes()); //$NON-NLS-1$ //$NON-NLS-2$
ous.flush();
log.info("Response code and message : " + conn.getResponseCode() + " " + conn.getResponseMessage());
if(conn.getResponseCode() != 200){
return conn.getResponseMessage();
}
StringBuilder responseBody = new StringBuilder();
InputStream is = conn.getInputStream();
responseBody.setLength(0);
if (is != null) {
BufferedReader in = new BufferedReader(new InputStreamReader(is, "UTF-8")); //$NON-NLS-1$
String s;
boolean first = true;
while ((s = in.readLine()) != null) {
if(first){
first = false;
} else {
responseBody.append("\n"); //$NON-NLS-1$
}
responseBody.append(s);
}
is.close();
}
Algorithms.closeStream(is);
Algorithms.closeStream(data);
Algorithms.closeStream(ous);
return responseBody.toString();
} catch (IOException e) {
log.error(e.getMessage(), e);
return e.getMessage();
}
}
private static final String BOUNDARY = "CowMooCowMooCowCowCow"; //$NON-NLS-1$ private static final String BOUNDARY = "CowMooCowMooCowCowCow"; //$NON-NLS-1$
public static String uploadFile(String urlText, File fileToUpload, String userNamePassword, public static String uploadFile(String urlText, File fileToUpload, String userNamePassword,
OsmOAuthAuthorizationClient client, OsmOAuthAuthorizationClient client,

BIN
OsmAnd/libs/opendb-core.jar Normal file

Binary file not shown.

View file

@ -34,17 +34,20 @@ import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat; import androidx.core.graphics.drawable.DrawableCompat;
import net.osmand.AndroidUtils; import net.osmand.AndroidUtils;
import net.osmand.PlatformUtil;
import net.osmand.binary.BinaryMapIndexReader; import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.data.Amenity; import net.osmand.data.Amenity;
import net.osmand.data.LatLon; import net.osmand.data.LatLon;
import net.osmand.data.PointDescription; import net.osmand.data.PointDescription;
import net.osmand.data.QuadRect; import net.osmand.data.QuadRect;
import net.osmand.osm.PoiCategory; import net.osmand.osm.PoiCategory;
import net.osmand.osm.io.NetworkUtils;
import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmAndFormatter;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandPlugin; import net.osmand.plus.OsmandPlugin;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.UiUtilities; import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.ActivityResultListener;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.helpers.FontCache; import net.osmand.plus.helpers.FontCache;
import net.osmand.plus.mapcontextmenu.builders.cards.AbstractCard; import net.osmand.plus.mapcontextmenu.builders.cards.AbstractCard;
@ -53,6 +56,7 @@ import net.osmand.plus.mapcontextmenu.builders.cards.ImageCard;
import net.osmand.plus.mapcontextmenu.builders.cards.ImageCard.GetImageCardsTask; import net.osmand.plus.mapcontextmenu.builders.cards.ImageCard.GetImageCardsTask;
import net.osmand.plus.mapcontextmenu.builders.cards.NoImagesCard; import net.osmand.plus.mapcontextmenu.builders.cards.NoImagesCard;
import net.osmand.plus.mapcontextmenu.controllers.TransportStopController; import net.osmand.plus.mapcontextmenu.controllers.TransportStopController;
import net.osmand.plus.osmedit.utils.SecUtils;
import net.osmand.plus.poi.PoiUIFilter; import net.osmand.plus.poi.PoiUIFilter;
import net.osmand.plus.render.RenderingIcons; import net.osmand.plus.render.RenderingIcons;
import net.osmand.plus.transport.TransportStopRoute; import net.osmand.plus.transport.TransportStopRoute;
@ -62,7 +66,9 @@ import net.osmand.plus.widgets.TextViewEx;
import net.osmand.plus.widgets.tools.ClickableSpanTouchListener; import net.osmand.plus.widgets.tools.ClickableSpanTouchListener;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils; import net.osmand.util.MapUtils;
import org.apache.commons.logging.Log;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -77,7 +83,7 @@ public class MenuBuilder {
public static final float SHADOW_HEIGHT_TOP_DP = 17f; public static final float SHADOW_HEIGHT_TOP_DP = 17f;
public static final int TITLE_LIMIT = 60; public static final int TITLE_LIMIT = 60;
protected static final String[] arrowChars = new String[]{"=>"," - "}; protected static final String[] arrowChars = new String[] {"=>", " - "};
protected MapActivity mapActivity; protected MapActivity mapActivity;
protected MapContextMenu mapContextMenu; protected MapContextMenu mapContextMenu;
@ -103,6 +109,8 @@ public class MenuBuilder {
private String preferredMapLang; private String preferredMapLang;
private String preferredMapAppLang; private String preferredMapAppLang;
private boolean transliterateNames; private boolean transliterateNames;
private static final int PICK_IMAGE = 1231;
private static final Log LOG = PlatformUtil.getLog(MenuBuilder.class);
public interface CollapseExpandListener { public interface CollapseExpandListener {
void onCollapseExpand(boolean collapsed); void onCollapseExpand(boolean collapsed);
@ -212,10 +220,59 @@ public class MenuBuilder {
if (showOnlinePhotos) { if (showOnlinePhotos) {
buildNearestPhotosRow(view); buildNearestPhotosRow(view);
} }
buildUploadImagesRow(view);
buildPluginRows(view); buildPluginRows(view);
// buildAfter(view); // buildAfter(view);
} }
public void buildUploadImagesRow(View view) {
if (mapContextMenu != null) {
//TODO to strings
String title = "Upload images";
buildRow(view, R.drawable.ic_action_note_dark, null, title, 0, false,
null, false, 0, false, new OnClickListener() {
@Override
public void onClick(View view) {
mapActivity.registerActivityResultListener(new ActivityResultListener(PICK_IMAGE,
new ActivityResultListener.OnActivityResultListener() {
@Override
public void onResult(int resultCode, Intent resultData) {
InputStream inputStream = null;
try {
inputStream = mapActivity.getContentResolver().openInputStream(resultData.getData());
} catch (FileNotFoundException e) {
LOG.error(e);
}
handleSelectedImage(inputStream);
}
}));
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
mapActivity.startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_IMAGE);
}
}, false);
}
}
private void handleSelectedImage(final InputStream image) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try{
String url = "https://test.openplacereviews.org/api/ipfs/image";
String response = NetworkUtils.sendPostDataRequest(url, image);
//TODO
(new SecUtils()).main(new String[0]);
}
catch (Exception e){
e.printStackTrace();
}
}
});
t.start();
}
private boolean showTransportRoutes() { private boolean showTransportRoutes() {
return showLocalTransportRoutes() || showNearbyTransportRoutes(); return showLocalTransportRoutes() || showNearbyTransportRoutes();
} }
@ -282,7 +339,7 @@ public class MenuBuilder {
protected void buildNearestWikiRow(View view) { protected void buildNearestWikiRow(View view) {
if (processNearestWiki() && nearestWiki.size() > 0) { if (processNearestWiki() && nearestWiki.size() > 0) {
buildRow(view, R.drawable.ic_action_wikipedia, null, app.getString(R.string.wiki_around) + " (" + nearestWiki.size()+")", 0, buildRow(view, R.drawable.ic_action_wikipedia, null, app.getString(R.string.wiki_around) + " (" + nearestWiki.size() + ")", 0,
true, getCollapsableWikiView(view.getContext(), true), true, getCollapsableWikiView(view.getContext(), true),
false, 0, false, null, false); false, 0, false, null, false);
} }
@ -379,7 +436,7 @@ public class MenuBuilder {
} }
} }
protected void buildDescription(View view){ protected void buildDescription(View view) {
} }
protected void buildAfter(View view) { protected void buildAfter(View view) {
@ -480,7 +537,7 @@ public class MenuBuilder {
textPrefixView.setLayoutParams(llTextParams); textPrefixView.setLayoutParams(llTextParams);
textPrefixView.setTypeface(FontCache.getRobotoRegular(view.getContext())); textPrefixView.setTypeface(FontCache.getRobotoRegular(view.getContext()));
textPrefixView.setTextSize(12); textPrefixView.setTextSize(12);
textPrefixView.setTextColor(app.getResources().getColor(light ? R.color.text_color_secondary_light: R.color.text_color_secondary_dark)); textPrefixView.setTextColor(app.getResources().getColor(light ? R.color.text_color_secondary_light : R.color.text_color_secondary_dark));
textPrefixView.setMinLines(1); textPrefixView.setMinLines(1);
textPrefixView.setMaxLines(1); textPrefixView.setMaxLines(1);
textPrefixView.setText(textPrefix); textPrefixView.setText(textPrefix);
@ -526,7 +583,7 @@ public class MenuBuilder {
textViewSecondary.setLayoutParams(llTextSecondaryParams); textViewSecondary.setLayoutParams(llTextSecondaryParams);
textViewSecondary.setTypeface(FontCache.getRobotoRegular(view.getContext())); textViewSecondary.setTypeface(FontCache.getRobotoRegular(view.getContext()));
textViewSecondary.setTextSize(14); textViewSecondary.setTextSize(14);
textViewSecondary.setTextColor(app.getResources().getColor(light ? R.color.text_color_secondary_light: R.color.text_color_secondary_dark)); textViewSecondary.setTextColor(app.getResources().getColor(light ? R.color.text_color_secondary_light : R.color.text_color_secondary_dark));
textViewSecondary.setText(secondaryText); textViewSecondary.setText(secondaryText);
llText.addView(textViewSecondary); llText.addView(textViewSecondary);
} }
@ -964,7 +1021,7 @@ public class MenuBuilder {
button.setTypeface(FontCache.getRobotoRegular(context)); button.setTypeface(FontCache.getRobotoRegular(context));
int bg; int bg;
if (selected) { if (selected) {
bg = light ? R.drawable.context_menu_controller_bg_light_selected: R.drawable.context_menu_controller_bg_dark_selected; bg = light ? R.drawable.context_menu_controller_bg_light_selected : R.drawable.context_menu_controller_bg_dark_selected;
} else if (showAll) { } else if (showAll) {
bg = light ? R.drawable.context_menu_controller_bg_light_show_all : R.drawable.context_menu_controller_bg_dark_show_all; bg = light ? R.drawable.context_menu_controller_bg_light_show_all : R.drawable.context_menu_controller_bg_dark_show_all;
} else { } else {

View file

@ -18,6 +18,7 @@ import net.osmand.osm.edit.Entity.EntityType;
import net.osmand.osm.edit.EntityInfo; import net.osmand.osm.edit.EntityInfo;
import net.osmand.osm.edit.Node; import net.osmand.osm.edit.Node;
import net.osmand.osm.edit.Way; import net.osmand.osm.edit.Way;
import net.osmand.osm.io.Base64;
import net.osmand.osm.io.NetworkUtils; import net.osmand.osm.io.NetworkUtils;
import net.osmand.osm.io.OsmBaseStorage; import net.osmand.osm.io.OsmBaseStorage;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
@ -30,10 +31,8 @@ import org.apache.commons.logging.Log;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer; import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayInputStream; import java.io.*;
import java.io.File; import java.net.HttpURLConnection;
import java.io.IOException;
import java.io.StringWriter;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.HashMap; import java.util.HashMap;
@ -108,14 +107,16 @@ public class OpenstreetmapRemoteUtil implements OpenstreetmapUtil {
boolean doAuthenticate) { boolean doAuthenticate) {
log.info("Sending request " + url); //$NON-NLS-1$ log.info("Sending request " + url); //$NON-NLS-1$
try { try {
if (doAuthenticate){
OsmOAuthAuthorizationAdapter client = new OsmOAuthAuthorizationAdapter(ctx); OsmOAuthAuthorizationAdapter client = new OsmOAuthAuthorizationAdapter(ctx);
Response response = client.performRequest(url,requestMethod,requestBody); if (doAuthenticate) {
if (client.isValidToken()) {
Response response = client.performRequest(url, requestMethod, requestBody);
return response.getBody(); return response.getBody();
} else {
return performBasicAuthRequest(url, requestMethod, requestBody, userOperation);
} }
else { } else {
OsmOAuthAuthorizationAdapter client = new OsmOAuthAuthorizationAdapter(ctx); Response response = client.performRequestWithoutAuth(url, requestMethod, requestBody);
Response response = client.performRequestWithoutAuth(url,requestMethod,requestBody);
return response.getBody(); return response.getBody();
} }
} catch (NullPointerException e) { } catch (NullPointerException e) {
@ -139,11 +140,64 @@ public class OpenstreetmapRemoteUtil implements OpenstreetmapUtil {
log.error(userOperation + " " + ctx.getString(R.string.failed_op), e); //$NON-NLS-1$ log.error(userOperation + " " + ctx.getString(R.string.failed_op), e); //$NON-NLS-1$
showWarning(MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template) showWarning(MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template)
+ ": " + ctx.getResources().getString(R.string.shared_string_unexpected_error), userOperation)); + ": " + ctx.getResources().getString(R.string.shared_string_unexpected_error), userOperation));
} catch (Exception e) {
log.error(userOperation + " " + ctx.getString(R.string.failed_op), e); //$NON-NLS-1$
showWarning(MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template)
+ ": " + ctx.getResources().getString(R.string.shared_string_unexpected_error), userOperation));
} }
return null; return null;
} }
private String performBasicAuthRequest(String url, String requestMethod, String requestBody, String userOperation) throws IOException {
HttpURLConnection connection = NetworkUtils.getHttpURLConnection(url);
connection.setConnectTimeout(15000);
connection.setRequestMethod(requestMethod);
connection.setRequestProperty("User-Agent", Version.getFullVersion(ctx)); //$NON-NLS-1$
StringBuilder responseBody = new StringBuilder();
String token = settings.USER_NAME.get() + ":" + settings.USER_PASSWORD.get(); //$NON-NLS-1$
connection.addRequestProperty("Authorization", "Basic " + Base64.encode(token.getBytes("UTF-8"))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
connection.setDoInput(true);
if (requestMethod.equals("PUT") || requestMethod.equals("POST") || requestMethod.equals("DELETE")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
connection.setDoOutput(true);
connection.setRequestProperty("Content-type", "text/xml"); //$NON-NLS-1$ //$NON-NLS-2$
OutputStream out = connection.getOutputStream();
if (requestBody != null) {
BufferedWriter bwr = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"), 1024); //$NON-NLS-1$
bwr.write(requestBody);
bwr.flush();
}
out.close();
}
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
String msg = userOperation
+ " " + ctx.getString(R.string.failed_op) + " : " + connection.getResponseMessage(); //$NON-NLS-1$//$NON-NLS-2$
log.error(msg);
showWarning(msg);
} else {
log.info("Response : " + connection.getResponseMessage()); //$NON-NLS-1$
// populate return fields.
responseBody.setLength(0);
InputStream i = connection.getInputStream();
if (i != null) {
BufferedReader in = new BufferedReader(new InputStreamReader(i, "UTF-8"), 256); //$NON-NLS-1$
String s;
boolean f = true;
while ((s = in.readLine()) != null) {
if (!f) {
responseBody.append("\n"); //$NON-NLS-1$
} else {
f = false;
}
responseBody.append(s);
}
}
return responseBody.toString();
}
return null;
}
public long openChangeSet(String comment) { public long openChangeSet(String comment) {
long id = -1; long id = -1;
StringWriter writer = new StringWriter(256); StringWriter writer = new StringWriter(256);

View file

@ -0,0 +1,16 @@
package net.osmand.plus.osmedit.utils;
public class FailedVerificationException extends Exception {
private static final long serialVersionUID = -4936205097177668159L;
public FailedVerificationException(Exception e) {
super(e);
}
public FailedVerificationException(String msg) {
super(msg);
}
}

View file

@ -0,0 +1,135 @@
package net.osmand.plus.osmedit.utils;
import com.google.gson.*;
import net.osmand.plus.osmedit.utils.ops.OpObject;
import net.osmand.plus.osmedit.utils.ops.OpOperation;
import java.io.Reader;
import java.lang.reflect.Type;
import java.util.*;
public class JsonFormatter {
private Gson gson;
private Gson gsonOperationHash;
private Gson gsonFullOutput;
public JsonFormatter() {
GsonBuilder builder = new GsonBuilder();
builder.disableHtmlEscaping();
builder.registerTypeAdapter(OpOperation.class, new OpOperation.OpOperationBeanAdapter(false));
builder.registerTypeAdapter(OpObject.class, new OpObject.OpObjectAdapter(false));
builder.registerTypeAdapter(TreeMap.class, new MapDeserializerDoubleAsIntFix());
gson = builder.create();
builder = new GsonBuilder();
builder.disableHtmlEscaping();
builder.registerTypeAdapter(OpOperation.class, new OpOperation.OpOperationBeanAdapter(false, true));
builder.registerTypeAdapter(OpObject.class, new OpObject.OpObjectAdapter(false));
builder.registerTypeAdapter(TreeMap.class, new MapDeserializerDoubleAsIntFix());
gsonOperationHash = builder.create();
builder = new GsonBuilder();
builder.disableHtmlEscaping();
builder.registerTypeAdapter(OpOperation.class, new OpOperation.OpOperationBeanAdapter(true));
builder.registerTypeAdapter(OpObject.class, new OpObject.OpObjectAdapter(true));
builder.registerTypeAdapter(TreeMap.class, new MapDeserializerDoubleAsIntFix());
gsonFullOutput = builder.create();
}
public static class MapDeserializerDoubleAsIntFix implements JsonDeserializer<TreeMap<String, Object>> {
@Override @SuppressWarnings("unchecked")
public TreeMap<String, Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return (TreeMap<String, Object>) read(json);
}
public Object read(JsonElement in) {
if(in.isJsonArray()){
List<Object> list = new ArrayList<Object>();
JsonArray arr = in.getAsJsonArray();
for (JsonElement anArr : arr) {
list.add(read(anArr));
}
return list;
}else if(in.isJsonObject()){
Map<String, Object> map = new TreeMap<String, Object>();
JsonObject obj = in.getAsJsonObject();
Set<Map.Entry<String, JsonElement>> entitySet = obj.entrySet();
for(Map.Entry<String, JsonElement> entry: entitySet){
map.put(entry.getKey(), read(entry.getValue()));
}
return map;
}else if(in.isJsonPrimitive()){
JsonPrimitive prim = in.getAsJsonPrimitive();
if(prim.isBoolean()){
return prim.getAsBoolean();
}else if(prim.isString()){
return prim.getAsString();
}else if(prim.isNumber()){
Number num = prim.getAsNumber();
// here you can handle double int/long values
// and return any type you want
// this solution will transform 3.0 float to long values
if(Math.ceil(num.doubleValue()) == num.longValue() && (!num.toString().contains(".") || num.toString().split("\\.")[1].length() <= 1))
return num.longValue();
else {
return num.doubleValue();
}
}
}
return null;
}
}
// operations to parse / format related
public OpOperation parseOperation(String opJson) {
return gson.fromJson(opJson, OpOperation.class);
}
public OpObject parseObject(String opJson) {
return gson.fromJson(opJson, OpObject.class);
}
public Object parseBlock(String opJson) {
throw new UnsupportedOperationException("");
}
public JsonElement toJsonElement(Object o) {
return gson.toJsonTree(o);
}
@SuppressWarnings("unchecked")
public TreeMap<String, Object> fromJsonToTreeMap(String json) {
return gson.fromJson(json, TreeMap.class);
}
public <T> T fromJson(Reader json, Class<T> classOfT) throws JsonSyntaxException {
return gson.fromJson(json, classOfT);
}
public String fullObjectToJson(Object o) {
return gsonFullOutput.toJson(o);
}
public String opToJsonNoHash(OpOperation op) {
return gsonOperationHash.toJson(op);
}
public String opToJson(OpOperation op) {
return gson.toJson(op);
}
public String objToJson(OpObject op) {
return gson.toJson(op);
}
}

View file

@ -0,0 +1,411 @@
package net.osmand.plus.osmedit.utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
//import java.util.Base64;
import java.util.concurrent.ThreadLocalRandom;
import net.osmand.plus.osmedit.utils.ops.OpOperation;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
//import org.bouncycastle.crypto.generators.SCrypt;
//import org.bouncycastle.crypto.prng.FixedSecureRandom;
public class SecUtils {
private static final String SIG_ALGO_SHA1_EC = "SHA1withECDSA";
private static final String SIG_ALGO_NONE_EC = "NonewithECDSA";
public static final String SIG_ALGO_ECDSA = "ECDSA";
public static final String ALGO_EC = "EC";
public static final String EC_256SPEC_K1 = "secp256k1";
public static final String KEYGEN_PWD_METHOD_1 = "EC256K1_S17R8";
public static final String DECODE_BASE64 = "base64";
public static final String HASH_SHA256 = "sha256";
public static final String HASH_SHA1 = "sha1";
public static final String JSON_MSG_TYPE = "json";
public static final String KEY_BASE64 = DECODE_BASE64;
public static void main(String[] args) {
//1) create op, 2) sign op 3) send to server process op
//
KeyPairGenerator keyGen = null ;
SecureRandom random = null;
try {
keyGen = KeyPairGenerator.getInstance(ALGO_EC);
random = SecureRandom.getInstance("SHA1PRNG");
keyGen.initialize(1024, random);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
KeyPair kp = null;
try {
kp = SecUtils.getKeyPair(ALGO_EC,
"base64:PKCS#8:MD4CAQAwEAYHKoZIzj0CAQYFK4EEAAoEJzAlAgEBBCDR+/ByIjTHZgfdnMfP9Ab5s14mMzFX+8DYqUiGmf/3rw=="
, "base64:X.509:MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEOMUiRZwU7wW8L3A1qaJPwhAZy250VaSxJmKCiWdn9EMeubXQgWNT8XUWLV5Nvg7O3sD+1AAQLG5kHY8nOc/AyA==");
} catch (FailedVerificationException e) {
e.printStackTrace();
}
// KeyPair kp = generateECKeyPairFromPassword(KEYGEN_PWD_METHOD_1, "openplacereviews", "");
// KeyPair kp = generateRandomEC256K1KeyPair();
System.out.println(kp.getPrivate().getFormat());
System.out.println(kp.getPrivate().getAlgorithm());
try {
System.out.println(SecUtils.validateKeyPair(ALGO_EC, kp.getPrivate(), kp.getPublic()));
} catch (FailedVerificationException e) {
e.printStackTrace();
}
String pr = encodeKey(KEY_BASE64, kp.getPrivate());
String pk = encodeKey(KEY_BASE64, kp.getPublic());
String algo = kp.getPrivate().getAlgorithm();
System.out.println(String.format("Private key: %s %s\nPublic key: %s %s", kp.getPrivate().getFormat(), pr, kp
.getPublic().getFormat(), pk));
String signMessageTest = "Hello this is a registration message test";
byte[] signature = signMessageWithKey(kp, signMessageTest.getBytes(), SIG_ALGO_SHA1_EC);
System.out.println(String.format("Signed message: %s %s", android.util.Base64.decode(signature, android.util.Base64.DEFAULT),
signMessageTest));
KeyPair nk = null;
try {
nk = getKeyPair(algo, pr, pk);
} catch (FailedVerificationException e) {
e.printStackTrace();
}
// validate
pr = new String(android.util.Base64.decode(nk.getPrivate().getEncoded(), android.util.Base64.DEFAULT));
pk = new String(android.util.Base64.decode(nk.getPublic().getEncoded(), android.util.Base64.DEFAULT));
System.out.println(String.format("Private key: %s %s\nPublic key: %s %s", nk.getPrivate().getFormat(), pr, nk
.getPublic().getFormat(), pk));
System.out.println(validateSignature(nk, signMessageTest.getBytes(), SIG_ALGO_SHA1_EC, signature));
JsonFormatter formatter = new JsonFormatter();
String msg = "{\n" +
" \"type\" : \"sys.signup\",\n" +
" \"signed_by\": \"openplacereviews\",\n" +
" \"create\": [{\n" +
" \"id\": [\"openplacereviews\"],\n" +
" \"name\" : \"openplacereviews\",\n" +
" \"algo\": \"EC\",\n" +
" \"auth_method\": \"provided\",\n" +
" \"pubkey\": \"base64:X.509:MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEn6GkOTN3SYc+OyCYCpqPzKPALvUgfUVNDJ+6eyBlCHI1/gKcVqzHLwaO90ksb29RYBiF4fW/PqHcECNzwJB+QA==\"\n" +
" }]\n" +
" }";
OpOperation opOperation = formatter.parseOperation(msg);
String hash = JSON_MSG_TYPE + ":"
+ SecUtils.calculateHashWithAlgo(SecUtils.HASH_SHA256, null,
formatter.opToJsonNoHash(opOperation));
byte[] hashBytes = SecUtils.getHashBytes(hash);
String signatureTxt = SecUtils.signMessageWithKeyBase64(kp, hashBytes, SecUtils.SIG_ALGO_ECDSA, null);
System.out.println(formatter.opToJsonNoHash(opOperation));
System.out.println(hash);
System.out.println(signatureTxt);
}
public static EncodedKeySpec decodeKey(String key) {
if (key.startsWith(KEY_BASE64 + ":")) {
key = key.substring(KEY_BASE64.length() + 1);
int s = key.indexOf(':');
if (s == -1) {
throw new IllegalArgumentException(String.format("Key doesn't contain algorithm of hashing to verify"));
}
return getKeySpecByFormat(key.substring(0, s),
android.util.Base64.decode(key.substring(s + 1), android.util.Base64.DEFAULT));
}
throw new IllegalArgumentException(String.format("Key doesn't contain algorithm of hashing to verify"));
}
public static String encodeKey(String algo, PublicKey pk) {
if (algo.equals(KEY_BASE64)) {
return SecUtils.KEY_BASE64 + ":" + pk.getFormat() + ":" + encodeBase64(pk.getEncoded());
}
throw new UnsupportedOperationException("Algorithm is not supported: " + algo);
}
public static String encodeKey(String algo, PrivateKey pk) {
if (algo.equals(KEY_BASE64)) {
return SecUtils.KEY_BASE64 + ":" + pk.getFormat() + ":" + encodeBase64(pk.getEncoded());
}
throw new UnsupportedOperationException("Algorithm is not supported: " + algo);
}
public static EncodedKeySpec getKeySpecByFormat(String format, byte[] data) {
switch (format) {
case "PKCS#8":
return new PKCS8EncodedKeySpec(data);
case "X.509":
return new X509EncodedKeySpec(data);
}
throw new IllegalArgumentException(format);
}
public static String encodeBase64(byte[] data) {
return new String(android.util.Base64.decode(data, android.util.Base64.DEFAULT));
}
public static boolean validateKeyPair(String algo, PrivateKey privateKey, PublicKey publicKey)
throws FailedVerificationException {
if (!algo.equals(ALGO_EC)) {
throw new FailedVerificationException("Algorithm is not supported: " + algo);
}
// create a challenge
byte[] challenge = new byte[512];
ThreadLocalRandom.current().nextBytes(challenge);
try {
// sign using the private key
Signature sig = Signature.getInstance(SIG_ALGO_SHA1_EC);
sig.initSign(privateKey);
sig.update(challenge);
byte[] signature = sig.sign();
// verify signature using the public key
sig.initVerify(publicKey);
sig.update(challenge);
boolean keyPairMatches = sig.verify(signature);
return keyPairMatches;
} catch (InvalidKeyException e) {
throw new FailedVerificationException(e);
} catch (NoSuchAlgorithmException e) {
throw new FailedVerificationException(e);
} catch (SignatureException e) {
throw new FailedVerificationException(e);
}
}
public static KeyPair getKeyPair(String algo, String prKey, String pbKey) throws FailedVerificationException {
try {
KeyFactory keyFactory = KeyFactory.getInstance(algo);
PublicKey pb = null;
PrivateKey pr = null;
if (pbKey != null) {
pb = keyFactory.generatePublic(decodeKey(pbKey));
}
if (prKey != null) {
pr = keyFactory.generatePrivate(decodeKey(prKey));
}
return new KeyPair(pb, pr);
} catch (NoSuchAlgorithmException e) {
throw new FailedVerificationException(e);
} catch (InvalidKeySpecException e) {
throw new FailedVerificationException(e);
}
}
public static KeyPair generateKeyPairFromPassword(String algo, String keygenMethod, String salt, String pwd)
throws FailedVerificationException {
if (algo.equals(ALGO_EC)) {
return generateECKeyPairFromPassword(keygenMethod, salt, pwd);
}
throw new UnsupportedOperationException("Unsupported algo keygen method: " + algo);
}
public static KeyPair generateECKeyPairFromPassword(String keygenMethod, String salt, String pwd)
throws FailedVerificationException {
if (keygenMethod.equals(KEYGEN_PWD_METHOD_1)) {
return generateEC256K1KeyPairFromPassword(salt, pwd);
}
throw new UnsupportedOperationException("Unsupported keygen method: " + keygenMethod);
}
// "EC:secp256k1:scrypt(salt,N:17,r:8,p:1,len:256)" algorithm - EC256K1_S17R8
public static KeyPair generateEC256K1KeyPairFromPassword(String salt, String pwd)
throws FailedVerificationException {
try {
KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGO_EC);
ECGenParameterSpec ecSpec = new ECGenParameterSpec(EC_256SPEC_K1);
if (pwd.length() < 10) {
throw new IllegalArgumentException("Less than 10 characters produces only 50 bit entropy");
}
byte[] bytes = pwd.getBytes("UTF-8");
//byte[] scrypt = SCrypt.generate(bytes, salt.getBytes("UTF-8"), 1 << 17, 8, 1, 256);
//kpg.initialize(ecSpec, new FixedSecureRandom(scrypt));
return kpg.genKeyPair();
} catch (NoSuchAlgorithmException e) {
throw new FailedVerificationException(e);
} catch (UnsupportedEncodingException e) {
throw new FailedVerificationException(e);
} /* catch (InvalidAlgorithmParameterException e) {
throw new FailedVerificationException(e);
}*/
}
public static KeyPair generateRandomEC256K1KeyPair() throws FailedVerificationException {
try {
KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGO_EC);
ECGenParameterSpec ecSpec = new ECGenParameterSpec(EC_256SPEC_K1);
kpg.initialize(ecSpec);
return kpg.genKeyPair();
} catch (NoSuchAlgorithmException e) {
throw new FailedVerificationException(e);
} catch (InvalidAlgorithmParameterException e) {
throw new FailedVerificationException(e);
}
}
public static String signMessageWithKeyBase64(KeyPair keyPair, byte[] msg, String signAlgo, ByteArrayOutputStream out) {
byte[] sigBytes = signMessageWithKey(keyPair, msg, signAlgo);
if(out != null) {
try {
out.write(sigBytes);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
String signature = new String(android.util.Base64.decode(sigBytes, android.util.Base64.DEFAULT));
return signAlgo + ":" + DECODE_BASE64 + ":" + signature;
}
public static byte[] signMessageWithKey(KeyPair keyPair, byte[] msg, String signAlgo) {
try {
Signature sig = Signature.getInstance(getInternalSigAlgo(signAlgo));
sig.initSign(keyPair.getPrivate());
sig.update(msg);
byte[] signatureBytes = sig.sign();
return signatureBytes;
} catch (NoSuchAlgorithmException e) {
//throw new FailedVerificationException(e);
} catch (InvalidKeyException e) {
//throw new FailedVerificationException(e);
} catch (SignatureException e) {
//throw new FailedVerificationException(e);
}
return new byte[0];
}
public static boolean validateSignature(KeyPair keyPair, byte[] msg, String sig) {
if(sig == null || keyPair == null) {
return false;
}
int ind = sig.indexOf(':');
String sigAlgo = sig.substring(0, ind);
return validateSignature(keyPair, msg, sigAlgo, decodeSignature(sig.substring(ind + 1)));
}
public static boolean validateSignature(KeyPair keyPair, byte[] msg, String sigAlgo, byte[] signature) {
if (keyPair == null) {
return false;
}
try {
Signature sig = Signature.getInstance(getInternalSigAlgo(sigAlgo));
sig.initVerify(keyPair.getPublic());
sig.update(msg);
return sig.verify(signature);
} catch (NoSuchAlgorithmException e) {
//throw new FailedVerificationException(e);
} catch (InvalidKeyException e) {
//throw new FailedVerificationException(e);
} catch (SignatureException e) {
//throw new FailedVerificationException(e);
}
return false;
}
private static String getInternalSigAlgo(String sigAlgo) {
return sigAlgo.equals(SIG_ALGO_ECDSA)? SIG_ALGO_NONE_EC : sigAlgo;
}
public static byte[] calculateHash(String algo, byte[] b1, byte[] b2) {
byte[] m = mergeTwoArrays(b1, b2);
if (algo.equals(HASH_SHA256)) {
return DigestUtils.sha256(m);
} else if (algo.equals(HASH_SHA1)) {
return DigestUtils.sha1(m);
}
throw new UnsupportedOperationException();
}
public static byte[] mergeTwoArrays(byte[] b1, byte[] b2) {
byte[] m = b1 == null ? b2 : b1;
if(b2 != null && b1 != null) {
m = new byte[b1.length + b2.length];
System.arraycopy(b1, 0, m, 0, b1.length);
System.arraycopy(b2, 0, m, b1.length, b2.length);
}
return m;
}
public static String calculateHashWithAlgo(String algo, String salt, String msg) {
try {
String hex = Hex.encodeHexString(calculateHash(algo, salt == null ? null : salt.getBytes("UTF-8"),
msg == null ? null : msg.getBytes("UTF-8")));
return algo + ":" + hex;
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e);
}
}
public static String calculateHashWithAlgo(String algo, byte[] bts) {
byte[] hash = calculateHash(algo, bts, null);
return formatHashWithAlgo(algo, hash);
}
public static String formatHashWithAlgo(String algo, byte[] hash) {
String hex = Hex.encodeHexString(hash);
return algo + ":" + hex;
}
public static byte[] getHashBytes(String msg) {
if(msg == null || msg.length() == 0) {
// special case for empty hash
return new byte[0];
}
int i = msg.lastIndexOf(':');
String s = i >= 0 ? msg.substring(i + 1) : msg;
try {
return Hex.decodeHex(s);
} catch (DecoderException e) {
throw new IllegalArgumentException(e);
}
}
public static boolean validateHash(String hash, String salt, String msg) {
int s = hash.indexOf(":");
if (s == -1) {
throw new IllegalArgumentException(String.format("Hash %s doesn't contain algorithm of hashing to verify",
s));
}
String v = calculateHashWithAlgo(hash.substring(0, s), salt, msg);
return hash.equals(v);
}
public static byte[] decodeSignature(String digest) {
try {
int indexOf = digest.indexOf(DECODE_BASE64 + ":");
if (indexOf != -1) {
// return Base64.getDecoder().decode(digest.substring(indexOf + DECODE_BASE64.length() + 1).
// getBytes("UTF-8"));
return android.util.Base64.decode(digest.substring(indexOf + DECODE_BASE64.length() + 1)
.getBytes("UTF-8"), android.util.Base64.DEFAULT);
}
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e);
}
throw new IllegalArgumentException("Unknown format for signature " + digest);
}
public static String hexify(byte[] bytes) {
if(bytes == null || bytes.length == 0) {
return "";
}
return Hex.encodeHexString(bytes);
}
}

View file

@ -0,0 +1,530 @@
package net.osmand.plus.osmedit.utils.ops;
import com.google.gson.*;
import net.osmand.plus.osmedit.utils.util.JsonObjectUtils;
import net.osmand.plus.osmedit.utils.util.OUtils;
import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
public class OpObject {
public static final String F_NAME = "name";
public static final String F_ID = "id";
public static final String F_COMMENT = "comment";
public static final String TYPE_OP = "sys.op";
public static final String TYPE_BLOCK = "sys.block";
public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
// transient info about validation timing etc
public static final String F_EVAL = "eval";
public static final String F_VALIDATION = "validation";
public static final String F_TIMESTAMP_ADDED = "timestamp";
public static final String F_PARENT_TYPE = "parentType";
public static final String F_PARENT_HASH = "parentHash";
public static final String F_CHANGE = "change";
public static final String F_CURRENT = "current";
// voting
public static final String F_OP = "op";
public static final String F_STATE = "state";
public static final String F_OPEN = "open";
public static final String F_FINAL = "final";
public static final String F_VOTE = "vote";
public static final String F_VOTES = "votes";
public static final String F_SUBMITTED_OP_HASH = "submittedOpHash";
public static final String F_USER = "user";
public static final OpObject NULL = new OpObject(true);
public static SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
static {
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
}
protected Map<String, Object> fields = new TreeMap<>();
protected transient Map<String, Object> cacheFields;
protected boolean isImmutable;
protected transient String parentType;
protected transient String parentHash;
protected transient boolean deleted;
public OpObject() {}
public OpObject(boolean deleted) {
this.deleted = deleted;
}
public OpObject(OpObject cp) {
this(cp, false);
}
public OpObject(OpObject cp, boolean copyCacheFields) {
createOpObjectCopy(cp, copyCacheFields);
}
@SuppressWarnings("unchecked")
private OpObject createOpObjectCopy(OpObject opObject, boolean copyCacheFields) {
this.parentType = opObject.parentType;
this.parentHash = opObject.parentHash;
this.deleted = opObject.deleted;
this.fields = (Map<String, Object>) copyingObjects(opObject.fields, copyCacheFields);
if (opObject.cacheFields != null && copyCacheFields) {
this.cacheFields = (Map<String, Object>) copyingObjects(opObject.cacheFields, copyCacheFields);
}
this.isImmutable = false;
return this;
}
public boolean isDeleted() {
return deleted;
}
@SuppressWarnings("unchecked")
private Object copyingObjects(Object object, boolean copyCacheFields) {
if (object instanceof Number) {
return (Number) object;
} else if (object instanceof String) {
return (String) object;
} else if (object instanceof Boolean) {
return (Boolean) object;
} else if (object instanceof List) {
List<Object> copy = new ArrayList<>();
List<Object> list = (List<Object>) object;
for (Object o : list) {
copy.add(copyingObjects(o, copyCacheFields));
}
return copy;
} else if (object instanceof Map) {
Map<Object, Object> copy = new LinkedHashMap<>();
Map<Object, Object> map = (Map<Object, Object>) object;
for (Object o : map.keySet()) {
copy.put(o, copyingObjects(map.get(o), copyCacheFields));
}
return copy;
} else if (object instanceof OpObject) {
return new OpObject((OpObject) object);
} else {
throw new UnsupportedOperationException("Type of object is not supported");
}
}
public void setParentOp(OpOperation op) {
setParentOp(op.type, op.getRawHash());
}
public void setParentOp(String parentType, String parentHash) {
this.parentType = parentType;
this.parentHash = parentHash;
}
public String getParentHash() {
return parentHash;
}
public String getParentType() {
return parentType;
}
public List<String> getId() {
return getStringList(F_ID);
}
public void setId(String id) {
addOrSetStringValue(F_ID, id);;
}
public boolean isImmutable() {
return isImmutable;
}
public OpObject makeImmutable() {
isImmutable = true;
return this;
}
public Object getFieldByExpr(String field) {
if (field.contains(".") || field.contains("[") || field.contains("]")) {
return JsonObjectUtils.getField(this.fields, generateFieldSequence(field));
}
return fields.get(field);
}
/**
* generateFieldSequence("a") - [a]
* generateFieldSequence("a.b") - [a, b]
* generateFieldSequence("a.b.c.de") - [a, b, c, de]
* generateFieldSequence("a.bwerq.c") - [a, bwerq, c]
* generateFieldSequence("a.bwerq...c") - [a, bwerq, c]
* generateFieldSequence("a.bwereq..c..") - [a, bwerq, c]
* generateFieldSequence("a.{b}") - [a, b]
* generateFieldSequence("a.{b.c.de}") - [a, b.c.de]
* generateFieldSequence("a.{b.c.de}") - [a, b.c.de]
* generateFieldSequence("a.{b{}}") - [a, b{}]
* generateFieldSequence("a.{b{}d.q}") - [a, b{}d.q]
*/
private static List<String> generateFieldSequence(String field) {
int STATE_OPEN_BRACE = 1;
int STATE_OPEN = 0;
int state = STATE_OPEN;
int start = 0;
List<String> l = new ArrayList<String>();
for(int i = 0; i < field.length(); i++) {
boolean split = false;
if (i == field.length() - 1) {
if (state == STATE_OPEN_BRACE) {
if(field.charAt(i) == '}') {
split = true;
} else {
throw new IllegalArgumentException("Illegal field expression: " + field);
}
} else {
if(field.charAt(i) != '.') {
i++;
}
split = true;
}
} else {
if (field.charAt(i) == '.' && state == STATE_OPEN) {
split = true;
} else if (field.charAt(i) == '}' && field.charAt(i + 1) == '.' && state == STATE_OPEN_BRACE) {
split = true;
} else if (field.charAt(i) == '{' && state == STATE_OPEN) {
if(start != i) {
throw new IllegalArgumentException("Illegal field expression (wrap {} is necessary): " + field);
}
state = STATE_OPEN_BRACE;
start = i + 1;
}
}
if(split) {
if (i != start) {
l.add(field.substring(start, i));
}
start = i + 1;
state = STATE_OPEN;
}
}
return l;
}
public void setFieldByExpr(String field, Object object) {
if (field.contains(".") || field.contains("[") || field.contains("]")) {
List<String> fieldSequence = generateFieldSequence(field);
if (object == null) {
JsonObjectUtils.deleteField(this.fields, fieldSequence);
} else {
JsonObjectUtils.setField(this.fields, fieldSequence, object);
}
} else if (object == null) {
fields.remove(field);
} else {
fields.put(field, object);
}
}
public Object getCacheObject(String f) {
if(cacheFields == null) {
return null;
}
return cacheFields.get(f);
}
public void putCacheObject(String f, Object o) {
if (isImmutable()) {
if (cacheFields == null) {
cacheFields = new ConcurrentHashMap<String, Object>();
}
cacheFields.put(f, o);
}
}
public void setId(String id, String id2) {
List<String> list = new ArrayList<String>();
list.add(id);
list.add(id2);
putObjectValue(F_ID, list);
}
public String getName() {
return getStringValue(F_NAME);
}
public String getComment() {
return getStringValue(F_COMMENT);
}
public Map<String, Object> getRawOtherFields() {
return fields;
}
@SuppressWarnings("unchecked")
public Map<String, String> getStringMap(String field) {
return (Map<String, String>) fields.get(field);
}
@SuppressWarnings("unchecked")
public Map<String, List<String>> getMapStringList(String field) {
return (Map<String, List<String>>) fields.get(field);
}
@SuppressWarnings("unchecked")
public List<Map<String, String>> getListStringMap(String field) {
return (List<Map<String, String>>) fields.get(field);
}
@SuppressWarnings("unchecked")
public List<Map<String, Object>> getListStringObjMap(String field) {
return (List<Map<String, Object>>) fields.get(field);
}
@SuppressWarnings("unchecked")
public Map<String, Object> getStringObjMap(String field) {
return (Map<String, Object>) fields.get(field);
}
@SuppressWarnings("unchecked")
public <T> T getField(T def, String... fields) {
Map<String, Object> p = this.fields;
for(int i = 0; i < fields.length - 1 ; i++) {
p = (Map<String, Object>) p.get(fields[i]);
if(p == null) {
return def;
}
}
T res = (T) p.get(fields[fields.length - 1]);
if(res == null) {
return def;
}
return res;
}
@SuppressWarnings("unchecked")
public Map<List<String>, Object> getStringListObjMap(String field) {
return (Map<List<String>, Object>) fields.get(field);
}
public long getDate(String field) {
String date = getStringValue(field);
if(OUtils.isEmpty(date)) {
return 0;
}
try {
return dateFormat.parse(date).getTime();
} catch (ParseException e) {
return 0;
}
}
public void setDate(String field, long time) {
putStringValue(field, dateFormat.format(new Date(time)));
}
public Number getNumberValue(String field) {
return (Number) fields.get(field);
}
public int getIntValue(String key, int def) {
Number o = getNumberValue(key);
return o == null ? def : o.intValue();
}
public long getLongValue(String key, long def) {
Number o = getNumberValue(key);
return o == null ? def : o.longValue();
}
public String getStringValue(String field) {
Object o = fields.get(field);
if (o instanceof String || o == null) {
return (String) o;
}
return o.toString();
}
@SuppressWarnings("unchecked")
public List<String> getStringList(String field) {
// cast to list if it is single value
Object o = fields.get(field);
if(o == null || o.toString().isEmpty()) {
return Collections.emptyList();
}
if(o instanceof String) {
return Collections.singletonList(o.toString());
}
return (List<String>) o;
}
public Object getObjectValue(String field) {
return fields.get(field);
}
public void putStringValue(String key, String value) {
checkNotImmutable();
if(value == null) {
fields.remove(key);
} else {
fields.put(key, value);
}
}
/**
* Operates as a single value if cardinality is less than 1
* or as a list of values if it stores > 1 value
* @param key
* @param value
*/
@SuppressWarnings("unchecked")
public void addOrSetStringValue(String key, String value) {
checkNotImmutable();
Object o = fields.get(key);
if(o == null) {
fields.put(key, value);
} else if(o instanceof List) {
((List<String>) o).add(value);
} else {
List<String> list = new ArrayList<String>();
list.add(o.toString());
list.add(value);
fields.put(key, list);
}
}
@SuppressWarnings("unchecked")
public Map<String, Object> getChangedEditFields() {
return (Map<String, Object>) fields.get(F_CHANGE);
}
@SuppressWarnings("unchecked")
public Map<String, Object> getCurrentEditFields() {
return (Map<String, Object>) fields.get(F_CURRENT);
}
public void putObjectValue(String key, Object value) {
checkNotImmutable();
if(value == null) {
fields.remove(key);
} else {
fields.put(key, value);
}
}
public void checkNotImmutable() {
if(isImmutable) {
throw new IllegalStateException("Object is immutable");
}
}
public void checkImmutable() {
if(!isImmutable) {
throw new IllegalStateException("Object is mutable");
}
}
public Object remove(String key) {
checkNotImmutable();
return fields.remove(key);
}
public Map<String, Object> getMixedFieldsAndCacheMap() {
TreeMap<String, Object> mp = new TreeMap<>(fields);
if(cacheFields != null || parentType != null || parentHash != null) {
TreeMap<String, Object> eval = new TreeMap<String, Object>();
if(parentType != null) {
eval.put(F_PARENT_TYPE, parentType);
}
if(parentHash != null) {
eval.put(F_PARENT_HASH, parentHash);
}
if (cacheFields != null) {
Iterator<Entry<String, Object>> it = cacheFields.entrySet().iterator();
while (it.hasNext()) {
Entry<String, Object> e = it.next();
Object v = e.getValue();
if (v instanceof Map || v instanceof String || v instanceof Number) {
eval.put(e.getKey(), v);
}
}
}
if(eval.size() > 0) {
mp.put(F_EVAL, eval);
}
}
return mp;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((fields == null) ? 0 : fields.hashCode());
return result;
}
@Override
public String toString() {
return getClass().getSimpleName() + "[" + fields + "]";
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
OpObject other = (OpObject) obj;
if (fields == null) {
if (other.fields != null)
return false;
} else if (!fields.equals(other.fields))
return false;
return true;
}
public static class OpObjectAdapter implements JsonDeserializer<OpObject>,
JsonSerializer<OpObject> {
private boolean fullOutput;
public OpObjectAdapter(boolean fullOutput) {
this.fullOutput = fullOutput;
}
@Override
public OpObject deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
OpObject bn = new OpObject();
bn.fields = context.deserialize(json, TreeMap.class);
// remove cache
bn.fields.remove(F_EVAL);
return bn;
}
@Override
public JsonElement serialize(OpObject src, Type typeOfSrc, JsonSerializationContext context) {
return context.serialize(fullOutput ? src.getMixedFieldsAndCacheMap() : src.fields);
}
}
}

View file

@ -0,0 +1,281 @@
package net.osmand.plus.osmedit.utils.ops;
import com.google.gson.*;
import java.lang.reflect.Type;
import java.util.*;
public class OpOperation extends OpObject {
public static final String F_TYPE = "type";
public static final String F_SIGNED_BY = "signed_by";
public static final String F_HASH = "hash";
public static final String F_SIGNATURE = "signature";
public static final String F_REF = "ref";
public static final String F_CREATE = "create";
public static final String F_DELETE = "delete";
public static final String F_EDIT = "edit";
public static final String F_NAME = "name";
public static final String F_COMMENT = "comment";
private List<OpObject> createdObjects = new LinkedList<OpObject>();
private List<OpObject> editedObjects = new LinkedList<OpObject>();
protected String type;
public OpOperation() {
}
public OpOperation(OpOperation cp, boolean copyCacheFields) {
super(cp, copyCacheFields);
this.type = cp.type;
for(OpObject o : cp.createdObjects) {
this.createdObjects.add(new OpObject(o, copyCacheFields));
}
for(OpObject o : cp.editedObjects) {
this.editedObjects.add(new OpObject(o, copyCacheFields));
}
}
public String getOperationType() {
return type;
}
public void setType(String name) {
checkNotImmutable();
type = name;
updateObjectsRef();
}
protected void updateObjectsRef() {
for(OpObject o : createdObjects) {
o.setParentOp(this);
}
for(OpObject o : editedObjects) {
o.setParentOp(this);
}
}
public String getType() {
return type;
}
public OpOperation makeImmutable() {
isImmutable = true;
for(OpObject o : createdObjects) {
o.makeImmutable();
}
for(OpObject o : editedObjects) {
o.makeImmutable();
}
return this;
}
public void setSignedBy(String value) {
putStringValue(F_SIGNED_BY, value);
}
public void addOtherSignedBy(String value) {
super.addOrSetStringValue(F_SIGNED_BY, value);
}
public List<String> getSignedBy() {
return getStringList(F_SIGNED_BY);
}
public String getHash() {
return getStringValue(F_HASH);
}
public String getRawHash() {
String rw = getStringValue(F_HASH);
// drop algorithm and everything else
if(rw != null) {
rw = rw.substring(rw.lastIndexOf(':') + 1);
}
return rw;
}
public List<String> getSignatureList() {
return getStringList(F_SIGNATURE);
}
public Map<String, List<String>> getRef() {
return getMapStringList(F_REF);
}
@SuppressWarnings("unchecked")
public List<List<String>> getDeleted() {
List<List<String>> l = (List<List<String>>) fields.get(F_DELETE);
if(l == null) {
return Collections.emptyList();
}
return l;
}
public boolean hasDeleted() {
return getDeleted().size() > 0;
}
public void addDeleted(List<String> id) {
if(!fields.containsKey(F_DELETE)) {
ArrayList<List<String>> lst = new ArrayList<>();
lst.add(id);
putObjectValue(F_DELETE, lst);
} else {
getDeleted().add(id);
}
}
public List<OpObject> getCreated() {
return createdObjects;
}
public void addCreated(OpObject o) {
checkNotImmutable();
createdObjects.add(o);
if(type != null) {
o.setParentOp(this);
}
}
public boolean hasCreated() {
return createdObjects.size() > 0;
}
public void addEdited(OpObject o) {
checkNotImmutable();
editedObjects.add(o);
if (type != null) {
o.setParentOp(this);
}
}
public List<OpObject> getEdited() {
return editedObjects;
}
public boolean hasEdited() {
return editedObjects.size() > 0;
}
public String getName() {
return getStringValue(F_NAME);
}
public String getComment() {
return getStringValue(F_COMMENT);
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((createdObjects == null) ? 0 : createdObjects.hashCode());
result = prime * result + ((editedObjects == null) ? 0 : editedObjects.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
OpOperation other = (OpOperation) obj;
if (createdObjects == null) {
if (other.createdObjects != null)
return false;
} else if (!createdObjects.equals(other.createdObjects))
return false;
if (editedObjects == null) {
if (other.editedObjects != null)
return false;
} else if (!editedObjects.equals(other.editedObjects))
return false;
if (type == null) {
if (other.type != null)
return false;
} else if (!type.equals(other.type))
return false;
return true;
}
public static class OpOperationBeanAdapter implements JsonDeserializer<OpOperation>,
JsonSerializer<OpOperation> {
// plain serialization to calculate hash
private boolean excludeHashAndSignature;
private boolean fullOutput;
public OpOperationBeanAdapter(boolean fullOutput, boolean excludeHashAndSignature) {
this.excludeHashAndSignature = excludeHashAndSignature;
this.fullOutput = fullOutput;
}
public OpOperationBeanAdapter(boolean fullOutput) {
this.fullOutput = fullOutput;
this.excludeHashAndSignature = false;
}
@Override
public OpOperation deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
JsonObject jsonObj = json.getAsJsonObject();
OpOperation op = new OpOperation();
JsonElement tp = jsonObj.remove(F_TYPE);
if(tp != null) {
String opType = tp.getAsString();
op.type = opType;
} else {
op.type = "";
}
JsonElement createdObjs = jsonObj.remove(F_CREATE);
if(createdObjs != null) {
JsonArray ar = createdObjs.getAsJsonArray();
for(int i = 0; i < ar.size(); i++) {
//op.addCreated(context.deserialize(ar.get(i), OpObject.class));
}
}
JsonElement editedObjs = jsonObj.remove(F_EDIT);
if (editedObjs != null) {
for (JsonElement editElem : editedObjs.getAsJsonArray()) {
//op.addEdited(context.deserialize(editElem, OpObject.class));
}
}
jsonObj.remove(F_EVAL);
op.fields = context.deserialize(jsonObj, TreeMap.class);
return op;
}
@Override
public JsonElement serialize(OpOperation src, Type typeOfSrc, JsonSerializationContext context) {
TreeMap<String, Object> tm = new TreeMap<>(fullOutput ? src.getMixedFieldsAndCacheMap() : src.fields);
if(excludeHashAndSignature) {
tm.remove(F_SIGNATURE);
tm.remove(F_HASH);
}
tm.put(F_TYPE, src.type);
if (src.hasEdited()) {
tm.put(F_EDIT, context.serialize(src.editedObjects));
}
if(src.hasCreated()) {
tm.put(F_CREATE, context.serialize(src.createdObjects));
}
return context.serialize(tm);
}
}
}

View file

@ -0,0 +1,171 @@
package net.osmand.plus.osmedit.utils.ops;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public class PerformanceMetrics {
private static final PerformanceMetrics inst = new PerformanceMetrics();
public static final int METRICS_COUNT = 2;
public static PerformanceMetrics i() {
return inst;
}
private final Map<String, PerformanceMetric> metrics = new ConcurrentHashMap<String, PerformanceMetric>();
private final AtomicInteger ids = new AtomicInteger();
private final PerformanceMetric DISABLED = new PerformanceMetric(-1, "<disabled>");
private boolean enabled = true;
private PerformanceMetric overhead;
private PerformanceMetrics() {
overhead = getByKey("_overhead");
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Map<String, PerformanceMetric> getMetrics() {
return metrics;
}
public PerformanceMetric getMetric(String prefix, String key) {
if(!enabled) {
return DISABLED;
}
return getMetric(prefix + "." + key);
}
public void reset(int c) {
for(PerformanceMetric p : metrics.values()) {
p.reset(c);
}
}
public PerformanceMetric getMetric(String key) {
if(!enabled) {
return DISABLED;
}
long s = System.nanoTime();
PerformanceMetric pm = getByKey(key);
overhead.capture(System.nanoTime() - s);
return pm;
}
private PerformanceMetric getByKey(String key) {
PerformanceMetric pm = metrics.get(key);
if(pm == null) {
pm = new PerformanceMetric(ids.incrementAndGet(), key);
metrics.put(key, pm);
}
return pm;
}
public final class PerformanceMetric {
final String name;
final int id;
String description;
AtomicInteger invocations = new AtomicInteger();
AtomicLong totalDuration = new AtomicLong();
AtomicInteger invocationsA = new AtomicInteger();
AtomicLong totalDurationA = new AtomicLong();
AtomicInteger invocationsB = new AtomicInteger();
AtomicLong totalDurationB = new AtomicLong();
private PerformanceMetric(int id, String name) {
this.id = id;
this.name = name;
}
public Metric start() {
if(id == -1) {
return Metric.EMPTY;
}
return new Metric(this);
}
public String getName() {
return name;
}
public void setDescription(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public void reset(int c) {
if(c == 1) {
invocationsA.set(0);
totalDurationA.set(0);
} else if(c == 2) {
totalDurationB.set(0);
invocationsB.set(0);
}
}
public int getInvocations(int c) {
if(c == 1) {
return invocationsA.get();
} else if(c == 2) {
return invocationsB.get();
}
return invocations.get();
}
public long getDuration(int c) {
if(c == 1) {
return totalDurationA.get();
} else if(c == 2) {
return totalDurationB.get();
}
return totalDuration.get();
}
public int getId() {
return id;
}
long capture(long d) {
invocations.incrementAndGet();
totalDuration.addAndGet(d);
invocationsA.incrementAndGet();
totalDurationA.addAndGet(d);
invocationsB.incrementAndGet();
totalDurationB.addAndGet(d);
return d;
}
}
public static final class Metric {
long start;
PerformanceMetric m;
boolean e;
public static final Metric EMPTY = new Metric(true);
private Metric(PerformanceMetric m) {
start = System.nanoTime();
this.m = m;
}
private Metric(boolean empty) {
e = empty;
}
public long capture() {
if(e) {
return 0;
}
return m.capture(System.nanoTime() - start);
}
}
}

View file

@ -0,0 +1,25 @@
package net.osmand.plus.osmedit.utils.util;
public interface DBConstants {
public static final String SCHEMA_NAME = "public";
// Tables
public static final String BLOCK_TABLE = "blocks";
public static final String LOGINS_TABLE = "logins";
public static final String USERS_TABLE = "users";
public static final String QUEUE_TABLE = "queue";
public static final String ROLES_TABLE = "roles";
public static final String TABLES_TABLE = "tables";
public static final String OP_DEFINITIONS_TABLE = "op_definitions";
public static final String EXECUTED_OPERATIONS_TABLE = "operations";
}

View file

@ -0,0 +1,275 @@
package net.osmand.plus.osmedit.utils.util;
import java.util.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Class uses for work with Json Object represent as Map.
*/
public class JsonObjectUtils {
private static final int GET_OPERATION = 0;
private static final int SET_OPERATION = 1;
private static final int DELETE_OPERATION = 2;
protected static final Log LOGGER = LogFactory.getLog(JsonObjectUtils.class);
private static class OperationAccess {
private final int operation;
private final Object value;
private OperationAccess(int op, Object v) {
this.operation = op;
this.value = v;
}
}
/**
* Retrieve value from jsonMap by field sequence.
* @param jsonMap source json object deserialized in map
* @param fieldSequence Sequence to field value.
* Example: person.car.number have to be ["person", "car[2]", "number"]
* @return Field value
*/
public static Object getField(Map<String, Object> jsonMap, String[] fieldSequence) {
return accessField(jsonMap, fieldSequence, new OperationAccess(GET_OPERATION, null));
}
/**
* Set value to json field (path to field presented as sequence of string)
*
* @param jsonMap source json object deserialized in map
* @param fieldSequence Sequence to field value.
* * Example: person.car.number have to be ["person", "car[2]", "number"]
* @param field field value
* @return
*/
public static Object setField(Map<String, Object> jsonMap, List<String> fieldSequence, Object field) {
return setField(jsonMap, fieldSequence.toArray(new String[fieldSequence.size()]), field);
}
/**
* Set value to json field (path to field presented as sequence of string)
*
* @param jsonObject source json object deserialized in map
* @param fieldSequence Sequence to field value.
* * Example: person.car.number have to be ["person", "car[2]", "number"]
* @param field field value
* @return
*/
public static Object setField(Map<String, Object> jsonObject, String[] fieldSequence, Object field) {
return accessField(jsonObject, fieldSequence, new OperationAccess(SET_OPERATION, field));
}
/**
* Retrieve value from jsonMap by field sequence.
*
* @param jsonObject source json object deserialized in map
* @param fieldSequence Sequence to field value.
* Example: person.car.number have to be ["person", "car[2]", "number"]
* @return Field value
*/
public static Object getField(Map<String, Object> jsonObject, List<String> fieldSequence) {
return getField(jsonObject, fieldSequence.toArray(new String[fieldSequence.size()]));
}
/**
* Delete field value from json Map (field path presented as sequence of string)
*
* @param jsonMap source json object deserialized in map
* @param fieldSequence Sequence to field value.
* Example: person.car.number have to be ["person", "car[2]", "number"]
* @return
*/
public static Object deleteField(Map<String, Object> jsonMap, List<String> fieldSequence) {
return accessField(jsonMap, fieldSequence.toArray(new String[fieldSequence.size()]), new OperationAccess(DELETE_OPERATION, null));
}
@SuppressWarnings("unchecked")
private static Object accessField(Map<String, Object> jsonObject, String[] fieldSequence, OperationAccess op) {
if (fieldSequence == null || fieldSequence.length == 0) {
throw new IllegalArgumentException("Field sequence is empty. Set value to root not possible.");
}
String fieldName = null;
Map<String, Object> jsonObjLocal = jsonObject;
List<Object> jsonListLocal = null;
int indexToAccess = -1;
for(int i = 0; i < fieldSequence.length; i++) {
boolean last = i == fieldSequence.length - 1;
fieldName = fieldSequence[i];
int indOpArray = -1;
for(int ic = 0; ic < fieldName.length(); ) {
if(ic > 0 && (fieldName.charAt(ic) == '[' || fieldName.charAt(ic) == ']') &&
fieldName.charAt(ic - 1) == '\\') {
// replace '\[' with '['
fieldName = fieldName.substring(0, ic - 1) + fieldName.substring(ic);
} else if(fieldName.charAt(ic) == '[') {
indOpArray = ic;
break;
} else {
ic++;
}
}
jsonListLocal = null; // reset
if(indOpArray == -1) {
if(!last) {
Map<String, Object> fieldAccess = (Map<String, Object>) jsonObjLocal.get(fieldName);
if(fieldAccess == null) {
if(op.operation == GET_OPERATION) {
// don't modify during get operation
return null;
}
Map<String, Object> newJsonMap = new TreeMap<>();
jsonObjLocal.put(fieldName, newJsonMap);
jsonObjLocal = newJsonMap;
} else {
jsonObjLocal = fieldAccess;
}
}
} else {
String arrayFieldName = fieldName.substring(0, indOpArray);
if(arrayFieldName.contains("]")) {
throw new IllegalArgumentException(String.format("Illegal field array modifier %s", fieldSequence[i]));
}
jsonListLocal = (List<Object>) jsonObjLocal.get(arrayFieldName);
if (jsonListLocal == null) {
if (op.operation == GET_OPERATION) {
// don't modify during get operation
return null;
}
jsonListLocal = new ArrayList<Object>();
jsonObjLocal.put(arrayFieldName, jsonListLocal);
}
while (indOpArray != -1) {
fieldName = fieldName.substring(indOpArray + 1);
int indClArray = fieldName.indexOf("]");
if (indClArray == -1) {
throw new IllegalArgumentException(String.format("Illegal field array modifier %s", fieldSequence[i]));
}
if(indClArray == fieldName.length() - 1) {
indOpArray = -1;
} else if(fieldName.charAt(indClArray + 1) == '[') {
indOpArray = indClArray + 1;
} else {
throw new IllegalArgumentException(String.format("Illegal field array modifier %s", fieldSequence[i]));
}
int index = Integer.parseInt(fieldName.substring(0, indClArray));
if (last && indOpArray == -1) {
indexToAccess = index;
} else {
Object obj = null;
if (index < jsonListLocal.size() && index >= 0) {
obj = jsonListLocal.get(index);
} else if (op.operation == SET_OPERATION && (index == -1 || index == jsonListLocal.size())) {
index = jsonListLocal.size();
jsonListLocal.add(null);
} else {
throw new IllegalArgumentException(
String.format("Illegal access to array at position %d", index));
}
if (obj == null) {
if (op.operation == GET_OPERATION) {
// don't modify during get operation
return null;
}
if (indOpArray == -1) {
obj = new TreeMap<>();
} else {
obj = new ArrayList<Object>();
}
jsonListLocal.set(index, obj);
}
if(indOpArray != -1) {
jsonListLocal = (List<Object>) obj;
} else {
jsonObjLocal = (Map<String, Object>) obj;
jsonListLocal = null;
}
}
}
}
}
if(jsonListLocal != null) {
return accessListField(op, jsonListLocal, indexToAccess);
} else {
return accessObjField(op, jsonObjLocal, fieldName);
}
}
private static Object accessObjField(OperationAccess op, Map<String, Object> jsonObjLocal, String fieldName) {
Object prevValue;
if (op.operation == DELETE_OPERATION) {
prevValue = jsonObjLocal.remove(fieldName);
} else if (op.operation == SET_OPERATION) {
prevValue = jsonObjLocal.put(fieldName, op.value);
} else {
prevValue = jsonObjLocal.get(fieldName);
}
return prevValue;
}
private static Object accessListField(OperationAccess op, List<Object> jsonListLocal, int indexToAccess) {
Object prevValue;
int lastIndex = indexToAccess;
if (op.operation == DELETE_OPERATION) {
if (lastIndex >= jsonListLocal.size() || lastIndex < 0) {
prevValue = null;
} else {
prevValue = jsonListLocal.remove(lastIndex);
}
} else if (op.operation == SET_OPERATION) {
if (lastIndex == jsonListLocal.size() || lastIndex == -1) {
prevValue = null;
jsonListLocal.add(op.value);
} else if (lastIndex >= jsonListLocal.size() || lastIndex < 0) {
throw new IllegalArgumentException(String.format("Illegal access to %d position in array with size %d",
lastIndex, jsonListLocal.size()));
} else {
prevValue = jsonListLocal.set(lastIndex, op.value);
}
} else {
if (lastIndex >= jsonListLocal.size() || lastIndex < 0) {
prevValue = null;
} else {
prevValue = jsonListLocal.get(lastIndex);
}
}
return prevValue;
}
@SuppressWarnings("unchecked")
public static List<Object> getIndexObjectByField(Object obj, List<String> field, List<Object> res) {
if(obj == null) {
return res;
}
if(field.size() == 0) {
if(res == null) {
res = new ArrayList<Object>();
}
res.add(obj);
return res;
}
if (obj instanceof Map) {
String fieldFirst = field.get(0);
Object value = ((Map<String, Object>) obj).get(fieldFirst);
return getIndexObjectByField(value, field.subList(1, field.size()), res);
} else if(obj instanceof Collection) {
for(Object o : ((Collection<Object>)obj)) {
res = getIndexObjectByField(o, field, res);
}
} else {
// we need extract but there no field
LOGGER.warn(String.format("Can't access field %s for object %s", field, obj));
}
return res;
}
}

View file

@ -0,0 +1,122 @@
package net.osmand.plus.osmedit.utils.util;
import java.util.List;
public class OUtils {
public static boolean isEmpty(String s) {
return s == null || s.trim().length() == 0;
}
public static boolean validateSqlIdentifier(String id, StringBuilder errorMessage, String field, String action) {
if(isEmpty(id)) {
errorMessage.append(String.format("Field '%s' is not specified which is necessary to %s", field, action));
return false;
}
if(!isValidJavaIdentifier(id)) {
errorMessage.append(String.format("Value '%s' is not valid for %s to %s", id, field, action));
return false;
}
return true;
}
public static long combine(int x1, int x2) {
long l = Integer.toUnsignedLong(x1);
l = (l << 32) | Integer.toUnsignedLong(x2);
return l;
}
public static int first(long l) {
long s = Integer.MAX_VALUE;
int t = (int) ((l & (s << 32)) >> 32);
if(l < 0) {
t = -t;
}
return t;
}
public static int second(long l) {
int t = (int) (l & Integer.MAX_VALUE);
if ((l & 0x80000000l) != 0) {
t = -t;
}
return t;
}
public static boolean isValidJavaIdentifier(String s) {
if (s.isEmpty()) {
return false;
}
if (!Character.isJavaIdentifierStart(s.charAt(0))) {
return false;
}
for (int i = 1; i < s.length(); i++) {
if (!Character.isJavaIdentifierPart(s.charAt(i))) {
return false;
}
}
return true;
}
public static boolean equals(List<?> s1, List<?> s2) {
if(s1 == null || s1.size() == 0) {
return s2 == null || s2.size() == 0;
}
if(s2 == null || s1.size() != s2.size()) {
return false;
}
for(int i = 0; i < s1.size(); i++) {
Object o1 = s1.get(i);
Object o2 = s2.get(i);
if(o1 == null) {
if(o2 != null) {
return false;
}
} else {
if(!o1.equals(o2)) {
return false;
}
}
}
return true;
}
public static boolean equals(Object s1, Object s2) {
if(s1 == null || s2 == null) {
return s1 == s2;
}
if(s1 instanceof Number && s2 instanceof Number) {
return ((Number) s1).longValue() == ((Number) s2).longValue() &&
((Number) s1).doubleValue() == ((Number) s2).doubleValue();
}
return s1.equals(s2);
}
public static boolean equalsStringValue(Object s1, Object s2) {
if(s1 == null || s2 == null) {
return s1 == s2;
}
return s1.toString().equals(s2.toString());
}
public static long parseLongSilently(String input, long def) {
if (input != null && input.length() > 0) {
try {
return Long.parseLong(input);
} catch (NumberFormatException e) {
return def;
}
}
return def;
}
public static int parseIntSilently(String input, int def) {
if (input != null && input.length() > 0) {
try {
return Integer.parseInt(input);
} catch (NumberFormatException e) {
return def;
}
}
return def;
}
}

View file

@ -0,0 +1,15 @@
package net.osmand.plus.osmedit.utils.util.exception;
public class ConnectionException extends TechnicalException {
private static final long serialVersionUID = 8191498058841215578L;
public ConnectionException(String message, Throwable cause) {
super(message, cause);
}
public ConnectionException(String message) {
super(message);
}
}

View file

@ -0,0 +1,16 @@
package net.osmand.plus.osmedit.utils.util.exception;
public class FailedVerificationException extends Exception {
private static final long serialVersionUID = -4936205097177668159L;
public FailedVerificationException(Exception e) {
super(e);
}
public FailedVerificationException(String msg) {
super(msg);
}
}

View file

@ -0,0 +1,15 @@
package net.osmand.plus.osmedit.utils.util.exception;
public class TechnicalException extends RuntimeException {
private static final long serialVersionUID = 9201898433665734132L;
public TechnicalException(String message, Throwable cause) {
super(message, cause);
}
public TechnicalException(String message) {
super(message);
}
}