I've an enum like this:
public enum ChartType
{
TABLE(0, false), BAR(1, false), COLUMN(3, false)
private int type;
private boolean stacked;
ChartType(int type, boolean stacked)
{
this.type = type;
this.stacked = stacked;
}
public int getType()
{
return type;
}
public boolean isStacked()
{
return this.stacked;
}
}
I get a charttype (int values like 0,1,3) from the request and want the matching input
Something along these lines. Not sure if syntax is 100 percent, but it demos the idea.
public ChartType getChartypeForValue(int value)
for(ChartType type : ChartType.values()){
if(type.getType() == value){
return type;
}
}
return null;
}
You can add map in your enum to store relationship between type and specific enum value. You can fill it once in static block after all values will be created like:
private static Map<Integer, ChartType> typeMap = new HashMap<>();
static{
for (ChartType chartType: values()){
typeMap.put(chartType.type, chartType);
}
}
then you can add method which will use this map to get value you want, or null if there isn't any
public static ChartType getByType(int type) {
return typeMap.get(type);
}
You can use it like
ChartType element = ChartType.getByType(1);
You should create a static method to retrieve a type by number. With just a few charts like this, it's simplest to do that by just running through all the options. For larger enums, you could create a map for quick lookup. The only thing to watch for here is that any static initializers aren't run until after the values have been created.
Simple approach:
public static ChartType fromType(int type) {
// Or for (ChartType chart : ChartType.getValues())
for (ChartType chart : EnumSet.allOf(ChartType.class)) {
if (chart.type == type) {
return chart;
}
}
return null; // Or throw an exception
}
If you use java 8, use stream() and filter()
int value =2;
Optional<ChartType> chartType = Arrays.asList(ChartType.values()).stream().
filter(c -> c.type == value).findFirst();
if(chartType.isPresent()){
ChartType chartType =chartType.get();
//
}
Define new method:
public ChartType valueOf(int id) {
switch(id) {
case 1:
return TABLE;
case 2:
return BAR;
case 3:
return COLUMN;
}
return null;
}
Example:
ChartType.valueOf(1) // TABLE
Related
I'm writing a function to check multiple conditions in an array, if they are all true then return true.
For example:
public class Attribute {
private final String key;
private final String value;
//...
}
boolean canContactDogOwner(List<Attribute> attributes) {
boolean hasDog = false;
boolean isSubscribed = false;
boolean isOkToCall = false;
for (var attribute : attributes) {
if (attribute.key().equals("dogName")) {
hasDog = true;
} else if (attribute.key().equals("isSubscribed") && attribute.value().equals("Y")) {
isSubscribed = true;
} else if (attribute.key().equals("okToCall") && attribute.value().equals("Y")) {
isOkToCall = true;
}
// 1.
}
return hasDog && isSubscribed && isOkToCall;
}
void foo() {
List<Attribute> attributes = new ArrayList<>();
attributes.add(new Attribute("isSubscribed", "Y"));
attributes.add(new Attribute("okToCall", "Y"));
attributes.add(new Attribute("mobile", "12345678"));
attributes.add(new Attribute("landline", "1346346"));
attributes.add(new Attribute("email", "white#email.com"));
attributes.add(new Attribute("dogName", "Alex"));
boolean canContact = canContactDogOwner(attributes);
}
Two questions:
When all conditions are meet, the loop can be break, but if I add a check there, we would be checking every step in the loop, which doesn't look good. Any suggestions?
Is there a better / concise way to do it?
Like following?
boolean canContactDogOwner(List<Attribute> attributes) {
return attributes.stream().allMatch(A,B,C);
}
You can modify method canContactDogOwner to be like this,
boolean canContactDogOwner(List<Attribute> attributes) {
List<Attribute> conditions = new ArrayList<>();
conditions.add(new Attribute("isSubscribed", "Y"));
conditions.add(new Attribute("okToCall", "Y"));
return attributes.containsAll(conditions) &&
attributes.stream().anyMatch((attribute -> attribute.key.equals("dogName")));
}
A working and cleaner approach (IMO) will be to use some abstract data type like Map in this case..
static boolean canContactDogOwner(List<Attribute> attributes){
Map<String, String> attributeMap = new HashMap<>(); // empty map
attributes.forEach(attr -> attributeMap.put(attr.getKey(), attr.getValue())); // populate map
return attributeMap.containsKey("dogName") &&
"Y".equals(attributeMap.get("isSubscribed")) &&
"Y".equals(attributeMap.get("okToCall")); // Constant-String-first on equals check to avoid nullPointerExc with less code, yet clean
}
The code above with the comment is self-explanatory, so not adding details of the code.
But it is worth mentioning that
the complexity is still O(n) like other solutions here, n - number of elements (attribute objects)
flexibility to add or remove more conditions in the return statement
map as a chosen data-type and <Constant>.equals check avoids key validation and nullPointerException respectively.
If you are fascinated with Java-Streams, you can modify the code like this too..
static boolean canContactDogOwner(List<Attribute> attributes){
Map<String, String> attributeMap = attributes.stream()
.collect(Collectors.toMap(Attribute::getKey, Attribute::getValue));
return attributeMap.containsKey("dogName") &&
"Y".equals(attributeMap.get("isSubscribed")) &&
"Y".equals(attributeMap.get("okToCall"));
}
You could check if all condition is meet only when you set a value to true,
it will happen only 3 time.
And more concise way, probably with stream().anyMatch() but i'm not sure it will be more readable
Stream and allMatch(Predicate predicate) is a better way to do it in my opinion, but keep in mind that allMatch() take a Predicate as an argument, so you need to provide one.
I would suggest you encapsulate the attributes and create a class
something like Owner.
public class Owner {
private boolean isSubscribed;
private boolean okToCall;
private String mobile;
private String landline;
private String email;
private Optional<String> dogName;
public Owner(boolean isSubscribed, boolean okToCall, String mobile, String landline, String email, Optional<String> dogName) {
this.isSubscribed = isSubscribed;
this.okToCall = okToCall;
this.mobile = mobile;
this.landline = landline;
this.email = email;
this.dogName = dogName;
}
public boolean canContact() {
return this.isSubscribed && this.okToCall;
}
public boolean hasDog() {
return dogName.isPresent();
}
}
This way you do not have to deal with the if loops, the Owner object will say if they have a dog and can be contacted, etc.
public static void main(String[] args) {
Owner owner = new Owner(true, true, "12345678", "1346346", "white#email.com", Optional.of("Alex"));
boolean canContact = owner.hasDog() && owner.canContact();
}
I think you can have two lists of your conditions and attributes and then check whether attributes contain all condition or not.
public static Boolean allConditionsExist(List<String> attributes, List<String> conditions) {
return attributes.containsAll(conditions);
}
To convert your conditions and attributes to a list you can do something like this.
List<String> conditions = Arrays.asList("dogName","isSubscribed", "okToCall"); // add all your conditions
and
List<String> attributeKeys = attributes.stream().map(Attribute::getKey).collect(Collectors.toList());
Then call
allConditionExist(attributeKeys, conditions);
Assuming that every attribute is present only once, you could write
boolean canContactDogOwner(List<Attribute> attributes) {
int matches = 0;
for (var attribute : attributes) {
if (attribute.key().equals("dogName")) ||
attribute.key().equals("isSubscribed") && attribute.value().equals("Y") ||
attribute.key().equals("okToCall") && attribute.value().equals("Y"))
{
matches++;
if (matches >= 3) {
return true;
}
}
}
return false;
}
For the stream way you could write a Collector, constructed with a list of Predicates and returning a boolean. Wouldn't be the fastest...
Something like:
public class AllMatch<T> implements Collector<T, Set<Predicate<T>>, Boolean>
{
private Set<Predicate<T>> filter;
public AllMatch(Predicate<T>... filter)
{
super();
this.filter = new HashSet(Arrays.asList(filter));
}
#Override
public Supplier<Set<Predicate<T>>> supplier()
{
return () -> new HashSet<>();
}
#Override
public BinaryOperator<Set<Predicate<T>>> combiner()
{
return this::combiner;
}
#Override
public Set<Characteristics> characteristics()
{
return Stream.of(Characteristics.UNORDERED).collect(Collectors.toCollection(HashSet::new));
}
public Set<Predicate<T>> combiner(Set<Predicate<T>> left, Set<Predicate<T>> right)
{
left.addAll(right);
return left;
}
public Set<Predicate<T>> accumulator(Set<Predicate<T>> acc, T t)
{
filter.stream().filter(f -> f.test(t)).forEach(f ->
{
acc.add(f);
});
return acc;
}
#Override
public Function<Set<Predicate<T>>, Boolean> finisher()
{
return (s) -> s.equals(filter);
}
#Override
public BiConsumer<Set<Predicate<T>>, T> accumulator()
{
return this::accumulator;
}
public static void main(String[] args) {
Integer[] numbers = {1,2,3,4,5,6,7,8};
System.out.println(Arrays.stream(numbers).collect(new AllMatch<Integer>((i)-> i.equals(5),(i)-> i.equals(6))));
System.out.println(Arrays.stream(numbers).collect(new AllMatch<Integer>((i)-> i.equals(5),(i)-> i.equals(9))));
}
}
I have a method with which I would like to check if, in my model, enum of some type exists.
If it exists I want to return true an if not I would like to return false with error message.
I have model that looks like this:
#Data
public class DataResponse {
public final String userId;
public final List<Model> modelList;
}
Model.java
#Data
public class Model {
public final String modelId;
public final ModelType type;
public final ModelStatus status;
}
ModelType.java
public enum ModelType {
Fast,
Slow;
}
ModelStatus.java
public enum ModelStatus {
CREATED,
FAILED
}
Now I would like to check using strems if my model contains type "Fast" and if contains status "CREATED" if yes return true if not return false with error message.
So far I have this:
public Boolean isModelFastAndSuccess(String modelId){
Optional<DataResponse> modelList = dataService.getModelStatus(modelId);
userProofList.stream().map(ml -> ml.modelList)
.map(models -> {
if()
})
.collect(Collectors.toList());
}
I am not sure how to finish it.
Any advice appreciated.
The approach to implementation of isModelFastAndSuccess could be as follows assuming that dataService::getModelStatus returns Optional<DataResponse>:
public Boolean areAllModelsFastAndSuccess(String modelId) {
return dataService.getModelStatus(modelId) // Optional<DataResponse>
.map(DataResponse::getModelList) // Optional<List<Model>>
.orElse(Collections.emptyList()) // List<Model>
.stream() // Stream<Model>
.allMatch(model -> model.getType() == ModelType.Fast
&& model.getStatus() == ModelStatus.CREATED
);
}
Also, a method may be implemented to return true if there is at least one Fast / Created model:
public Boolean isAnyModelFastAndSuccess(String modelId) {
return dataService.getModelStatus(modelId) // Optional<DataResponse>
.map(DataResponse::getModelList) // Optional<List<Model>>
.orElse(Collections.emptyList()) // List<Model>
.stream() // Stream<Model>
.anyMatch(model -> model.getType() == ModelType.Fast
&& model.getStatus() == ModelStatus.CREATED
);
}
I am trying to add a ComboBox into the TableView but for some reason I cannot make the conversion. Behind the scenes, I want to make the conversation if the value is
0 then it should display 'free' if the value is 1 then it will say 'taken' in the ComboBox, and once the user changes the value in the ComboBox
it will save its integer value.
I am not sure how to add the converter and it gives the following error at event.getNewValue():
cant convert int to string.
Any help where I am doing wrong?
private final IntegerProperty mode;
public int getMode() {
return mode.get();
}
public void setMode(int mode) {
this.mode.set(mode);
}
public IntegerProperty modeProperty() {
return mode;
}
Set<String> modeList = new HashSet<>();
modeList.add("Free");
modeList.add("Taken");
var converter=modeConverter();
TableColumn<Review, String> modeCombo = new TableColumn("Mode");
modeCombo.setCellValueFactory(new PropertyValueFactory("mode"));
modeCombo.setCellFactory(ComboBoxTableCell.forTableColumn(converter); //How to apply the converter.
modeCombo.setCellFactory(ComboBoxTableCell.forTableColumn(FXCollections.observableList(modeList))));
modeCombo.setOnEditCommit(event -> {
mode.setOperationMode(event.getNewValue()); //Method cannot be applied java.lang.String. But I already make the conversatin via modeConverter
});
...
private StringConverter modeConverter() {
return new StringConverter<Integer>() {
#Override
public String toString(Integer object) {
if (object == 0) {
return "FREE";
} else {
return "Taken";
}
}
#Override
public Integer fromString(String string) {
if (string.equalsIgnoreCase("free")) {
return 0;
} else {
return 1;
}
}
};
}
You need to use the overloaded method:
forTableColumn​(StringConverter converter,
ObservableList items)
Please note that ComboBox backing list should contain 0, 1 instead of Free, Taken. The converter is responsible for displaying 0 as Free and 1 as Taken.
Also, the TableColumn should be of type <Review, Integer> instead of <Review, String>.
In your code, you can do something as follows:
ObservableList<Integer> modeList = FXCollections.observableList(0, 1);
TableColumn<Review, Integer> modeCombo = new TableColumn("Mode");
modeCombo.setCellValueFactory(new PropertyValueFactory("mode"));
modeCombo.setCellFactory(ComboBoxTableCell.forTableColumn(converter, modeList)));
Once your basic type is fixed, the following should work:
modeCombo.setOnEditCommit(event -> {
mode.setOperationMode(event.getNewValue());
});
I have task to change this if:
if (userDecision.equalsIgnoreCase("D")) {
return DirectoriesActivity.DELETE;
} else if (userDecision.equalsIgnoreCase("R")) {
return DirectoriesActivity.REPLACE;
} else {
return DirectoriesActivity.ADD_NEW_CONTENTS;
}
On something what will return just enum without using if. I have to use some enum properties but I don't know which one :/ Here is my enum:
public enum DirectoriesActivity {
DELETE,
REPLACE,
ADD_NEW_CONTENTS;
}
I tried to do something like this:
public enum DirectoriesActivity {
DELETE ("D"),
REPLACE ("R"),
ADD_NEW_CONTENTS ("A");
private String directoriesActivityCode;
private DirectoriesActivity(String directoriesActivityCode) {
this.directoriesActivityCode = directoriesActivityCode;
}
public DirectoriesActivity getEnum(String x){
//no idea what to do here
}
}
Or maybe somebody have some other idea?
You can add a Map lookup.
static Map<String, DirectoriesActivity> lookup = new HashMap<>();
static {
// iterate over all the values and
// put the value we want to lookup as the key to the map.
for(DirectoriesActivity da: values())
lookup.put(da.directoriesActivitCode, da);
}
public static DirectoriesActivity lookup(String s) {
// lookup the map we built in the static block.
return s == null ? null : lookup.get(s.toUppercase());
}
This way you can add as many codes as you want without having to change the code.
How about this:
public enum DirectoriesActivity {
DELETE ("D"),
REPLACE ("R"),
ADD_NEW_CONTENTS ("A");
private String directoriesActivityCode;
private DirectoriesActivity(String directoriesActivityCode) {
this.directoriesActivityCode = directoriesActivityCode;
}
public DirectoriesActivity getEnum(String x){
for (DirectoriesActivity directoriesActivity : values()) {
if (directoriesActivity.directoriesActivityCode.equals(x)) {
return directoriesActivity;
}
}
throw new IllegalArgumentException("Unknown value " + x);
}
}
Or in case you are using Java 8
return Arrays.stream(DirectoriesActivity.values())
.filter(directoriesActivity -> directoriesActivity.directoriesActivityCode.equals(userDecision))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Unknown value " + userDecision));
Important side note here is that this solution is performing a lot worse than the solution provided by Peter. But as long as performance is not an issue, I'd prefer a solution like this.
If you can change enum names to D, R, A then you can use built-in feature:
public enum DirectoriesActivity {D,R,A}
DirectoriesActivity activity = DirectoriesActivity.valueOf("D");
valueOf throws IllegalArgumentException if string is not match.
Consider this case.
You have a class which you cannot change or extend in any way.
public class Foo {
...
private Boolean bar;
...
}
You need to edit the fields of that class via BeanEditor, but the logic behind that class allows and uses the fact that Boolean can have, so to say, 3 states: null, true and false.
Tapestry will, however, give you a checkbox with only 2 options, true or false.
So, people online suggest that you convert your Boolean type property to BooleanExtendedEnum type property which could represent three way logic.
public enum BooleanExtendedEnum {
UNDEFINED(null),
TRUE(Boolean.TRUE),
FALSE(Boolean.FALSE);
private Boolean booleanValue;
private static Map<Boolean, BooleanExtendedEnum> booleanToExtendedMap = new HashMap<Boolean, BooleanExtendedEnum>();
static {
for (BooleanExtendedEnum be : BooleanExtendedEnum.values()) {
booleanToExtendedMap.put(be.booleanValue, be);
}
}
private BooleanExtendedEnum(Boolean booleanValue) {
this.booleanValue = booleanValue;
}
public Boolean getBooleanValue() {
return booleanValue;
}
public static BooleanExtendedEnum getBooleanExtendedValue(Boolean booleanInput) {
return booleanToExtendedMap.get(booleanInput);
}
}
Since you cannot change your Foo class, you'll need to create a coercer for Boolean <=> BooleanExtendedEnum.
Coercion<Boolean, BooleanExtendedEnum> threeWayBooleanToExtended = new Coercion<Boolean, BooleanExtendedEnum>() {
#Override
public BooleanExtendedEnum coerce(Boolean input) {
if (input == null) {
return BooleanExtendedEnum.UNDEFINED;
} else {
return BooleanExtendedEnum.getBooleanExtendedEnumValue(input);
}
}
};
Coercion<BooleanExtendedEnum, Boolean> threeWayExtendedToBoolean = new Coercion<BooleanExtendedEnum, Boolean>() {
#Override
public Boolean coerce(BooleanExtendedEnum input) {
if (input == null) {
return null;
} else {
return input.getBooleanValue();
}
}
};
configuration.add(new CoercionTuple<Boolean, BooleanExtendedEnum>(Boolean.class, BooleanExtendedEnum.class, threeWayBooleanToExtended));
configuration.add(new CoercionTuple<BooleanExtendedEnum, Boolean>(BooleanExtendedEnum.class, Boolean.class, threeWayExtendedToBoolean));
Let's assume you have done something as simple as this in your BeanEditor in your tml:
<p:bar>
<div class="t-beaneditor-row">
<label>Bar Value</label>
<t:select t:id="fooBar" t:value="foo.bar" t:model="booleanExtendedSelectModel" t:blankOption="NEVER"/>
</div>
</p:bar>
... and provided the SelectModel like this:
public SelectModel getBooleanExtendedSelectModel() {
return new EnumSelectModel(BooleanExtendedEnum.class, messages);
}
Tapestry will create a drop-down list with three options
Undefined
True
False
However, the real Boolean values it will coerce those displayed values to will be
Undefined -> true
True -> true
False -> false
How can one achieve the desired effect (Undefined -> null), with limitations of not changing the class or wrapping it in another class which has Boolean type fields replaced with BooleanExtendedEnum type ones or using any other "hacky" solution?
The "glue" between the BeanEditor and the backing bean is the BeanModel. BeanModels are created by the BeanModelSource which in turn uses PropertyConduitSource.
It's quite simple to decorate the PropertyConduitSource to use Ternary instead of Boolean.
eg
public class MyAppModule {
public PropertyConduitSource decoratePropertyConduitSource(final PropertyConduitSource old) {
return new PropertyConduitSource() {
public PropertyConduit create(Class rootType, String expression) {
PropertyConduit conduit = old.create(rootType, expression);
// you cound also check for conduit.getAnnotation(AllowNull.class)
// and then annotate your bean properties for a more granular approach
if (Boolean.class.equals(conduit.getPropertyType()) {
return new TernaryPropertyConduit(conduit);
}
return conduit;
}
}
}
}
public class TernaryPropertyConduit implements PropertyConduit {
private PropertyConduit delegate;
public getPropertyType() { return Ternary.class };
public set(Object instance, Object value) {
delegate.set(instance, ((Ternary) value).asBoolean());
}
public get(Object) {
Boolean bValue = (Boolean) delegate.get(instance);
return Ternary.valueOf(instance);
}
}
You could add a property to your page and use a custom block.
public enum Ternary {
TRUE(Boolean.TRUE), FALSE(Boolean.FALSE), UNDEFINED(null);
public static Ternary valueOf(Boolean value) { ... }
public Boolean asBoolean() { ... }
}
public class MyPage {
#Property
private Foo foo;
public Ternary getTernaryBar() {
return Ternary.valueOf(foo.getBar());
}
public void setTernaryBar(Ternary tBar) {
foo.setBar(tBar.asBoolean());
}
}
<t:beaneditor t:id="foo" exclude="bar" add="ternaryBar">
<p:ternaryBar>
<t:label for="ternaryBar"/>
<t:select t:id="ternaryBar" />
</p:ternaryBar>
</t:beaneditor>