I am using proto3 for an Android app and I am having a problem with object equality, which makes it really hard on testing, especially for verify methods
Here is a unit-test representing the problem:
#Test
public void test_equal () {
PlayerCards playerCards1 = new PlayerCards();
playerCards1.playerId = 1;
playerCards1.cards = new int[]{2};
PlayerCards playerCards2 = new PlayerCards();
playerCards2.playerId = 1;
playerCards2.cards = new int[]{2};
assertThat(playerCards1.toString(), is(playerCards2.toString())); // pass
assertThat(PlayerCards.toByteArray(playerCards1),
is(PlayerCards.toByteArray(playerCards2))); // pass
assertThat(playerCards1, is(playerCards2)); // <----- fail
}
It is quite clear that the method equals is not working properly, checking the produced code (attached at the bottom) no equals, hashcode are generated.
I can workaround the assertThat by using .toString method but I cannot find any other way for verifications,
eg. verify(anyMock).anyMethod(playerCards)
I am afraid that this may also affect my runtime if am not extremely careful on checks.
Is there any way to generate equals, hashcode?
If not, can I at least extend, override verify so as to use toString when checking against proto-generated objects?
code-snippets:
My proto file is:
syntax = "proto3";
option java_multiple_files = true;
option optimize_for = LITE_RUNTIME; // existing or not has no effect.
option java_package = "com.package.my";
option java_outer_classname = "Proto";
option objc_class_prefix = "ABC";
package com.package.protos;
message PlayerCards {
int64 playerId = 1;
repeated int32 cards = 2;
}
I generate the files through gradle build and use the following properties
buildscript {
// ...
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0'
}
}
apply plugin: 'com.google.protobuf'
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.1.0'
}
generateProtoTasks {
all().each { task ->
task.builtins {
remove java
javanano {
// Options added to --javanano_out
option 'ignore_services=true'
option 'enum_style=java'
option 'generate_intdefs=true'
}
}
}
}
}
dependencies {
// ...
compile 'io.grpc:grpc-protobuf-nano:1.0.2'
}
The generated output:
// Generated by the protocol buffer compiler. DO NOT EDIT!
package com.package.my.nano;
#SuppressWarnings("hiding")
public final class PlayerCards extends
com.google.protobuf.nano.MessageNano {
private static volatile PlayerCards[] _emptyArray;
public static PlayerCards[] emptyArray() {
// Lazily initializes the empty array
if (_emptyArray == null) {
synchronized (
com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
if (_emptyArray == null) {
_emptyArray = new PlayerCards[0];
}
}
}
return _emptyArray;
}
// optional int64 playerId = 1;
public long playerId;
// repeated int32 cards = 2;
public int[] cards;
public PlayerCards() {
clear();
}
public PlayerCards clear() {
playerId = 0L;
cards = com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY;
cachedSize = -1;
return this;
}
#Override
public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output)
throws java.io.IOException {
if (this.playerId != 0L) {
output.writeInt64(1, this.playerId);
}
if (this.cards != null && this.cards.length > 0) {
for (int i = 0; i < this.cards.length; i++) {
output.writeInt32(2, this.cards[i]);
}
}
super.writeTo(output);
}
#Override
protected int computeSerializedSize() {
int size = super.computeSerializedSize();
if (this.playerId != 0L) {
size += com.google.protobuf.nano.CodedOutputByteBufferNano
.computeInt64Size(1, this.playerId);
}
if (this.cards != null && this.cards.length > 0) {
int dataSize = 0;
for (int i = 0; i < this.cards.length; i++) {
int element = this.cards[i];
dataSize += com.google.protobuf.nano.CodedOutputByteBufferNano
.computeInt32SizeNoTag(element);
}
size += dataSize;
size += 1 * this.cards.length;
}
return size;
}
#Override
public PlayerCards mergeFrom(
com.google.protobuf.nano.CodedInputByteBufferNano input)
throws java.io.IOException {
while (true) {
int tag = input.readTag();
switch (tag) {
case 0:
return this;
default: {
if (!com.google.protobuf.nano.WireFormatNano.parseUnknownField(input, tag)) {
return this;
}
break;
}
case 8: {
this.playerId = input.readInt64();
break;
}
case 16: {
int arrayLength = com.google.protobuf.nano.WireFormatNano
.getRepeatedFieldArrayLength(input, 16);
int i = this.cards == null ? 0 : this.cards.length;
int[] newArray = new int[i + arrayLength];
if (i != 0) {
java.lang.System.arraycopy(this.cards, 0, newArray, 0, i);
}
for (; i < newArray.length - 1; i++) {
newArray[i] = input.readInt32();
input.readTag();
}
// Last one without readTag.
newArray[i] = input.readInt32();
this.cards = newArray;
break;
}
case 18: {
int length = input.readRawVarint32();
int limit = input.pushLimit(length);
// First pass to compute array length.
int arrayLength = 0;
int startPos = input.getPosition();
while (input.getBytesUntilLimit() > 0) {
input.readInt32();
arrayLength++;
}
input.rewindToPosition(startPos);
int i = this.cards == null ? 0 : this.cards.length;
int[] newArray = new int[i + arrayLength];
if (i != 0) {
java.lang.System.arraycopy(this.cards, 0, newArray, 0, i);
}
for (; i < newArray.length; i++) {
newArray[i] = input.readInt32();
}
this.cards = newArray;
input.popLimit(limit);
break;
}
}
}
}
public static PlayerCards parseFrom(byte[] data)
throws com.google.protobuf.nano.InvalidProtocolBufferNanoException {
return com.google.protobuf.nano.MessageNano.mergeFrom(new PlayerCards(), data);
}
public static PlayerCards parseFrom(
com.google.protobuf.nano.CodedInputByteBufferNano input)
throws java.io.IOException {
return new PlayerCards().mergeFrom(input);
}
}
Proved a missing documentation.
Googling around I noticed the following property in a pom (!?) file generate_equals=true.
After adding this to gradle generation options the methods equals, hashcode generated!
ie.
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.1.0'
}
generateProtoTasks {
all().each { task ->
task.builtins {
remove java
javanano {
// Options added to --javanano_out
option 'ignore_services=true'
option 'enum_style=java'
option 'generate_intdefs=true'
option 'generate_equals=true' // <--- this one
}
}
}
}
}
Related
What would be the equivalent foreach loop look like in android java I am porting the below code to android .This for loops is working for c#.
foreach (XmlNode candidate in parent.ChildNodes)
{
if (candidate is XmlElement && candidate.Name == element.Name)
{
if (candidate == element)
{
return index;
}
index++;
}
}
below is my function for android which gets error in the for loop:
private static int FindElementIndex(Element element)
{
Node parentNode = element.getParentNode();
if (parentNode.equals(Node.DOCUMENT_NODE))
{
return 1;
}
Element parent = (Element)parentNode;
int index = 1;
//how should be the foreach of the below to be changed?
for (Node candidate : parent.getChildNodes()) {
if (candidate.equals(Node.ELEMENT_NODE) && candidate.getNodeName() == element.getNodeName())
{
if (candidate == element)
{
return index;
}
index++;
}
}
Log.d("Log_d","Couldn't find element within parent");
//throw new ArgumentException("Couldn't find element within parent");
}
// parent.ChilNodes is some type of colleciton like arraylist
some update it show be for not foreach
for(XmlNode candidate : parent.ChildNodes)
{
if (candidate instanceOf XmlElement && candidate.Name == element.Name)
{
if (candidate == element)
{
return index;
}
index++;
}
}
I changed the function and for loop in the function like this
private static int FindElementIndex(Element element)
{
Node parentNode = element.getParentNode();
if (parentNode.equals(Node.DOCUMENT_NODE))
{
return 1;
}
Element parent = (Element)parentNode;
int index = 1;
NodeList nodes = parent.getChildNodes();
for (int k = 0; k < nodes.getLength(); k++) {
Node candidate = nodes.item(k);
if (candidate.equals(Node.ELEMENT_NODE) && candidate.getNodeName() == element.getNodeName()) {
return index;
}
index++;
}
Log.d("Log_d","Couldn't find element within parent");
//throw new ArgumentException("Couldn't find element within parent");
return 0;
}
I try to unit test a method in my android project.
Everything works unit I try to set a value on a TextView.
Is it possible to skip one line of code or do I have to implement it as a UI Test?
This is the method I try to test:
public int compareEnteredValue(int batterySize, double valueFromEntry, double batteryArray[]) {
int output = 0;
for (int i = 0; i <= batteryArray.length - 1; i++) {
if (valueFromEntry >= batteryArray[i]) {
realChargeValue.setText((i * 5) + ""); // This is where the unit test fails
output = i*5;
}
if (valueFromEntry < batteryArray[batteryArray.length - 1]) {
realChargeValue.setText(R.string.hundredPercent);
output = 100;
}
}
return output;
}
You could break it down further. So that your unit does not set the text from within.
public int compareEnteredValue(int batterySize, double valueFromEntry, double batteryArray[]) {
int output = 0;
for (int i = 0; i <= batteryArray.length - 1; i++) {
if (valueFromEntry >= batteryArray[i]) {
//realChargeValue.setText((i * 5) + ""); // This is where the unit test fails
output = i*5;
}
if (valueFromEntry < batteryArray[batteryArray.length - 1]) {
//realChargeValue.setText(R.string.hundredPercent);
output = 100;
}
}
return output;
}
Then where you call the method..
int value = compareEnteredValue(....);
if(value == 100)
realChargeValue.setText(R.string.hundredPercent);
else
realChargeValue.setText(value + "");
You can then test the unit with no issue of the setText.
Adding to #Doomsknight answer, it is usually not required to Unit test every single line of your code. But in case you are passionate enough to test even to that level, here is how you can achieve it. I am using some added interfaces to showcase the testing.
public class TestableClass{
private UIElement realChargeValue;
public TestableClass(UIElement uiElem){
realChargeValue = uiElem;
}
public int compareEnteredValue(int batterySize, double valueFromEntry, double batteryArray[]) {
//Your method which needs to test realChargeValue
int output = 0;
for (int i = 0; i <= batteryArray.length - 1; i++) {
if (valueFromEntry >= batteryArray[i]) {
realChargeValue.setText((i * 5) + ""); // This is where the unit test fails
output = i*5;
}
if (valueFromEntry < batteryArray[batteryArray.length - 1]) {
realChargeValue.setText(R.string.hundredPercent);
output = 100;
}
}
return output;
}
}
public interface UIElement{
boolean setText();
}
For test class, you use as follows:
public class Test{
#Test
public void verify_compareEnteredValue_sets_correct_text(){
ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
UIElement dummyUIElement = mock(UIElement.class);
doReturn(true).when(dummyUIElement).setText(argumentCaptor.capture());
new TestableClass(dummyUIElement).compareEnteredValue();
List<String> expectedValues = argumentCaptor.getAllValues();
//do all asserts here over expectedValues
}
}
I'd like to use ExoPlayer2 with playlists having possibility to dinamically change the tracks (add or remove them from playlist) and change the loop settings.
Since ConcatenatingMediaSource has static arrays (and not lists), I'm implementing a DynamicMediaSource, like Concatenating one but with lists instead of arrays and one mode method addSource to add one more media source to the list.
public void addSource(MediaSource mediaSource) {
this.mediaSources.add(mediaSource);
duplicateFlags = buildDuplicateFlags(this.mediaSources);
if(!mediaSources.isEmpty())
prepareSource(mediaSources.size() -1);
else
prepareSource(0);
}
When I invoke addSource
MediaSource ms = buildMediaSource(mynewuri, null);
mediaSource.addSource(ms);
the track is added to the arrays but it seems something is missing because I always obtain ArrayOutOfBoundsException in createPeriod method.
In createPeriod the method
mediaSources.get(sourceIndex)...
is trying to access the index = mediaSources.size().
Can you help me?
I eventually managed it.
It was my fault during the conversion from arrays to lists.
I had to use SparseArrays for timelines and manifests and everything began to work.
In the DynamicMediaSource simply set the following types:
private final List<MediaSource> mediaSources;
private final SparseArray<Timeline> timelines;
private final SparseArray<Object> manifests;
private final Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
private SparseArray<Boolean> duplicateFlags;
you have to use sparse arrays to set the proper values into the timelines and manifests in the method
private void handleSourceInfoRefreshed(int sourceFirstIndex, Timeline sourceTimeline,
Object sourceManifest) {
// Set the timeline and manifest.
timelines.put(sourceFirstIndex, sourceTimeline);
manifests.put(sourceFirstIndex, sourceManifest);
// Also set the timeline and manifest for any duplicate entries of the same source.
for (int i = sourceFirstIndex + 1; i < mediaSources.size(); i++) {
if (mediaSources.get(i).equals(mediaSources.get(sourceFirstIndex))) {
timelines.put(i, sourceTimeline);
manifests.put(i, sourceManifest);
}
}
for(int i= 0; i<mediaSources.size(); i++){
if(timelines.get(i) == null){
// Don't invoke the listener until all sources have timelines.
return;
}
}
timeline = new DynamicTimeline(new ArrayList(asList(timelines)));
listener.onSourceInfoRefreshed(timeline, new ArrayList(asList(manifests)));
}
Here is the complete code of DynamicMediaSource class:
public final class DynamicMediaSource implements MediaSource {
private static final String TAG = "DynamicSource";
private final List<MediaSource> mediaSources;
private final List<Timeline> timelines;
private final List<Object> manifests;
private final Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
private SparseArray<Boolean> duplicateFlags;
private Listener listener;
private DynamicTimeline timeline;
/**
* #param mediaSources The {#link MediaSource}s to concatenate. It is valid for the same
* {#link MediaSource} instance to be present more than once in the array.
*/
public DynamicMediaSource(MediaSource... mediaSources) {
this.mediaSources = new ArrayList<MediaSource>(Arrays.asList(mediaSources));
timelines = new ArrayList<Timeline>();
manifests = new ArrayList<Object>();
sourceIndexByMediaPeriod = new HashMap<>();
duplicateFlags = buildDuplicateFlags(this.mediaSources);
}
public void addSource(MediaSource mediaSource) {
this.mediaSources.add(mediaSource);
duplicateFlags = buildDuplicateFlags(this.mediaSources);
/*if(!mediaSources.isEmpty())
prepareSource(mediaSources.size() -1);
else
prepareSource(0);*/
}
#Override
public void prepareSource(Listener listener) {
this.listener = listener;
for (int i = 0; i < mediaSources.size(); i++) {
prepareSource(i);
/*if (duplicateFlags.get(i) == null || !duplicateFlags.get(i)) {
final int index = i;
mediaSources.get(i).prepareSource(new Listener() {
#Override
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
handleSourceInfoRefreshed(index, timeline, manifest);
}
});
}*/
}
}
private void prepareSource(int sourceindex) {
if (duplicateFlags.get(sourceindex) == null || !duplicateFlags.get(sourceindex)) {
final int index = sourceindex;
mediaSources.get(sourceindex).prepareSource(new Listener() {
#Override
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
handleSourceInfoRefreshed(index, timeline, manifest);
}
});
}
}
#Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
for (int i = 0; i < mediaSources.size(); i++) {
if (duplicateFlags.get(i) == null || !duplicateFlags.get(i)) {
mediaSources.get(i).maybeThrowSourceInfoRefreshError();
}
}
}
#Override
public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator,
long positionUs) {
int sourceIndex = timeline.getSourceIndexForPeriod(index);
int periodIndexInSource = index - timeline.getFirstPeriodIndexInSource(sourceIndex);
MediaPeriod mediaPeriod = mediaSources.get(sourceIndex).createPeriod(periodIndexInSource, callback,
allocator, positionUs);
sourceIndexByMediaPeriod.put(mediaPeriod, sourceIndex);
return mediaPeriod;
}
#Override
public void releasePeriod(MediaPeriod mediaPeriod) {
int sourceIndex = sourceIndexByMediaPeriod.get(mediaPeriod);
sourceIndexByMediaPeriod.remove(mediaPeriod);
mediaSources.get(sourceIndex).releasePeriod(mediaPeriod);
}
#Override
public void releaseSource() {
for (int i = 0; i < mediaSources.size(); i++) {
if (duplicateFlags.get(i) == null || !duplicateFlags.get(i)) {
mediaSources.get(i).releaseSource();
}
}
}
private void handleSourceInfoRefreshed(int sourceFirstIndex, Timeline sourceTimeline,
Object sourceManifest) {
// Set the timeline and manifest.
timelines.add(sourceFirstIndex, sourceTimeline);
manifests.add(sourceFirstIndex, sourceManifest);
// Also set the timeline and manifest for any duplicate entries of the same source.
for (int i = sourceFirstIndex + 1; i < mediaSources.size(); i++) {
if (mediaSources.get(i).equals(mediaSources.get(sourceFirstIndex))) {
timelines.add(i, sourceTimeline);
manifests.add(i, sourceManifest);
}
}
for (Timeline timeline : timelines) {
if (timeline == null) {
// Don't invoke the listener until all sources have timelines.
return;
}
}
timeline = new DynamicTimeline(new ArrayList(timelines));
listener.onSourceInfoRefreshed(timeline, new ArrayList(manifests));
}
private static SparseArray<Boolean> buildDuplicateFlags(List<MediaSource> mediaSources) {
SparseArray<Boolean> duplicateFlags = new SparseArray<Boolean>();
IdentityHashMap<MediaSource, Void> sources = new IdentityHashMap<>(mediaSources.size());
for (int i = 0; i < mediaSources.size(); i++) {
MediaSource mediaSource = mediaSources.get(i);
if (!sources.containsKey(mediaSource)) {
sources.put(mediaSource, null);
} else {
duplicateFlags.setValueAt(i, true);
}
}
return duplicateFlags;
}
/**
* A {#link Timeline} that is the concatenation of one or more {#link Timeline}s.
*/
private static final class DynamicTimeline extends Timeline {
private final List<Timeline> timelines;
private final List<Integer> sourcePeriodOffsets;
private final List<Integer> sourceWindowOffsets;
public DynamicTimeline(List<Timeline> timelines) {
List<Integer> sourcePeriodOffsets = new ArrayList<>();
List<Integer> sourceWindowOffsets = new ArrayList<>();
int periodCount = 0;
int windowCount = 0;
for (Timeline timeline : timelines) {
periodCount += timeline.getPeriodCount();
windowCount += timeline.getWindowCount();
sourcePeriodOffsets.add(periodCount);
sourceWindowOffsets.add(windowCount);
}
this.timelines = timelines;
this.sourcePeriodOffsets = sourcePeriodOffsets;
this.sourceWindowOffsets = sourceWindowOffsets;
}
#Override
public int getWindowCount() {
return sourceWindowOffsets.get(sourceWindowOffsets.size() - 1);
}
#Override
public Window getWindow(int windowIndex, Window window, boolean setIds) {
int sourceIndex = getSourceIndexForWindow(windowIndex);
int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
timelines.get(sourceIndex).getWindow(windowIndex - firstWindowIndexInSource, window, setIds);
window.firstPeriodIndex += firstPeriodIndexInSource;
window.lastPeriodIndex += firstPeriodIndexInSource;
return window;
}
#Override
public int getPeriodCount() {
return sourcePeriodOffsets.get(sourcePeriodOffsets.size() - 1);
}
#Override
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
int sourceIndex = getSourceIndexForPeriod(periodIndex);
int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
timelines.get(sourceIndex).getPeriod(periodIndex - firstPeriodIndexInSource, period, setIds);
period.windowIndex += firstWindowIndexInSource;
if (setIds) {
period.uid = Pair.create(sourceIndex, period.uid);
}
return period;
}
#Override
public int getIndexOfPeriod(Object uid) {
if (!(uid instanceof Pair)) {
return C.INDEX_UNSET;
}
Pair<?, ?> sourceIndexAndPeriodId = (Pair<?, ?>) uid;
if (!(sourceIndexAndPeriodId.first instanceof Integer)) {
return C.INDEX_UNSET;
}
int sourceIndex = (Integer) sourceIndexAndPeriodId.first;
Object periodId = sourceIndexAndPeriodId.second;
if (sourceIndex < 0 || sourceIndex >= timelines.size()) {
return C.INDEX_UNSET;
}
int periodIndexInSource = timelines.get(sourceIndex).getIndexOfPeriod(periodId);
return periodIndexInSource == C.INDEX_UNSET ? C.INDEX_UNSET
: getFirstPeriodIndexInSource(sourceIndex) + periodIndexInSource;
}
private int getSourceIndexForPeriod(int periodIndex) {
return Util.binarySearchFloor(sourcePeriodOffsets, periodIndex, true, false) + 1;
}
private int getFirstPeriodIndexInSource(int sourceIndex) {
return sourceIndex == 0 ? 0 : sourcePeriodOffsets.get(sourceIndex - 1);
}
private int getSourceIndexForWindow(int windowIndex) {
return Util.binarySearchFloor(sourceWindowOffsets, windowIndex, true, false) + 1;
}
private int getFirstWindowIndexInSource(int sourceIndex) {
return sourceIndex == 0 ? 0 : sourceWindowOffsets.get(sourceIndex - 1);
}
}
}
How to compare app version in android
I got latest version code and current version code , but the problem is
current version is 1.0
and latest version is 1.0.0
so how to compare that float value in android
I have written a small Android library for comparing version numbers: https://github.com/G00fY2/version-compare
What it basically does is this:
public int compareVersions(String versionA, String versionB) {
String[] versionTokensA = versionA.split("\\.");
String[] versionTokensB = versionB.split("\\.");
List<Integer> versionNumbersA = new ArrayList<>();
List<Integer> versionNumbersB = new ArrayList<>();
for (String versionToken : versionTokensA) {
versionNumbersA.add(Integer.parseInt(versionToken));
}
for (String versionToken : versionTokensB) {
versionNumbersB.add(Integer.parseInt(versionToken));
}
final int versionASize = versionNumbersA.size();
final int versionBSize = versionNumbersB.size();
int maxSize = Math.max(versionASize, versionBSize);
for (int i = 0; i < maxSize; i++) {
if ((i < versionASize ? versionNumbersA.get(i) : 0) > (i < versionBSize ? versionNumbersB.get(i) : 0)) {
return 1;
} else if ((i < versionASize ? versionNumbersA.get(i) : 0) < (i < versionBSize ? versionNumbersB.get(i) : 0)) {
return -1;
}
}
return 0;
}
This snippet doesn't offer any error checks or handling. Beside that my library also supports suffixes like "1.2-rc" > "1.2-beta".
I am a bit late to the party but I have a great solution for all of you!
1. Use this class:
public class VersionComparator implements Comparator {
public boolean equals(Object o1, Object o2) {
return compare(o1, o2) == 0;
}
public int compare(Object o1, Object o2) {
String version1 = (String) o1;
String version2 = (String) o2;
VersionTokenizer tokenizer1 = new VersionTokenizer(version1);
VersionTokenizer tokenizer2 = new VersionTokenizer(version2);
int number1, number2;
String suffix1, suffix2;
while (tokenizer1.MoveNext()) {
if (!tokenizer2.MoveNext()) {
do {
number1 = tokenizer1.getNumber();
suffix1 = tokenizer1.getSuffix();
if (number1 != 0 || suffix1.length() != 0) {
// Version one is longer than number two, and non-zero
return 1;
}
}
while (tokenizer1.MoveNext());
// Version one is longer than version two, but zero
return 0;
}
number1 = tokenizer1.getNumber();
suffix1 = tokenizer1.getSuffix();
number2 = tokenizer2.getNumber();
suffix2 = tokenizer2.getSuffix();
if (number1 < number2) {
// Number one is less than number two
return -1;
}
if (number1 > number2) {
// Number one is greater than number two
return 1;
}
boolean empty1 = suffix1.length() == 0;
boolean empty2 = suffix2.length() == 0;
if (empty1 && empty2) continue; // No suffixes
if (empty1) return 1; // First suffix is empty (1.2 > 1.2b)
if (empty2) return -1; // Second suffix is empty (1.2a < 1.2)
// Lexical comparison of suffixes
int result = suffix1.compareTo(suffix2);
if (result != 0) return result;
}
if (tokenizer2.MoveNext()) {
do {
number2 = tokenizer2.getNumber();
suffix2 = tokenizer2.getSuffix();
if (number2 != 0 || suffix2.length() != 0) {
// Version one is longer than version two, and non-zero
return -1;
}
}
while (tokenizer2.MoveNext());
// Version two is longer than version one, but zero
return 0;
}
return 0;
}
// VersionTokenizer.java
public static class VersionTokenizer {
private final String _versionString;
private final int _length;
private int _position;
private int _number;
private String _suffix;
private boolean _hasValue;
VersionTokenizer(String versionString) {
if (versionString == null)
throw new IllegalArgumentException("versionString is null");
_versionString = versionString;
_length = versionString.length();
}
public int getNumber() {
return _number;
}
String getSuffix() {
return _suffix;
}
public boolean hasValue() {
return _hasValue;
}
boolean MoveNext() {
_number = 0;
_suffix = "";
_hasValue = false;
// No more characters
if (_position >= _length)
return false;
_hasValue = true;
while (_position < _length) {
char c = _versionString.charAt(_position);
if (c < '0' || c > '9') break;
_number = _number * 10 + (c - '0');
_position++;
}
int suffixStart = _position;
while (_position < _length) {
char c = _versionString.charAt(_position);
if (c == '.') break;
_position++;
}
_suffix = _versionString.substring(suffixStart, _position);
if (_position < _length) _position++;
return true;
}
}
}
2. create this function
private fun isNewVersionAvailable(currentVersion: String, latestVersion: String): Boolean {
val versionComparator = VersionComparator()
val result: Int = versionComparator.compare(currentVersion, latestVersion)
var op = "=="
if (result < 0) op = "<"
if (result > 0) op = ">"
System.out.printf("%s %s %s\n", currentVersion, op, latestVersion)
return if (op == ">" || op == "==") {
false
} else op == "<"
}
3. and just call it by
e.g. isNewVersionAvailable("1.2.8","1.2.9") where 1.2.8 is your current version here and 1.2.9 is the latest version, which returns true!
Why overcomplicate this so much?
Just scale the major, minor, patch version and you have it covered:
fun getAppVersionFromString(version: String): Int { // "2.3.5"
val versions = version.split(".") // [2, 3, 5]
val major = versions[0].toIntOrDefault(0) * 10000 // 20000
val minor = versions[1].toIntOrDefault(0) * 1000 // 3000
val patch = versions[2].toIntOrDefault(0) * 100 // 500
return major + minor + patch // 2350
}
That way when you compare e.g 9.10.10 with 10.0.0 the second one is greater.
Use the following method to compare the versions number:
Convert float to String first.
public static int versionCompare(String str1, String str2) {
String[] vals1 = str1.split("\\.");
String[] vals2 = str2.split("\\.");
int i = 0;
// set index to first non-equal ordinal or length of shortest version string
while (i < vals1.length && i < vals2.length && vals1[i].equals(vals2[i])) {
i++;
}
// compare first non-equal ordinal number
if (i < vals1.length && i < vals2.length) {
int diff = Integer.valueOf(vals1[i]).compareTo(Integer.valueOf(vals2[i]));
return Integer.signum(diff);
}
// the strings are equal or one string is a substring of the other
// e.g. "1.2.3" = "1.2.3" or "1.2.3" < "1.2.3.4"
return Integer.signum(vals1.length - vals2.length);
}
Refer the following SO question : Efficient way to compare version strings in Java
I am trying to do a jigsaw puzzle app in android. In this, I have split a Bitmap into many small chunks. These chunks are then displayed in a GridViewNow I need to shuffle them. Then, I need to know each image chunk's actualPosition(where the piece was supposed to be, its actual location in the image) and its currentPosition(where the piece is currently located). actualPosition and currentPosition are 2 integer arrays. So is there a way that I can get each image chunk's currentPosition and actualPosition after the shuffling so that after every move that the user make I can check wether every image chunk's actualPosition equals its currentPosition. If so the user wins the game. Can anyone please help me out.
Below is the number puzzle game in pure Java that works. Can be run from command line.
It re-prints the whole matrix after every move (not pretty). It demos the basic game.
I hope most of the code is self explanatory. This shows the basic 2-dim mapping of the game, position tracking, validating based on numbers. Have fun.
package madhav.turangi.basic.game;
import java.util.Random;
import java.util.Scanner;
public class NumberPuzzle {
int size;
int[][] arr;
int spaceRow;
int spaceCol;
int turnsTook;
public NumberPuzzle(int size) {
this.size = size;
arr = new int[size][size];
}
void init()
{
for(int r=0; r<size; r++)
{
for(int c=0; c<arr[r].length; c++)
{
arr[r][c] = r*size + c + 1; // row-column of cell to its value equation
}
}
spaceRow = spaceCol = size - 1; // bottom-right cell index
}
int readUserInput()
{
int value = -1;
boolean valid = false;
do {
System.out.printf("To move space [0 - Up, 1 - Down, 2 - Left, 3 - Right] : ? ");
Scanner sc = new Scanner(System.in);
String line = sc.nextLine();
try
{
value = Integer.parseInt(line);
valid = (value>=0 && value<=3);
}
catch(NumberFormatException ne)
{
}
if(! valid) System.out.println("== Invalid ==");
} while (! valid);
return value;
}
void swap(int aRow, int aCol, int withRow, int withCol)
{
int temp = arr[aRow][aCol];
arr[aRow][aCol] = arr[withRow][withCol];
arr[withRow][withCol] = temp;
}
boolean moveUp()
{
if(spaceRow != 0)
{
int newSpaceRow = spaceRow - 1;
swap(spaceRow, spaceCol, newSpaceRow, spaceCol);
spaceRow--;
return true;
}
else
{
return false;
}
}
boolean moveDown()
{
if(spaceRow != size-1)
{
int newSpaceRow = spaceRow + 1;
swap(spaceRow, spaceCol, newSpaceRow, spaceCol);
spaceRow++;
return true;
}
else
{
return false;
}
}
boolean moveRight()
{
if(spaceCol != size-1)
{
int newSpaceCol = spaceCol + 1;
swap(spaceRow, spaceCol, spaceRow, newSpaceCol);
spaceCol++;
return true;
}
else
{
return false;
}
}
boolean moveLeft()
{
if(spaceCol != 0)
{
int newSpaceCol = spaceCol - 1;
swap(spaceRow, spaceCol, spaceRow, newSpaceCol);
spaceCol--;
return true;
}
else
{
return false;
}
}
void shuffle()
{
Random rnd = new Random(System.currentTimeMillis());
boolean moved = false;
int attemptCount = 1;
int maxMoves = 20;
for(int moveCount=0; moveCount<maxMoves; moveCount++, attemptCount++)
{
int randomMoveDir = rnd.nextInt(4);
moved = move(randomMoveDir);
if(! moved) moveCount--; //ensure maxMoves number of moves
}
System.out.printf("Shuffle attempts %d\n",attemptCount);
}
boolean move(int dir)
{
boolean moved = false;
switch(dir)
{
case 0 : // up
moved = moveUp();
break;
case 1 : // down
moved = moveDown();
break;
case 2 : // left
moved = moveLeft();
break;
case 3 : // right
moved = moveRight();
break;
}
return moved;
}
void prnArray()
{
System.out.println("-- -- -- -- --");
for(int[] row : arr)
{
for(int cellValue : row)
{
String v = (cellValue == 16 ? "" : String.valueOf(cellValue));
System.out.printf("%4s", v);
}
System.out.println();
}
System.out.println("-- -- -- -- --");
}
boolean validate()
{
for(int r=0; r<size; r++)
{
for(int c=0; c<arr[r].length; c++)
{
if(arr[r][c] != (r*size + c + 1))
{
return false;
}
}
}
return true;
}
boolean oneTurn()
{
int dir = readUserInput();
boolean moved = move(dir);
boolean won = false;
if(moved)
{
turnsTook++;
prnArray();
won = validate();
}
else
{
System.out.println("= Invalid =");
}
return won;
}
void play()
{
init();
System.out.println("Before shuffle");
prnArray();
shuffle();
prnArray();
boolean won = false;
while(! won)
{
won = oneTurn();
}
System.out.printf("Won in %d\n", turnsTook);
}
public static void main(String[] args)
{
NumberPuzzle puzzle = new NumberPuzzle(4);
puzzle.play();
}
}