From 4fa55d6bbc1d20a80f2b3cd2f1cf74aaa902cb5b Mon Sep 17 00:00:00 2001 From: serso Date: Wed, 2 Mar 2016 08:25:41 +0100 Subject: [PATCH] Remove dependencies --- app/build.gradle | 1 - .../org/solovyev/android/calculator/App.java | 20 +- .../android/calculator/BaseNumberBuilder.java | 4 +- .../android/calculator/Calculator.java | 55 +- .../solovyev/android/calculator/Engine.java | 9 +- .../variables/VariablesFragment.java | 23 +- jscl/build.gradle | 1 - jscl/src/main/java/jscl/JsclMathEngine.java | 37 +- .../jscl/math/function/ExtendedConstant.java | 3 +- .../java/org/solovyev/common/Converter.java | 38 ++ .../org/solovyev/common/EqualsResult.java | 79 +++ .../org/solovyev/common/HashCodeBuilder.java | 550 ++++++++++++++++++ .../java/org/solovyev/common/JBuilder.java | 31 + .../java/org/solovyev/common/JPredicate.java | 33 ++ .../java/org/solovyev/common/Objects.java | 213 +++++++ .../solovyev/common/SynchronizedObject.java | 86 +++ .../common/collections/SortedList.java | 262 +++++++++ .../common/equals/CollectionEqualizer.java | 67 +++ .../org/solovyev/common/equals/Equalizer.java | 30 + .../solovyev/common/equals/ListEqualizer.java | 87 +++ .../common/math/AbstractMathRegistry.java | 25 +- .../org/solovyev/common/math/MathEntity.java | 16 - .../solovyev/common/msg/AbstractMessage.java | 134 +++++ .../common/msg/ListMessageRegistry.java | 51 ++ .../java/org/solovyev/common/msg/Message.java | 67 +++ .../org/solovyev/common/msg/MessageLevel.java | 32 + .../solovyev/common/msg/MessageRegistry.java | 54 ++ .../org/solovyev/common/msg/MessageType.java | 65 +++ .../org/solovyev/common/msg/Messages.java | 86 +++ .../msg/SynchronizedMessageRegistry.java | 70 +++ .../org/solovyev/common/msg/Utf8Control.java | 60 ++ .../common/search/StartsWithFinder.java | 60 ++ .../org/solovyev/common/text/Strings.java | 83 +++ 33 files changed, 2321 insertions(+), 111 deletions(-) create mode 100644 jscl/src/main/java/org/solovyev/common/Converter.java create mode 100644 jscl/src/main/java/org/solovyev/common/EqualsResult.java create mode 100644 jscl/src/main/java/org/solovyev/common/HashCodeBuilder.java create mode 100644 jscl/src/main/java/org/solovyev/common/JBuilder.java create mode 100644 jscl/src/main/java/org/solovyev/common/JPredicate.java create mode 100644 jscl/src/main/java/org/solovyev/common/Objects.java create mode 100644 jscl/src/main/java/org/solovyev/common/SynchronizedObject.java create mode 100644 jscl/src/main/java/org/solovyev/common/collections/SortedList.java create mode 100644 jscl/src/main/java/org/solovyev/common/equals/CollectionEqualizer.java create mode 100644 jscl/src/main/java/org/solovyev/common/equals/Equalizer.java create mode 100644 jscl/src/main/java/org/solovyev/common/equals/ListEqualizer.java create mode 100644 jscl/src/main/java/org/solovyev/common/msg/AbstractMessage.java create mode 100644 jscl/src/main/java/org/solovyev/common/msg/ListMessageRegistry.java create mode 100644 jscl/src/main/java/org/solovyev/common/msg/Message.java create mode 100644 jscl/src/main/java/org/solovyev/common/msg/MessageLevel.java create mode 100644 jscl/src/main/java/org/solovyev/common/msg/MessageRegistry.java create mode 100644 jscl/src/main/java/org/solovyev/common/msg/MessageType.java create mode 100644 jscl/src/main/java/org/solovyev/common/msg/Messages.java create mode 100644 jscl/src/main/java/org/solovyev/common/msg/SynchronizedMessageRegistry.java create mode 100644 jscl/src/main/java/org/solovyev/common/msg/Utf8Control.java create mode 100644 jscl/src/main/java/org/solovyev/common/search/StartsWithFinder.java create mode 100644 jscl/src/main/java/org/solovyev/common/text/Strings.java diff --git a/app/build.gradle b/app/build.gradle index 9a1bbe4e..a12218d0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -60,7 +60,6 @@ android { dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') - compile 'org.solovyev:common-msg:1.0.7' compile 'com.android.support:support-v4:23.2.0' compile 'com.android.support:appcompat-v7:23.2.0' compile 'com.android.support:cardview-v7:23.2.0' diff --git a/app/src/main/java/org/solovyev/android/calculator/App.java b/app/src/main/java/org/solovyev/android/calculator/App.java index ccd6d36c..22438da0 100644 --- a/app/src/main/java/org/solovyev/android/calculator/App.java +++ b/app/src/main/java/org/solovyev/android/calculator/App.java @@ -48,9 +48,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.widget.TextView; - import com.squareup.otto.Bus; - import org.solovyev.android.Check; import org.solovyev.android.calculator.floating.FloatingCalculatorService; import org.solovyev.android.calculator.ga.Ga; @@ -60,12 +58,10 @@ import org.solovyev.android.calculator.wizard.CalculatorWizards; import org.solovyev.android.wizard.Wizards; import org.solovyev.common.JPredicate; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; /** * This class aggregates several useful in any Android application interfaces and provides access to {@link android.app.Application} object from a static context. @@ -262,18 +258,6 @@ public final class App { return null; } - public static T find(@Nullable Collection collection, @Nonnull JPredicate finder) { - if (collection == null || collection.isEmpty()) { - return null; - } - for (T t : collection) { - if (finder.apply(t)) { - return t; - } - } - return null; - } - @Nullable public static String find(@Nonnull List tokens, @Nonnull String text, int position) { for (int i = 0; i < tokens.size(); i++) { diff --git a/app/src/main/java/org/solovyev/android/calculator/BaseNumberBuilder.java b/app/src/main/java/org/solovyev/android/calculator/BaseNumberBuilder.java index 1386c370..6b4b51d6 100644 --- a/app/src/main/java/org/solovyev/android/calculator/BaseNumberBuilder.java +++ b/app/src/main/java/org/solovyev/android/calculator/BaseNumberBuilder.java @@ -23,9 +23,9 @@ package org.solovyev.android.calculator; import android.text.SpannableStringBuilder; +import android.text.TextUtils; import jscl.NumeralBase; import org.solovyev.android.calculator.math.MathType; -import org.solovyev.common.text.Strings; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -60,7 +60,7 @@ public abstract class BaseNumberBuilder { } private boolean spaceBefore(@Nonnull MathType.Result mathTypeResult) { - return numberBuilder == null && Strings.isEmpty(mathTypeResult.match.trim()); + return numberBuilder == null && TextUtils.isEmpty(mathTypeResult.match.trim()); } private boolean numeralBaseInTheStart(@Nonnull MathType mathType) { diff --git a/app/src/main/java/org/solovyev/android/calculator/Calculator.java b/app/src/main/java/org/solovyev/android/calculator/Calculator.java index 766f26a2..75d9eff1 100644 --- a/app/src/main/java/org/solovyev/android/calculator/Calculator.java +++ b/app/src/main/java/org/solovyev/android/calculator/Calculator.java @@ -26,39 +26,8 @@ import android.content.SharedPreferences; import android.support.annotation.NonNull; import android.text.TextUtils; import android.util.Log; - import com.squareup.otto.Bus; import com.squareup.otto.Subscribe; - -import org.solovyev.android.Check; -import org.solovyev.android.calculator.calculations.CalculationCancelledEvent; -import org.solovyev.android.calculator.calculations.CalculationFailedEvent; -import org.solovyev.android.calculator.calculations.CalculationFinishedEvent; -import org.solovyev.android.calculator.calculations.ConversionFailedEvent; -import org.solovyev.android.calculator.calculations.ConversionFinishedEvent; -import org.solovyev.android.calculator.functions.FunctionsRegistry; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.android.calculator.variables.CppVariable; -import org.solovyev.common.msg.ListMessageRegistry; -import org.solovyev.common.msg.Message; -import org.solovyev.common.msg.MessageRegistry; -import org.solovyev.common.msg.MessageType; -import org.solovyev.common.text.Strings; -import org.solovyev.common.units.ConversionException; - -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicLong; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - import jscl.JsclArithmeticException; import jscl.MathEngine; import jscl.NumeralBase; @@ -67,6 +36,28 @@ import jscl.math.Generic; import jscl.math.function.Constants; import jscl.math.function.IConstant; import jscl.text.ParseInterruptedException; +import org.solovyev.android.Check; +import org.solovyev.android.calculator.calculations.*; +import org.solovyev.android.calculator.functions.FunctionsRegistry; +import org.solovyev.android.calculator.jscl.JsclOperation; +import org.solovyev.android.calculator.variables.CppVariable; +import org.solovyev.common.msg.ListMessageRegistry; +import org.solovyev.common.msg.Message; +import org.solovyev.common.msg.MessageRegistry; +import org.solovyev.common.msg.MessageType; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.measure.converter.ConversionException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicLong; @Singleton public class Calculator implements SharedPreferences.OnSharedPreferenceChangeListener { @@ -166,7 +157,7 @@ public class Calculator implements SharedPreferences.OnSharedPreferenceChangeLis @Nonnull String e, @Nonnull MessageRegistry mr) { e = e.trim(); - if (Strings.isEmpty(e)) { + if (TextUtils.isEmpty(e)) { bus.post(new CalculationFinishedEvent(o, e, sequence)); return; } diff --git a/app/src/main/java/org/solovyev/android/calculator/Engine.java b/app/src/main/java/org/solovyev/android/calculator/Engine.java index 958c743e..38b9f5d0 100644 --- a/app/src/main/java/org/solovyev/android/calculator/Engine.java +++ b/app/src/main/java/org/solovyev/android/calculator/Engine.java @@ -23,13 +23,15 @@ package org.solovyev.android.calculator; import android.content.SharedPreferences; +import android.text.TextUtils; import com.squareup.otto.Bus; import jscl.AngleUnit; import jscl.JsclMathEngine; import jscl.MathEngine; import jscl.NumeralBase; import jscl.math.operator.Operator; -import jscl.text.*; +import jscl.text.Identifier; +import jscl.text.Parser; import org.solovyev.android.Check; import org.solovyev.android.calculator.functions.FunctionsRegistry; import org.solovyev.android.calculator.operators.OperatorsRegistry; @@ -40,7 +42,6 @@ import org.solovyev.android.prefs.Preference; import org.solovyev.android.prefs.StringPreference; import org.solovyev.common.text.EnumMapper; import org.solovyev.common.text.NumberMapper; -import org.solovyev.common.text.Strings; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -104,7 +105,7 @@ public class Engine implements SharedPreferences.OnSharedPreferenceChangeListene } public static boolean isValidName(@Nullable String name) { - if (!Strings.isEmpty(name)) { + if (!TextUtils.isEmpty(name)) { try { Identifier.parser.parse(Parser.Parameters.get(name), null); return true; @@ -205,7 +206,7 @@ public class Engine implements SharedPreferences.OnSharedPreferenceChangeListene mathEngine.setRoundResult(Preferences.Output.round.getPreference(preferences)); final String groupingSeparator = Preferences.groupingSeparator.getPreference(preferences); - if (Strings.isEmpty(groupingSeparator)) { + if (TextUtils.isEmpty(groupingSeparator)) { mathEngine.setUseGroupingSeparator(false); } else { mathEngine.setUseGroupingSeparator(true); diff --git a/app/src/main/java/org/solovyev/android/calculator/variables/VariablesFragment.java b/app/src/main/java/org/solovyev/android/calculator/variables/VariablesFragment.java index 9275131a..90ed0065 100644 --- a/app/src/main/java/org/solovyev/android/calculator/variables/VariablesFragment.java +++ b/app/src/main/java/org/solovyev/android/calculator/variables/VariablesFragment.java @@ -34,16 +34,14 @@ import org.solovyev.android.Check; import org.solovyev.android.calculator.*; import org.solovyev.android.calculator.entities.BaseEntitiesFragment; import org.solovyev.android.calculator.entities.Category; -import org.solovyev.android.calculator.RemovalConfirmationDialog; import org.solovyev.android.calculator.math.MathType; -import org.solovyev.common.JPredicate; -import org.solovyev.common.collections.Collections; import org.solovyev.common.text.Strings; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.inject.Inject; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; public class VariablesFragment extends BaseEntitiesFragment { @@ -77,16 +75,17 @@ public class VariablesFragment extends BaseEntitiesFragment { @Nonnull @Override protected List getEntities() { - final List result = new ArrayList<>(registry.getEntities()); - - Collections.removeAll(result, new JPredicate() { - @Override - public boolean apply(@Nullable IConstant var) { - return var != null && Collections.contains(var.getName(), MathType.INFINITY_JSCL, MathType.NAN); + final List entities = new ArrayList<>(registry.getEntities()); + for (Iterator it = entities.iterator(); it.hasNext(); ) { + final IConstant constant = it.next(); + switch (constant.getName()) { + case MathType.INFINITY_JSCL: + case MathType.NAN: + it.remove(); + break; } - }); - - return result; + } + return entities; } @Override diff --git a/jscl/build.gradle b/jscl/build.gradle index c14494f2..882ad138 100644 --- a/jscl/build.gradle +++ b/jscl/build.gradle @@ -24,7 +24,6 @@ apply plugin: 'java' dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') - compile 'org.solovyev:common-msg:1.0.7' compile 'com.google.code.findbugs:annotations:2.0.1' compile 'xerces:xercesImpl:2.8.0' diff --git a/jscl/src/main/java/jscl/JsclMathEngine.java b/jscl/src/main/java/jscl/JsclMathEngine.java index 26a76303..689b7c62 100644 --- a/jscl/src/main/java/jscl/JsclMathEngine.java +++ b/jscl/src/main/java/jscl/JsclMathEngine.java @@ -9,16 +9,16 @@ import jscl.math.operator.Percent; import jscl.math.operator.Rand; import jscl.math.operator.matrix.OperatorsRegistry; import jscl.text.ParseException; -import org.solovyev.common.JPredicate; -import org.solovyev.common.collections.Collections; import org.solovyev.common.math.MathRegistry; import org.solovyev.common.msg.MessageRegistry; import org.solovyev.common.msg.Messages; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; +import java.util.List; import java.util.Locale; public class JsclMathEngine implements MathEngine { @@ -165,20 +165,7 @@ public class JsclMathEngine implements MathEngine { // decimal numeral base => do specific formatting // detect if current number is precisely equals to constant in constants' registry (NOTE: ONLY FOR SYSTEM CONSTANTS) - final Double localValue = value; - IConstant constant = Collections.find(getConstantsRegistry().getSystemEntities(), new JPredicate() { - public boolean apply(@Nonnull IConstant constant) { - if (!localValue.equals(constant.getDoubleValue())) { - return false; - } - final String name = constant.getName(); - if (name.equals(Constants.PI_INV.getName()) || name.equals(Constants.ANS)) { - return false; - } - return !name.equals(Constants.PI.getName()) || getAngleUnits() == AngleUnit.rad; - } - }); - + IConstant constant = findConstant(getConstantsRegistry().getSystemEntities(), value); if (constant == null) { final IConstant piInv = this.getConstantsRegistry().get(Constants.PI_INV.getName()); @@ -231,6 +218,24 @@ public class JsclMathEngine implements MathEngine { } } + @Nullable + private IConstant findConstant(@Nonnull List constants, @Nonnull Double value) { + for (int i = 0; i < constants.size(); i++) { + final IConstant constant = constants.get(i); + if (!value.equals(constant.getDoubleValue())) { + continue; + } + final String name = constant.getName(); + if (name.equals(Constants.PI_INV.getName()) || name.equals(Constants.ANS)) { + continue; + } + if (!name.equals(Constants.PI.getName()) || getAngleUnits() == AngleUnit.rad) { + return constant; + } + } + return null; + } + @Nonnull public String convert(@Nonnull Double value, @Nonnull NumeralBase to) { String ungroupedValue; diff --git a/jscl/src/main/java/jscl/math/function/ExtendedConstant.java b/jscl/src/main/java/jscl/math/function/ExtendedConstant.java index 636ec5eb..01e89311 100644 --- a/jscl/src/main/java/jscl/math/function/ExtendedConstant.java +++ b/jscl/src/main/java/jscl/math/function/ExtendedConstant.java @@ -2,7 +2,6 @@ package jscl.math.function; import org.solovyev.common.JBuilder; import org.solovyev.common.math.MathEntity; -import org.solovyev.common.text.Strings; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -50,7 +49,7 @@ public class ExtendedConstant implements Comparable, IConstant final Double doubleValue = constant.getDoubleValue(); if (doubleValue == null) { final String stringValue = constant.getValue(); - if (!Strings.isEmpty(stringValue)) { + if (stringValue != null && stringValue.length() > 0) { return constant.getName() + " = " + stringValue; } else { return constant.getName(); diff --git a/jscl/src/main/java/org/solovyev/common/Converter.java b/jscl/src/main/java/org/solovyev/common/Converter.java new file mode 100644 index 00000000..f6a5a68b --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/Converter.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common; + +import javax.annotation.Nonnull; + +/** + * Interface converts one object to another + * + * @param type of object to be converted + * @param type of result object (converted object) + */ +public interface Converter { + + @Nonnull + TO convert(@Nonnull FROM from); +} + diff --git a/jscl/src/main/java/org/solovyev/common/EqualsResult.java b/jscl/src/main/java/org/solovyev/common/EqualsResult.java new file mode 100644 index 00000000..a1e9e80e --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/EqualsResult.java @@ -0,0 +1,79 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common; + +import org.solovyev.common.equals.Equalizer; + +import javax.annotation.Nullable; + +public class EqualsResult { + + public static final Integer BOTH_NULLS_CONST = 0; + + @Nullable + private final Integer result; + + @Nullable + private final T o1; + + @Nullable + private final T o2; + + @Nullable + private final Equalizer equalizer; + + EqualsResult(@Nullable T o1, @Nullable T o2, @Nullable Equalizer equalizer) { + this.equalizer = equalizer; + if (o1 == null && o2 == null) { + result = EqualsResult.BOTH_NULLS_CONST; + } else if (o1 == null) { + result = -1; + } else if (o2 == null) { + result = 1; + } else { + //both not nulls + result = null; + } + this.o1 = o1; + this.o2 = o2; + } + + @Nullable + public Integer getResult() { + return result; + } + + public boolean areBothNotNulls() { + return result == null; + } + + public boolean areBothNulls() { + return result != null && result.equals(BOTH_NULLS_CONST); + } + + public boolean areEqual() { + //noinspection ConstantConditions + boolean areSame = o1 == o2; + return areBothNulls() || areSame || (areBothNotNulls() && (equalizer == null ? o1.equals(o2) : equalizer.areEqual(o1, o2))); + } +} diff --git a/jscl/src/main/java/org/solovyev/common/HashCodeBuilder.java b/jscl/src/main/java/org/solovyev/common/HashCodeBuilder.java new file mode 100644 index 00000000..80c4fa89 --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/HashCodeBuilder.java @@ -0,0 +1,550 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common; + +import javax.annotation.Nonnull; + +/** + *

+ * Assists in implementing {@link Object#hashCode()} methods. + *

+ *

+ *

+ * This class enables a good hashCode method to be built for any class. It follows the rules laid out in + * the book Effective Java by Joshua Bloch. Writing a + * good hashCode method is actually quite difficult. This class aims to simplify the process. + *

+ *

+ *

+ * All relevant fields from the object should be included in the hashCode method. Derived fields may be + * excluded. In general, any field used in the equals method must be used in the hashCode + * method. + *

+ *

+ *

+ * To use this class write code as follows: + *

+ *

+ *

+ * public class Person {
+ *   String name;
+ *   int age;
+ *   boolean smoker;
+ *   ...
+ *
+ *   public int hashCode() {
+ * 	// you pick a hard-coded, randomly chosen, non-zero, odd number
+ * 	// ideally different for each class
+ *     return new HashCodeBuilder(17, 37).
+ *       append(name).
+ *       append(age).
+ *       append(smoker).
+ *       toHashCode();
+ *   }
+ * }
+ * 
+ *

+ *

+ * If required, the superclass hashCode() can be added using {@link #appendSuper}. + *

+ *

+ *

+ * Alternatively, there is a method that uses reflection to determine the fields to test. Because these fields are + * usually private, the method, reflectionHashCode, uses AccessibleObject.setAccessible + * to change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions + * are set up correctly. It is also slower than testing explicitly. + *

+ *

+ *

+ * A typical invocation for this method would look like: + *

+ *

+ *

+ * public int hashCode() {
+ *   return HashCodeBuilder.reflectionHashCode(this);
+ * }
+ * 
+ * + * @author Apache Software Foundation + * @author Gary Gregory + * @author Pete Gieser + * @author Sergey Solovyev + * @version $Id: HashCodeBuilder.java 907376 2010-02-07 03:43:02Z mbenson $ + * @since 1.0 + */ +public class HashCodeBuilder { + + /* + ********************************************************************** + * + * FIELDS + * + ********************************************************************** + */ + + /** + * Constant to use in building the hashCode. + */ + private final int constant; + + /** + * Running total of the hashCode. + */ + private int total = 0; + + /* + ********************************************************************** + * + * CONSTRUCTORS + * + ********************************************************************** + */ + + /** + *

+ * Uses two hard coded choices for the constants needed to build a hashCode. + *

+ */ + private HashCodeBuilder() { + constant = 37; + total = 17; + } + + /** + *

+ * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class, + * however this is not vital. + *

+ *

+ *

+ * Prime numbers are preferred, especially for the multiplier. + *

+ * + * @param initialNonZeroOddNumber a non-zero, odd number used as the initial value + * @param multiplierNonZeroOddNumber a non-zero, odd number used as the multiplier + * @throws IllegalArgumentException if the number is zero or even + */ + private HashCodeBuilder(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber) { + if (initialNonZeroOddNumber == 0) { + throw new IllegalArgumentException("HashCodeBuilder requires a non zero initial value"); + } + if (initialNonZeroOddNumber % 2 == 0) { + throw new IllegalArgumentException("HashCodeBuilder requires an odd initial value"); + } + if (multiplierNonZeroOddNumber == 0) { + throw new IllegalArgumentException("HashCodeBuilder requires a non zero multiplier"); + } + if (multiplierNonZeroOddNumber % 2 == 0) { + throw new IllegalArgumentException("HashCodeBuilder requires an odd multiplier"); + } + constant = multiplierNonZeroOddNumber; + total = initialNonZeroOddNumber; + } + + @Nonnull + public static HashCodeBuilder newInstance() { + return new HashCodeBuilder(); + } + + @Nonnull + public static HashCodeBuilder newInstance(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber) { + return new HashCodeBuilder(initialNonZeroOddNumber, multiplierNonZeroOddNumber); + } + + /* + ********************************************************************** + * + * METHODS + * + ********************************************************************** + */ + + /** + *

+ * Append a hashCode for a boolean. + *

+ *

+ * This adds constant * 1 to the hashCode and not a 1231 or + * 1237 as done in java.lang.Boolean. This is in accordance with the Effective Java + * design. + *

+ * + * @param value the boolean to add to the hashCode + * @return this + */ + public HashCodeBuilder append(boolean value) { + total = total * constant + (value ? 0 : 1); + return this; + } + + /** + *

+ * Append a hashCode for a boolean array. + *

+ * + * @param array the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(boolean[] array) { + if (array == null) { + total = total * constant; + } else { + for (boolean element : array) { + append(element); + } + } + return this; + } + + // ------------------------------------------------------------------------- + + /** + *

+ * Append a hashCode for a byte. + *

+ * + * @param value the byte to add to the hashCode + * @return this + */ + public HashCodeBuilder append(byte value) { + total = total * constant + value; + return this; + } + + // ------------------------------------------------------------------------- + + /** + *

+ * Append a hashCode for a byte array. + *

+ * + * @param array the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(byte[] array) { + if (array == null) { + total = total * constant; + } else { + for (byte element : array) { + append(element); + } + } + return this; + } + + /** + *

+ * Append a hashCode for a char. + *

+ * + * @param value the char to add to the hashCode + * @return this + */ + public HashCodeBuilder append(char value) { + total = total * constant + value; + return this; + } + + /** + *

+ * Append a hashCode for a char array. + *

+ * + * @param array the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(char[] array) { + if (array == null) { + total = total * constant; + } else { + for (char element : array) { + append(element); + } + } + return this; + } + + /** + *

+ * Append a hashCode for a double. + *

+ * + * @param value the double to add to the hashCode + * @return this + */ + public HashCodeBuilder append(double value) { + return append(Double.doubleToLongBits(value)); + } + + /** + *

+ * Append a hashCode for a double array. + *

+ * + * @param array the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(double[] array) { + if (array == null) { + total = total * constant; + } else { + for (double element : array) { + append(element); + } + } + return this; + } + + /** + *

+ * Append a hashCode for a float. + *

+ * + * @param value the float to add to the hashCode + * @return this + */ + public HashCodeBuilder append(float value) { + total = total * constant + Float.floatToIntBits(value); + return this; + } + + /** + *

+ * Append a hashCode for a float array. + *

+ * + * @param array the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(float[] array) { + if (array == null) { + total = total * constant; + } else { + for (float element : array) { + append(element); + } + } + return this; + } + + /** + *

+ * Append a hashCode for an int. + *

+ * + * @param value the int to add to the hashCode + * @return this + */ + public HashCodeBuilder append(int value) { + total = total * constant + value; + return this; + } + + /** + *

+ * Append a hashCode for an int array. + *

+ * + * @param array the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(int[] array) { + if (array == null) { + total = total * constant; + } else { + for (int element : array) { + append(element); + } + } + return this; + } + + /** + *

+ * Append a hashCode for a long. + *

+ * + * @param value the long to add to the hashCode + * @return this + */ + // NOTE: This method uses >> and not >>> as Effective Java and + // Long.hashCode do. Ideally we should switch to >>> at + // some stage. There are backwards compat issues, so + // that will have to wait for the time being. cf LANG-342. + public HashCodeBuilder append(long value) { + total = total * constant + ((int) (value ^ (value >> 32))); + return this; + } + + /** + *

+ * Append a hashCode for a long array. + *

+ * + * @param array the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(long[] array) { + if (array == null) { + total = total * constant; + } else { + for (long element : array) { + append(element); + } + } + return this; + } + + /** + *

+ * Append a hashCode for an Object. + *

+ * + * @param object the Object to add to the hashCode + * @return this + */ + public HashCodeBuilder append(Object object) { + if (object == null) { + total = total * constant; + + } else { + if (object.getClass().isArray()) { + // 'Switch' on type of array, to dispatch to the correct handler + // This handles multi dimensional arrays + if (object instanceof long[]) { + append((long[]) object); + } else if (object instanceof int[]) { + append((int[]) object); + } else if (object instanceof short[]) { + append((short[]) object); + } else if (object instanceof char[]) { + append((char[]) object); + } else if (object instanceof byte[]) { + append((byte[]) object); + } else if (object instanceof double[]) { + append((double[]) object); + } else if (object instanceof float[]) { + append((float[]) object); + } else if (object instanceof boolean[]) { + append((boolean[]) object); + } else { + // Not an array of primitives + append((Object[]) object); + } + } else { + total = total * constant + object.hashCode(); + } + } + return this; + } + + /** + *

+ * Append a hashCode for an Object array. + *

+ * + * @param array the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(Object[] array) { + if (array == null) { + total = total * constant; + } else { + for (Object element : array) { + append(element); + } + } + return this; + } + + /** + *

+ * Append a hashCode for a short. + *

+ * + * @param value the short to add to the hashCode + * @return this + */ + public HashCodeBuilder append(short value) { + total = total * constant + value; + return this; + } + + /** + *

+ * Append a hashCode for a short array. + *

+ * + * @param array the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(short[] array) { + if (array == null) { + total = total * constant; + } else { + for (short element : array) { + append(element); + } + } + return this; + } + + /** + *

+ * Adds the result of super.hashCode() to this builder. + *

+ * + * @param superHashCode the result of calling super.hashCode() + * @return this HashCodeBuilder, used to chain calls. + * @since 2.0 + */ + public HashCodeBuilder appendSuper(int superHashCode) { + total = total * constant + superHashCode; + return this; + } + + /** + *

+ * Return the computed hashCode. + *

+ * + * @return hashCode based on the fields appended + */ + public int toHashCode() { + return total; + } + + /** + *

+ * The computed hashCode from toHashCode() is returned due to the likelyhood + * of bugs in mis-calling toHashCode() and the unlikelyness of it mattering what the hashCode for + * HashCodeBuilder itself is. + * + * @return hashCode based on the fields appended + * @since 2.5 + */ + public int hashCode() { + return toHashCode(); + } + +} diff --git a/jscl/src/main/java/org/solovyev/common/JBuilder.java b/jscl/src/main/java/org/solovyev/common/JBuilder.java new file mode 100644 index 00000000..de8281b3 --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/JBuilder.java @@ -0,0 +1,31 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common; + +import javax.annotation.Nonnull; + +public interface JBuilder { + + @Nonnull + T create(); +} diff --git a/jscl/src/main/java/org/solovyev/common/JPredicate.java b/jscl/src/main/java/org/solovyev/common/JPredicate.java new file mode 100644 index 00000000..f077d270 --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/JPredicate.java @@ -0,0 +1,33 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common; + +import javax.annotation.Nullable; + +/** + * Predicate, copy of Guava's {@link com.google.common.base.Predicate} + */ +public interface JPredicate { + + boolean apply(@Nullable T t); +} diff --git a/jscl/src/main/java/org/solovyev/common/Objects.java b/jscl/src/main/java/org/solovyev/common/Objects.java new file mode 100644 index 00000000..efe235dc --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/Objects.java @@ -0,0 +1,213 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common; + +/** + * User: serso + * Date: 5/18/11 + * Time: 11:18 AM + */ + +import org.solovyev.common.equals.Equalizer; + +import javax.annotation.Nullable; +import java.util.Date; +import java.util.List; + +public class Objects { + + protected Objects() { + throw new AssertionError(); + } + + /* + ********************************************************************** + * + * EQUALS + * + ********************************************************************** + */ + + public static EqualsResult getEqualsResult(@Nullable Object o1, @Nullable Object o2) { + return new EqualsResult(o1, o2, null); + } + + public static boolean areEqual(@Nullable T o1, @Nullable T o2) { + return new EqualsResult(o1, o2, null).areEqual(); + } + + public static boolean areEqual(@Nullable T o1, @Nullable T o2, @Nullable Equalizer equalizer) { + return new EqualsResult(o1, o2, equalizer).areEqual(); + } + + /* + ********************************************************************** + * + * COMPARE + * + ********************************************************************** + */ + + public static int compare(Object value1, Object value2) { + Integer result = compareOnNullness(value1, value2); + + if (result == null) { + if (value1 instanceof Comparable && value2 instanceof Comparable) { + //noinspection unchecked + result = ((Comparable) value1).compareTo(value2); + } else { + result = 0; + } + } + return result; + } + + public static > int compare(@Nullable T l, + @Nullable T r) { + Integer result = compareOnNullness(l, r); + + if (result == null) { + assert l != null; + result = l.compareTo(r); + + } + return result; + } + + public static int compare(List list1, List list2) { + Integer result = compareOnNullness(list1, list2); + + if (result == null) { + result = list1.size() - list2.size(); + if (result == 0) { + for (int i = 0; i < list1.size(); i++) { + result = compare(list1.get(i), list2.get(i)); + if (result != 0) { + break; + } + } + } + } + + return result; + } + + public static int compare(Number value1, Number value2) { + Integer result = compareOnNullness(value1, value2); + + if (result == null) { + if (value1 instanceof Comparable && value2 instanceof Comparable) { + //noinspection unchecked + result = ((Comparable) value1).compareTo(value2); + } else { + result = 0; + } + } + + return result; + } + + public static int compare(Date value1, Date value2) { + Integer result = compareOnNullness(value1, value2); + if (result == null) { + if (value1.before(value2)) { + result = -1; + } else if (value1.after(value2)) { + result = 1; + } else { + result = 0; + } + } + return result; + } + + public static int compare(int value1, int value2) { + if (value1 > value2) { + return 1; + } else if (value1 == value2) { + return 0; + } else { + return -1; + } + } + + public static int compare(String value1, String value2, boolean ignoreCase) { + Integer result = compareOnNullness(value1, value2); + + if (result == null) { + if (ignoreCase) { + result = value1.toLowerCase().compareTo(value2.toLowerCase()); + } else { + result = value1.compareTo(value2); + } + } + + return result; + } + + public static int compare(Boolean value1, Boolean value2) { + Integer result = compareOnNullness(value1, value2); + + if (result == null) { + result = value1.compareTo(value2); + } + + return result; + } + + /** + * Method compares objects according their nullness property + * + * @param o1 first compared object + * @param o2 second compared object + * @return if both objects are nulls then 0 (they are equal), if first is null then -1, if second is null then 1, otherwise - null + */ + @Nullable + public static Integer compareOnNullness(Object o1, Object o2) { + Integer result; + + if (o1 == null && o2 == null) { + result = EqualsResult.BOTH_NULLS_CONST; + } else if (o1 == null) { + result = -1; + } else if (o2 == null) { + result = 1; + } else { + //both not nulls + result = null; + } + + return result; + } + + /** + * Method compares objects according their nullness property + * + * @param o1 first compared object + * @param o2 second compared object + * @return if both objects are nulls then 0 (they are equal), if first is null then -1, if second is null then 1, otherwise - null + */ + public static EqualsResult compareOnNullnessWithResult(Object o1, Object o2) { + return getEqualsResult(o1, o2); + } +} diff --git a/jscl/src/main/java/org/solovyev/common/SynchronizedObject.java b/jscl/src/main/java/org/solovyev/common/SynchronizedObject.java new file mode 100644 index 00000000..4fea89e6 --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/SynchronizedObject.java @@ -0,0 +1,86 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common; + +import javax.annotation.Nonnull; +import javax.annotation.concurrent.GuardedBy; + +public abstract class SynchronizedObject { + + @GuardedBy("mutex") + @Nonnull + protected final D delegate; + + @Nonnull + protected final Object mutex; + + protected SynchronizedObject(@Nonnull D delegate) { + this.delegate = delegate; + this.mutex = this; + } + + protected SynchronizedObject(@Nonnull D delegate, @Nonnull Object mutex) { + this.delegate = delegate; + this.mutex = mutex; + } + + // for manually synchronization it is allows to use mutex + @Nonnull + public Object getMutex() { + return mutex; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SynchronizedObject)) { + return false; + } + + final SynchronizedObject that = (SynchronizedObject) o; + + synchronized (mutex) { + if (!delegate.equals(that.delegate)) { + return false; + } + } + + return true; + } + + @Override + public int hashCode() { + synchronized (mutex) { + return delegate.hashCode(); + } + } + + @Override + public String toString() { + synchronized (mutex) { + return delegate.toString(); + } + } +} diff --git a/jscl/src/main/java/org/solovyev/common/collections/SortedList.java b/jscl/src/main/java/org/solovyev/common/collections/SortedList.java new file mode 100644 index 00000000..c22b800e --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/collections/SortedList.java @@ -0,0 +1,262 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common.collections; + +import javax.annotation.Nonnull; +import java.util.*; + +public class SortedList implements List { + + @Nonnull + private final Comparator comparator; + @Nonnull + private List list = new ArrayList(); + + private SortedList(@Nonnull Comparator comparator) { + this.comparator = comparator; + } + + private SortedList(@Nonnull List list, @Nonnull Comparator comparator) { + this.list = list; + this.comparator = comparator; + } + + @Nonnull + public static SortedList newInstance(@Nonnull Comparator comparator) { + return new SortedList(comparator); + } + + @Nonnull + public static SortedList newInstance(@Nonnull List list, @Nonnull Comparator comparator) { + return new SortedList(list, comparator); + } + + @Override + public int size() { + return list.size(); + } + + @Override + public boolean isEmpty() { + return list.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return list.contains(o); + } + + @Nonnull + @Override + public Iterator iterator() { + final Iterator it = list.iterator(); + return new Iterator() { + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public T next() { + return it.next(); + } + + @Override + public void remove() { + it.remove(); + // todo serso: think + sort(); + } + }; + } + + @Nonnull + @Override + public Object[] toArray() { + return list.toArray(); + } + + @Nonnull + @Override + public T[] toArray(@Nonnull T[] a) { + return list.toArray(a); + } + + @Override + public boolean add(T t) { + boolean result = list.add(t); + sort(); + return result; + } + + @Override + public boolean remove(Object o) { + boolean result = list.remove(o); + sort(); + return result; + } + + @Override + public boolean containsAll(@Nonnull Collection c) { + return this.list.containsAll(c); + } + + @Override + public boolean addAll(@Nonnull Collection c) { + boolean result = this.list.addAll(c); + sort(); + return result; + } + + @Override + public boolean addAll(int index, @Nonnull Collection c) { + boolean result = this.list.addAll(index, c); + sort(); + return result; + } + + @Override + public boolean removeAll(@Nonnull Collection c) { + boolean result = this.list.removeAll(c); + sort(); + return result; + } + + @Override + public boolean retainAll(@Nonnull Collection c) { + boolean result = this.list.retainAll(c); + sort(); + return result; + } + + @Override + public void clear() { + this.list.clear(); + } + + @Override + public T get(int index) { + return this.list.get(index); + } + + @Override + public T set(int index, T element) { + T result = this.list.set(index, element); + sort(); + return result; + } + + @Override + public void add(int index, T element) { + this.list.add(index, element); + sort(); + } + + @Override + public T remove(int index) { + T result = this.list.remove(index); + sort(); + return result; + } + + @Override + public int indexOf(Object o) { + return this.list.indexOf(o); + } + + @Override + public int lastIndexOf(Object o) { + return this.list.lastIndexOf(o); + } + + @Nonnull + @Override + public ListIterator listIterator() { + return listIterator(0); + } + + @Nonnull + @Override + public ListIterator listIterator(int index) { + final ListIterator it = this.list.listIterator(index); + return new ListIterator() { + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public T next() { + return it.next(); + } + + @Override + public boolean hasPrevious() { + return it.hasPrevious(); + } + + @Override + public T previous() { + return it.previous(); + } + + @Override + public int nextIndex() { + return it.nextIndex(); + } + + @Override + public int previousIndex() { + return it.previousIndex(); + } + + @Override + public void remove() { + it.remove(); + sort(); + } + + @Override + public void set(T t) { + it.set(t); + sort(); + } + + @Override + public void add(T t) { + it.add(t); + sort(); + } + }; + } + + public void sort() { + Collections.sort(list, comparator); + } + + @Nonnull + @Override + public List subList(int fromIndex, int toIndex) { + return this.list.subList(fromIndex, toIndex); + } +} diff --git a/jscl/src/main/java/org/solovyev/common/equals/CollectionEqualizer.java b/jscl/src/main/java/org/solovyev/common/equals/CollectionEqualizer.java new file mode 100644 index 00000000..3012de9b --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/equals/CollectionEqualizer.java @@ -0,0 +1,67 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common.equals; + +import org.solovyev.common.Objects; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collection; + +public class CollectionEqualizer implements Equalizer> { + + @Nullable + protected final Equalizer nestedEqualizer; + + public CollectionEqualizer(@Nullable Equalizer nestedEqualizer) { + this.nestedEqualizer = nestedEqualizer; + } + + @Override + public boolean areEqual(@Nonnull Collection first, @Nonnull Collection second) { + boolean result = false; + + if (first.size() == second.size()) { + result = true; + + for (T el1 : first) { + boolean found = false; + + for (T el2 : second) { + if (Objects.areEqual(el1, el2, nestedEqualizer)) { + found = true; + break; + } + } + + if (!found) { + result = false; + break; + } + } + } + + return result; + } + +} diff --git a/jscl/src/main/java/org/solovyev/common/equals/Equalizer.java b/jscl/src/main/java/org/solovyev/common/equals/Equalizer.java new file mode 100644 index 00000000..1415592f --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/equals/Equalizer.java @@ -0,0 +1,30 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common.equals; + +import javax.annotation.Nonnull; + +public interface Equalizer { + + boolean areEqual(@Nonnull T first, @Nonnull T second); +} \ No newline at end of file diff --git a/jscl/src/main/java/org/solovyev/common/equals/ListEqualizer.java b/jscl/src/main/java/org/solovyev/common/equals/ListEqualizer.java new file mode 100644 index 00000000..d462a4b2 --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/equals/ListEqualizer.java @@ -0,0 +1,87 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common.equals; + +import org.solovyev.common.Objects; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +public class ListEqualizer implements Equalizer> { + + @Nonnull + private static final Equalizer> instanceWithOrder = new ListEqualizer<>(true, null); + + @Nonnull + private static final Equalizer> instanceWithoutOrder = new ListEqualizer<>(false, null); + + private final boolean checkOrder; + + @Nullable + protected final Equalizer nestedEqualizer; + + private ListEqualizer(boolean checkOrder, @Nullable Equalizer nestedEqualizer) { + this.checkOrder = checkOrder; + this.nestedEqualizer = nestedEqualizer; + } + + @Nonnull + public static ListEqualizer newWithNestedEqualizer(boolean checkOrder, @Nullable Equalizer nestedEqualizer) { + return new ListEqualizer<>(checkOrder, nestedEqualizer); + } + + @Nonnull + public static ListEqualizer newWithNaturalEquals(boolean checkOrder) { + if (checkOrder) { + return (ListEqualizer) instanceWithOrder; + } else { + return (ListEqualizer) instanceWithoutOrder; + } + } + + @Override + public boolean areEqual(@Nonnull List first, @Nonnull List second) { + boolean result = false; + + if (first.size() == second.size()) { + if (checkOrder) { + result = true; + for (int i = 0; i < first.size(); i++) { + final T el1 = first.get(i); + final T el2 = second.get(i); + + if (!Objects.areEqual(el1, el2, nestedEqualizer)) { + result = false; + break; + } + + } + } else { + result = Objects.areEqual(first, second, new CollectionEqualizer<>(nestedEqualizer)); + } + } + + return result; + } +} diff --git a/jscl/src/main/java/org/solovyev/common/math/AbstractMathRegistry.java b/jscl/src/main/java/org/solovyev/common/math/AbstractMathRegistry.java index b5fe8bd8..cd996fae 100644 --- a/jscl/src/main/java/org/solovyev/common/math/AbstractMathRegistry.java +++ b/jscl/src/main/java/org/solovyev/common/math/AbstractMathRegistry.java @@ -23,7 +23,6 @@ package org.solovyev.common.math; import org.solovyev.common.JBuilder; -import org.solovyev.common.collections.Collections; import org.solovyev.common.collections.SortedList; import javax.annotation.Nonnull; @@ -64,6 +63,22 @@ public abstract class AbstractMathRegistry implements Math return result; } + @Nullable + private static E removeByName(@Nonnull List entities, @Nonnull String name) { + for (int i = 0; i < entities.size(); i++) { + final E entity = entities.get(i); + if (entity.getName().equals(name)) { + entities.remove(i); + return entity; + } + } + return null; + } + + private static boolean areEqual(@Nullable Object l, @Nullable Object r) { + return l != null ? l.equals(r) : r == null; + } + @Nonnull public List getEntities() { synchronized (this) { @@ -136,8 +151,8 @@ public abstract class AbstractMathRegistry implements Math public void remove(@Nonnull T entity) { synchronized (this) { if (!entity.isSystem()) { - final T removed = Collections.removeFirst(this.entities, new MathEntity.Finder(entity.getName())); - if(removed != null) { + final T removed = removeByName(entities, entity.getName()); + if (removed != null) { this.entityNames.clear(); } } @@ -174,10 +189,6 @@ public abstract class AbstractMathRegistry implements Math return null; } - private static boolean areEqual(@Nullable Object l, @Nullable Object r) { - return l != null ? l.equals(r) : r == null; - } - public T getById(@Nonnull final Integer id) { synchronized (this) { for (T entity : entities) { diff --git a/jscl/src/main/java/org/solovyev/common/math/MathEntity.java b/jscl/src/main/java/org/solovyev/common/math/MathEntity.java index 9625b064..a7ab8754 100644 --- a/jscl/src/main/java/org/solovyev/common/math/MathEntity.java +++ b/jscl/src/main/java/org/solovyev/common/math/MathEntity.java @@ -22,10 +22,8 @@ package org.solovyev.common.math; -import org.solovyev.common.JPredicate; import javax.annotation.Nonnull; -import javax.annotation.Nullable; public interface MathEntity { @@ -42,18 +40,4 @@ public interface MathEntity { boolean isIdDefined(); void copy(@Nonnull MathEntity that); - - class Finder implements JPredicate { - - @Nonnull - private final String name; - - public Finder(@Nonnull String name) { - this.name = name; - } - - public boolean apply(@Nullable T entity) { - return entity != null && name.equals(entity.getName()); - } - } } diff --git a/jscl/src/main/java/org/solovyev/common/msg/AbstractMessage.java b/jscl/src/main/java/org/solovyev/common/msg/AbstractMessage.java new file mode 100644 index 00000000..c7d72041 --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/msg/AbstractMessage.java @@ -0,0 +1,134 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common.msg; + +import org.solovyev.common.HashCodeBuilder; +import org.solovyev.common.Objects; +import org.solovyev.common.equals.ListEqualizer; +import org.solovyev.common.text.Strings; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; + +public abstract class AbstractMessage implements Message { + + @Nonnull + private final String messageCode; + + @Nonnull + private final List parameters; + + @Nonnull + private final MessageLevel messageLevel; + + protected AbstractMessage(@Nonnull String messageCode, @Nonnull MessageLevel messageType, @Nullable Object... parameters) { + this(messageCode, messageType, parameters == null ? Collections.emptyList() : Arrays.asList(parameters)); + } + + protected AbstractMessage(@Nonnull String messageCode, @Nonnull MessageLevel messageType, @Nonnull List parameters) { + this.messageCode = messageCode; + this.parameters = new ArrayList<>(parameters); + this.messageLevel = messageType; + } + + @Override + @Nonnull + public String getMessageCode() { + return this.messageCode; + } + + @Nonnull + @Override + public List getParameters() { + return java.util.Collections.unmodifiableList(this.parameters); + } + + @Nonnull + @Override + public MessageLevel getMessageLevel() { + return this.messageLevel; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final AbstractMessage that = (AbstractMessage) o; + + if (!Objects.areEqual(parameters, that.parameters, ListEqualizer.newWithNaturalEquals(true))) { + return false; + } + if (!messageCode.equals(that.messageCode)) { + return false; + } + if (!messageLevel.equals(that.messageLevel)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + final HashCodeBuilder hcb = HashCodeBuilder.newInstance(); + + hcb.append(messageCode); + hcb.append(messageLevel); + hcb.append(parameters); + + return hcb.toHashCode(); + } + + /** + * Method converts message to string setting passed message parameters and translating some of them. + * + * @param locale language to which parameters should be translated (if possible) + * @return message as string with properly translated and set parameters + */ + @Nonnull + public String getLocalizedMessage(@Nonnull Locale locale) { + String result = null; + + final String messagePattern = getMessagePattern(locale); + if (!Strings.isEmpty(messagePattern)) { + result = Messages.prepareMessage(locale, messagePattern, parameters); + } + + return Strings.getNotEmpty(result, messageLevel.getName() + ": message code = " + messageCode); + } + + @Nonnull + @Override + public String getLocalizedMessage() { + return this.getLocalizedMessage(Locale.getDefault()); + } + + @Nullable + protected abstract String getMessagePattern(@Nonnull Locale locale); +} diff --git a/jscl/src/main/java/org/solovyev/common/msg/ListMessageRegistry.java b/jscl/src/main/java/org/solovyev/common/msg/ListMessageRegistry.java new file mode 100644 index 00000000..e1bd556d --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/msg/ListMessageRegistry.java @@ -0,0 +1,51 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common.msg; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; + +public class ListMessageRegistry implements MessageRegistry { + + @Nonnull + private final List messages = new ArrayList(); + + @Override + public void addMessage(@Nonnull Message message) { + if (!messages.contains(message)) { + messages.add(message); + } + } + + @Nonnull + @Override + public Message getMessage() { + return this.messages.remove(0); + } + + @Override + public boolean hasMessage() { + return !this.messages.isEmpty(); + } +} diff --git a/jscl/src/main/java/org/solovyev/common/msg/Message.java b/jscl/src/main/java/org/solovyev/common/msg/Message.java new file mode 100644 index 00000000..2e2ada7a --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/msg/Message.java @@ -0,0 +1,67 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common.msg; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.Locale; + +/** + * Interface represents translatable user message. + * Implementation of this class will likely contains + * some logic for translation message according to + * it's message code and list of parameters. + */ +public interface Message { + + /** + * @return message code + */ + @Nonnull + public String getMessageCode(); + + /** + * @return list of message parameters + */ + @Nonnull + public List getParameters(); + + /** + * @return message level + */ + @Nonnull + public MessageLevel getMessageLevel(); + + /** + * @param locale locate to which current message should be translated + * @return message string translated to specified locale + */ + @Nonnull + public String getLocalizedMessage(@Nonnull Locale locale); + + /** + * @return message string translated to deault locale (Locale.getDefault()) + */ + @Nonnull + public String getLocalizedMessage(); +} diff --git a/jscl/src/main/java/org/solovyev/common/msg/MessageLevel.java b/jscl/src/main/java/org/solovyev/common/msg/MessageLevel.java new file mode 100644 index 00000000..473ed070 --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/msg/MessageLevel.java @@ -0,0 +1,32 @@ +package org.solovyev.common.msg; + +import javax.annotation.Nonnull; + +/** + * See {@link MessageType} as default implementation of this class + */ +public interface MessageLevel { + + public static final int INFO_LEVEL = 100; + public static final int WARNING_LEVEL = 500; + public static final int ERROR_LEVEL = 1000; + + /** + * Position of current message level in some message level hierarchy. + * By default, one can use {@link MessageType} implementation which uses next levels: + * 100 500 1000 level + * --------|-----------|--------------|-------------> + * Info Warning Error + * + * @return int message level + */ + int getMessageLevel(); + + /** + * Some string id for level (might be used in logs) + * + * @return string level identifier + */ + @Nonnull + String getName(); +} diff --git a/jscl/src/main/java/org/solovyev/common/msg/MessageRegistry.java b/jscl/src/main/java/org/solovyev/common/msg/MessageRegistry.java new file mode 100644 index 00000000..1cdd8b63 --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/msg/MessageRegistry.java @@ -0,0 +1,54 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common.msg; + +import javax.annotation.Nonnull; + +/** + * Container for messages + */ +public interface MessageRegistry { + + /** + * Adds message to the registry. + * Note: according to the implementation this method doesn't guarantee that new message will be added + * in underlying container (e.g. if such message already exists) + * + * @param message message to be added + */ + void addMessage(@Nonnull Message message); + + /** + * @return true if there is any message available in the registry + */ + boolean hasMessage(); + + /** + * Method returns message from registry and removes it from underlying container + * Note: this method must be called after {@link MessageRegistry#hasMessage()} + * + * @return message from the registry + */ + @Nonnull + Message getMessage(); +} diff --git a/jscl/src/main/java/org/solovyev/common/msg/MessageType.java b/jscl/src/main/java/org/solovyev/common/msg/MessageType.java new file mode 100644 index 00000000..dd11027a --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/msg/MessageType.java @@ -0,0 +1,65 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common.msg; + +import javax.annotation.Nonnull; + +public enum MessageType implements MessageLevel { + + error(ERROR_LEVEL, "ERROR"), + + warning(WARNING_LEVEL, "WARNING"), + + info(INFO_LEVEL, "INFO"); + + private final int messageLevel; + + @Nonnull + private final String stringValue; + + MessageType(int messageLevel, @Nonnull String stringValue) { + this.messageLevel = messageLevel; + this.stringValue = stringValue; + } + + @Nonnull + public static MessageType getLowestMessageType() { + return info; + } + + @Nonnull + public String getStringValue() { + return stringValue; + } + + @Override + public int getMessageLevel() { + return messageLevel; + } + + @Nonnull + @Override + public String getName() { + return stringValue; + } +} diff --git a/jscl/src/main/java/org/solovyev/common/msg/Messages.java b/jscl/src/main/java/org/solovyev/common/msg/Messages.java new file mode 100644 index 00000000..27f08506 --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/msg/Messages.java @@ -0,0 +1,86 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common.msg; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.text.MessageFormat; +import java.util.List; +import java.util.Locale; + +public final class Messages { + + private Messages() { + throw new AssertionError(); + } + + @Nonnull + public static MessageRegistry synchronizedMessageRegistry(@Nonnull MessageRegistry messageRegistry) { + return SynchronizedMessageRegistry.wrap(messageRegistry); + } + + /** + * @param locale locale for which default formatting will be applied + * @param messagePattern message pattern which will be used for {@link MessageFormat} + * @param parameters message parameters which will be used for {@link MessageFormat} + * @return formatted message string according to default locale formatting, nested messages are processed properly + * (for each message from parameter method {@link Message#getLocalizedMessage(Locale)} is called) + */ + @Nonnull + public static String prepareMessage(@Nonnull Locale locale, @Nonnull String messagePattern, @Nonnull List parameters) { + String result; + + if (parameters.isEmpty()) { + result = messagePattern; + } else { + final MessageFormat format = new MessageFormat(messagePattern); + + format.setLocale(locale); + format.applyPattern(messagePattern); + + result = format.format(prepareParameters(parameters, locale)); + } + + return result; + } + + @Nonnull + private static Object[] prepareParameters(@Nonnull List parameters, @Nonnull Locale locale) { + final Object[] result = new Object[parameters.size()]; + + for (int i = 0; i < parameters.size(); i++) { + result[i] = substituteParameter(parameters.get(i), locale); + } + + return result; + } + + @Nullable + private static Object substituteParameter(@Nullable Object object, @Nonnull Locale locale) { + if (object instanceof Message) { + return ((Message) object).getLocalizedMessage(locale); + } else { + return object; + } + } +} diff --git a/jscl/src/main/java/org/solovyev/common/msg/SynchronizedMessageRegistry.java b/jscl/src/main/java/org/solovyev/common/msg/SynchronizedMessageRegistry.java new file mode 100644 index 00000000..47d85489 --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/msg/SynchronizedMessageRegistry.java @@ -0,0 +1,70 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common.msg; + +import org.solovyev.common.SynchronizedObject; + +import javax.annotation.Nonnull; + +class SynchronizedMessageRegistry extends SynchronizedObject implements MessageRegistry { + + private SynchronizedMessageRegistry(@Nonnull MessageRegistry delegate) { + super(delegate); + } + + private SynchronizedMessageRegistry(@Nonnull MessageRegistry delegate, @Nonnull Object mutex) { + super(delegate, mutex); + } + + @Nonnull + public static MessageRegistry wrap(@Nonnull MessageRegistry delegate) { + return new SynchronizedMessageRegistry(delegate); + } + + @Nonnull + public static MessageRegistry wrap(@Nonnull MessageRegistry delegate, @Nonnull Object mutex) { + return new SynchronizedMessageRegistry(delegate, mutex); + } + + @Override + public void addMessage(@Nonnull Message message) { + synchronized (this.mutex) { + delegate.addMessage(message); + } + } + + @Override + public boolean hasMessage() { + synchronized (this.mutex) { + return delegate.hasMessage(); + } + } + + @Nonnull + @Override + public Message getMessage() { + synchronized (this.mutex) { + return delegate.getMessage(); + } + } +} diff --git a/jscl/src/main/java/org/solovyev/common/msg/Utf8Control.java b/jscl/src/main/java/org/solovyev/common/msg/Utf8Control.java new file mode 100644 index 00000000..d7c85c71 --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/msg/Utf8Control.java @@ -0,0 +1,60 @@ +package org.solovyev.common.msg; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; +import java.util.Locale; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; + +class Utf8Control extends ResourceBundle.Control { + + @Nonnull + private static final ResourceBundle.Control instance = new Utf8Control(); + + @Nonnull + public static ResourceBundle.Control getInstance() { + return instance; + } + + private Utf8Control() { + } + + public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) throws IllegalAccessException, InstantiationException, IOException { + final ResourceBundle result; + + // The below is a copy of the default implementation. + final String bundleName = toBundleName(baseName, locale); + final String resourceName = toResourceName(bundleName, "properties"); + + InputStream stream = null; + if (reload) { + final URL url = loader.getResource(resourceName); + if (url != null) { + final URLConnection connection = url.openConnection(); + if (connection != null) { + connection.setUseCaches(false); + stream = connection.getInputStream(); + } + } + } else { + stream = loader.getResourceAsStream(resourceName); + } + + if (stream != null) { + try { + // Only this line is changed to make it to read properties files as UTF-8. + result = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8")); + } finally { + stream.close(); + } + } else { + result = null; + } + + return result; + } +} diff --git a/jscl/src/main/java/org/solovyev/common/search/StartsWithFinder.java b/jscl/src/main/java/org/solovyev/common/search/StartsWithFinder.java new file mode 100644 index 00000000..0c52fd45 --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/search/StartsWithFinder.java @@ -0,0 +1,60 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common.search; + +import org.solovyev.common.JPredicate; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class StartsWithFinder implements JPredicate { + + private int i; + + @Nonnull + private final String targetString; + + private StartsWithFinder(@Nonnull String targetString, int i) { + this.targetString = targetString; + this.i = i; + } + + @Nonnull + public static StartsWithFinder newFrom(@Nonnull String targetString, int i) { + return new StartsWithFinder(targetString, i); + } + + @Nonnull + public static StartsWithFinder newInstance(@Nonnull String targetString) { + return newFrom(targetString, 0); + } + + @Override + public boolean apply(@Nullable String s) { + return s != null && targetString.startsWith(s, i); + } + + public void setI(int i) { + this.i = i; + } +} diff --git a/jscl/src/main/java/org/solovyev/common/text/Strings.java b/jscl/src/main/java/org/solovyev/common/text/Strings.java new file mode 100644 index 00000000..f75e18fd --- /dev/null +++ b/jscl/src/main/java/org/solovyev/common/text/Strings.java @@ -0,0 +1,83 @@ +/* + * Copyright 2013 serso aka se.solovyev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * --------------------------------------------------------------------- + * Contact details + * + * Email: se.solovyev@gmail.com + * Site: http://se.solovyev.org + */ + +package org.solovyev.common.text; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Date; +import java.util.Random; + +public class Strings { + + public static final String LINE_SEPARATOR = System.getProperty("line.separator"); + public static final Character[] EMPTY_CHARACTER_OBJECT_ARRAY = new Character[0]; + // random variable: must be synchronized in usage + private static final Random RANDOM = new Random(new Date().getTime()); + + // not intended for instantiation + protected Strings() { + throw new AssertionError(); + } + + public static boolean isEmpty(@Nullable CharSequence s) { + return s == null || s.length() == 0; + } + + @Nonnull + public static String getNotEmpty(@Nullable CharSequence s, @Nonnull String defaultValue) { + return isEmpty(s) ? defaultValue : s.toString(); + } + + @Nonnull + public static Character[] toObjects(char[] array) { + if (array == null || array.length == 0) { + return EMPTY_CHARACTER_OBJECT_ARRAY; + } + + final Character[] result = new Character[array.length]; + + for (int i = 0; i < array.length; i++) { + result[i] = array[i]; + } + return result; + } + + @Nonnull + public static String generateRandomString(int length) { + + final StringBuilder result = new StringBuilder(length); + for (int i = 0; i < length; i++) { + final char ch; + + synchronized (RANDOM) { + // 'A' = 65 + ch = (char) (RANDOM.nextInt(52) + 65); + } + + result.append(ch); + } + + return result.toString(); + } + +}