Drools : How to get String in List<String> - string

I want to get String in List< String > but did not work.
Drools Rule
import schedule.Schedule
import schedule.Control_Exam_List
rule "People"
salience 5
when
$controlExamList : Control_Exam_List( ) from accumulate( $scheduleCheck : Schedule( $scheduleCECheck1 : control1 , $scheduleCECheck2 : control2 , $scheduleCECheck3 : control3 ) ,
init( Control_Exam_List CEL = new Control_Exam_List(); ),
action( CEL.addData($scheduleCECheck1); CEL.addData($scheduleCECheck2); CEL.addData($scheduleCECheck3); ),
result( CEL ) )
then
System.out.println("Test1: "+$controlExamList);
end
The result return list but I get all string from this list.
Result : Still list< String >
Test1: schedule.Control_Exam_List#22916a
Control_Exam_List Class : List< String >
import java.util.ArrayList;
import java.util.List;
public class Control_Exam_List {
private List<String> code = new ArrayList<String>();
public void addData(String code){
if(this.code.contains(code) != true && !code.equals(""))
this.code.add(code);
}
public List<String> getCode() {
return code;
}
}
Schedule Class : Accumulate from this class
public class Schedule {
private String control1 = "", control2 = "", control3 = "";
public String getControl1() {
return control1;
}
public String getControl2() {
return control2;
}
public String getControl3() {
return control3;
}
public void setControlExam1(String ce_code) {
this.control1 = ce_code;
}
public void setControlExam2(String ce_code) {
this.control2 = ce_code;
}
public void setControlExam3(String ce_code) {
this.control3 = ce_code;
}
}

This is almost as you'd do it Java, with $controlExamList being bound to a Control_Exam_List object; although, in Drools, you may have to cast:
then
for( Object obj: $controlExamList.getCode() ){
System.out.println( (String)obj );
}
end
But you can also try:
then
for( String str: $controlExamList.getCode() ){
System.out.println( str );
}
end

Related

How to implement a search for optional fields using Spring data JPA?

How to implement a search for optional fields using Spring data JPA? I already researched in several places, but nothing helped me.
After some time of trial and error, I was able to implement my search for optional fields.
1st) I extended my repository with the JpaSpecificationExecutor interface
package br.mp.mpce.sge.repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
import br.mp.mpce.sge.domain.Documento;
import br.mp.mpce.sge.domain.Setor;
#Repository
public interface DocumentoRepository extends JpaRepository<Documento, Integer>, JpaSpecificationExecutor<Documento> {
Page<Documento> findPageBySetorCadastro(Setor setor, Pageable pageRequest);
}
2) I implemented methods that returned Specifications according to my search parameters.
package br.mp.mpce.sge.specifications;
import java.time.LocalDateTime;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.data.jpa.domain.Specification;
import br.mp.mpce.sge.domain.Documento;
import br.mp.mpce.sge.domain.Setor;
public final class DocumentoSpecification {
public static Specification<Documento> byCodigo(Integer codigo) {
return new Specification<Documento>() {
private static final long serialVersionUID = -4943925466558950754L;
#Override
public Predicate toPredicate(Root<Documento> root, CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.equal(root.get("codigo"), codigo);
}
};
}
public static Specification<Documento> byAno(String ano) {
return new Specification<Documento>() {
private static final long serialVersionUID = 8333055743400529192L;
#Override
public Predicate toPredicate(Root<Documento> root, CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.equal(root.get("ano"), ano);
}
};
}
public static Specification<Documento> byDataInicial(LocalDateTime dataInicial) {
return new Specification<Documento>() {
private static final long serialVersionUID = -9172458840598392417L;
#Override
public Predicate toPredicate(Root<Documento> root, CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.greaterThanOrEqualTo(root.get("dataCadastro"), dataInicial);
}
};
}
public static Specification<Documento> byDataFinal(LocalDateTime dataFinal) {
return new Specification<Documento>() {
private static final long serialVersionUID = 1422089735211883866L;
#Override
public Predicate toPredicate(Root<Documento> root, CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.lessThanOrEqualTo(root.get("dataCadastro"), dataFinal);
}
};
}
public static Specification<Documento> bySetor(Setor setor) {
return new Specification<Documento>() {
private static final long serialVersionUID = -2336742358257555013L;
#Override
public Predicate toPredicate(Root<Documento> root, CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.equal(root.get("setorCadastro"), setor);
}
};
}
}
3º) I implemented the class of service method.
public Page<Documento> findByFields(Integer codigo, String ano, String dataInicio, String dataFinal, Integer page,
Integer lines, String direction, String orderBy) {
UsuarioSS usuarioSS = UsuarioServiceImpl.authenticated();
if (usuarioSS == null) {
throw new PersonalizedAutorizationException("Acesso negado - Usuario deve estar autenticado");
}
Usuario usuario = usuarioService.findById(usuarioSS.getId());
PageRequest pageRequest = PageRequest.of(page, lines, Direction.fromString(direction), orderBy);
Specification<Documento> specification = DocumentoSpecification.bySetor(usuario.getSetor());
if (codigo != null) {
specification = specification.and(DocumentoSpecification.byCodigo(codigo));
}
if (ano != null) {
specification = specification.and(DocumentoSpecification.byAno(ano));
}
if (dataInicio != null) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate localDateInicio;
localDateInicio = LocalDate.parse(dataInicio, formatter);
LocalDateTime localDateTimeInicio = localDateInicio.atStartOfDay();
specification = specification.and(DocumentoSpecification.byDataInicial(localDateTimeInicio));
}
if (dataFinal != null) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate localDateFinal;
localDateFinal = LocalDate.parse(dataFinal, formatter);
LocalDateTime localDateTimeFinal = localDateFinal.atStartOfDay();
specification = specification.and(DocumentoSpecification.byDataFinal(localDateTimeFinal));
}
Page<Documento> pageDoc = documentoRepository.findAll(specification, pageRequest);
if (pageDoc.getContent().isEmpty()) {
throw new PersonalizedObjectNotFoundException(super.messageSource.getMessage("error.notFound",
new String[] { super.messageSource.getMessage(
StringUtils.uncapitalize(super.getEntityClass().getSimpleName()), null,
StringUtils.uncapitalize(getEntityClass().getSimpleName()), null) },
"error.notFound", null));
}
return pageDoc;
}
4º) Implemented the search method in the restcontroller.
#RequestMapping(value = "/search", method = RequestMethod.GET)
public ResponseEntity<Page<Documento>> findPageByFields(
#RequestParam(value = "codigo", required = false) Integer codigo,
#RequestParam(value = "ano", required = false) String ano,
#RequestParam(value = "dataInicio", required = false) String dataInicio,
#RequestParam(value = "dataFinal", required = false) String dataFinal,
#RequestParam(value = "page", defaultValue = "0") Integer page,
#RequestParam(value = "lines", defaultValue = "10") Integer lines,
#RequestParam(value = "direction", defaultValue = "DESC") String direction,
#RequestParam(value = "orderBy", defaultValue = "dataCadastro") String orderBy) {
Page<Documento> pageDocumento = documentoService.findByFields(codigo, ano, dataInicio, dataFinal, page, lines,
direction, orderBy);
return ResponseEntity.ok().body(pageDocumento);
}

Visibility Of AdminPresentationCollection Annotations - Broadleaf-Commerce

I using broadleaf-commerce famework for my program. I just want to know, how to visibility of AdminPresentationCollection in to config entity file. I config in js file but it doesn't work.
This's my entity. It use #AdminPresentation annotations and #AdminPresentationCollection annotations :
#Entity
#Table(name="BLC_PRICE_LIST")
#Inheritance(strategy = InheritanceType.JOINED)
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region="blStandardElements")
#SQLDelete(sql="UPDATE BLC_PRICE_LIST SET ARCHIVED = 'Y' WHERE PRICE_LIST_ID = ?")
#DirectCopyTransform({
#DirectCopyTransformMember(templateTokens = DirectCopyTransformTypes.SANDBOX, skipOverlaps=true),
#DirectCopyTransformMember(templateTokens = DirectCopyTransformTypes.MULTITENANT_CATALOG)
})
public class PriceListImpl implements PriceList, PriceListAdminPresentation, AdminMainEntity {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "PriceListId")
#GenericGenerator(
name="PriceListId",
strategy="org.broadleafcommerce.common.persistence.IdOverrideTableGenerator",
parameters = {
#Parameter(name="segment_value", value="PriceListImpl"),
#Parameter(name="entity_name", value="com.community.core.pricelist.domain.PriceListImpl")
}
)
#Column(name = "CUSTOMER_RESTRICTION_RULE_TYPE")
#AdminPresentation(
friendlyName = "prices_rule_customer_title_group",
group = GroupName.Customer, order = FieldOrder.TargetCustomer,
fieldType=SupportedFieldType.BROADLEAF_ENUMERATION,
broadleafEnumeration="com.community.core.pricelist.service.type.PriceListCustomerRestrictionRuleType",
defaultValue = "prices_rule_customer_available_for_all_customer",
tooltip = "prices_rule_customer_tooltip"
)
protected String customerRestrictionRuleType;
#OneToMany(mappedBy = "priceList",
targetEntity = PriceListPriceListRuleXrefImpl.class,
cascade = { CascadeType.ALL },
orphanRemoval = true)
#MapKey(name = "key")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region="blPriceLists")
#AdminPresentationMapFields(
toOneTargetProperty = "priceListRule",
toOneParentProperty = "priceList",
mapDisplayFields = {
#AdminPresentationMapField(
fieldName = RuleIdentifier.CUSTOMER_FIELD_KEY,
fieldPresentation = #AdminPresentation(
fieldType = SupportedFieldType.RULE_SIMPLE,
group = GroupName.Customer,
order = FieldOrder.CustomerRule,
ruleIdentifier = RuleIdentifier.CUSTOMER,
friendlyName = "RESTRICT TO THE FOLLOWING CUSTOMERS"
)
)
,
#AdminPresentationMapField(
fieldName = RuleIdentifier.PRICING_CONTEXT_FIELD_KEY,
fieldPresentation = #AdminPresentation(
fieldType = SupportedFieldType.RULE_SIMPLE,
group = GroupName.PricingContext,
order = FieldOrder.PricingContext,
ruleIdentifier = RuleIdentifier.PRICING_CONTEXT,
friendlyName = "price_list_rule_pricing_context_lab")
)
,
#AdminPresentationMapField(
fieldName = RuleIdentifier.REQUEST_FIELD_KEY,
fieldPresentation = #AdminPresentation(
fieldType = SupportedFieldType.RULE_SIMPLE,
group = GroupName.PricingContext,
order = 9000,
ruleIdentifier = RuleIdentifier.REQUEST,
friendlyName = "LIMIT TO CERTAIN WEB REQUESTS?")
)
}
)
Map<String, PriceListPriceListRuleXref> priceListMatchRules = new HashMap<String, PriceListPriceListRuleXref>();
#OneToMany(mappedBy = "priceList", targetEntity = PriceModifierImpl.class,
cascade = { CascadeType.ALL }, orphanRemoval = true)
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "blPriceLists")
#BatchSize(size = 50)
#AdminPresentationCollection( friendlyName = "Price Modifiers",
group = GroupName.PricingContext,
order = 20000,
addType = AddMethodType.PERSIST
,showIfFieldEquals={
#org.broadleafcommerce.common.presentation.FieldValueConfiguration(
fieldName = "customerRestrictionRuleType",
fieldValues = {"ALL_CUSTOMERS", "A","B", "a" , "b"}
)
}
)
protected List<PriceModifier> priceModifier = new ArrayList<PriceModifier>(100);
#Column(name = "ERROR_CODE")
#AdminPresentation(friendlyName = "productOption_errorCode",
group = GroupName.PricingContext, order = 21000)
protected String errorCode;
#Override
public Boolean getIsPricingContext() {
return isPricingContext;
}
#Override
public void setIsPricingContext(Boolean isPricingContext) {
this.isPricingContext = isPricingContext;
}
#Override
public Boolean getIsRequest() {
return isRequest;
}
#Override
public void setIsRequest(Boolean isRequest) {
this.isRequest = isRequest;
}
#Embedded
protected ArchiveStatus archiveStatus = new ArchiveStatus();
#Override
public Long getId() {
return id;
}
#Override
public void setId(Long id) {
this.id = id;
}
public String getCustomerRestrictionRuleType() {
return customerRestrictionRuleType;
//return DynamicTranslationProvider.getValue(this, "customerRestrictionRuleType", customerRestrictionRuleType);
}
public void setCustomerRestrictionRuleType(String customerRestrictionRuleType) {
this.customerRestrictionRuleType = customerRestrictionRuleType;
}
#Override
public Map<String, PriceListPriceListRuleXref> getPriceListMatchRulesXref() {
return priceListMatchRules;
}
#Override
public void setPriceListMatchRulesXref(Map<String, PriceListPriceListRuleXref> priceListMatchRulesXref) {
this.priceListMatchRules = priceListMatchRulesXref;
}
#Override
public List<PriceModifier> getPriceModifier() {
return priceModifier;
}
#Override
public void setPriceModifier(List<PriceModifier> priceModifier) {
this.priceModifier = priceModifier;
}
#Override
public Character getArchived() {
ArchiveStatus temp;
if (archiveStatus == null) {
temp = new ArchiveStatus();
} else {
temp = archiveStatus;
}
return temp.getArchived();
}
#Override
public void setArchived(Character archived) {
if (archiveStatus == null) {
archiveStatus = new ArchiveStatus();
}
archiveStatus.setArchived(archived);
}
#Override
public boolean isActive() {
return DateUtil.isActive(startDate, endDate, true) && 'Y'!=getArchived();
}
#Override
public <G extends PriceList> CreateResponse<G> createOrRetrieveCopyInstance(MultiTenantCopyContext context) throws CloneNotSupportedException {
CreateResponse<G> createResponse = context.createOrRetrieveCopyInstance(this);
if (createResponse.isAlreadyPopulated()) {
return createResponse;
}
PriceList cloned = createResponse.getClone();
cloned.setName(name);
return createResponse;
}
#Override
public String getMainEntityName() {
return getName();
}
#Override
public PriceList createPriceList(){
return new PriceListImpl();
}
}
And this is my enum:
public static final PriceListCustomerRestrictionRuleType ALL_A = new PriceListCustomerRestrictionRuleType("ALL_CUSTOMERS", "AVAILABLE TO ALL CUSTOMERS");
public static final PriceListCustomerRestrictionRuleType A_RULE_ASSIGNMENT_A = new PriceListCustomerRestrictionRuleType("CUSTOMER_RULE_ASSIGNMENT_CUSTOMERS", "CUSTOMER RULE AND ASSIGNMENT TO CUSTOMERS");
And this is my js file:
(function($, BLCAdmin) {
$.each(['com.community.core.pricelist.domain.PriceList'], function(idx, clazz) {
BLCAdmin.addDependentFieldHandler(
clazz,
'#field-customerRestrictionRuleType',
'#field-priceListMatchRules---CUSTOMER',
'CUSTOMER_RULE_ASSIGNMENT_CUSTOMERS'
);
BLCAdmin.addDependentFieldHandler(
clazz,
'#field-isPricingContext',
'#field-priceListMatchRules---PRICING-CONTEXT',
'true'
);
BLCAdmin.addDependentFieldHandler(
clazz,
'#field-isRequest',
'#field-priceListMatchRules---REQUEST',
'true'
);
BLCAdmin.addDependentFieldHandler(
clazz,
'#field-customerRestrictionRuleType',
'#field-priceModifier',
'ALL_CUSTOMERS'
);
/*BLCAdmin.addDependentFieldHandler(
clazz,
'#field-customerRestrictionRuleType',
'#field-priceModifier',
'ALL_CUSTOMERS'
);*/
});
/*$.each(['com.community.core.pricelist.domain.PriceModifier'], function(idx, clazz) {
BLCAdmin.addDependentFieldHandler(
clazz,
'#field-isTargetedItemsRule',
'#field-priceModifierMatchRules---PRODUCT',
'true'
);
});*/
})(jQuery, BLCAdmin);
Help me! Many thank!

Find changed object in JavaFX ObservableArrayList

It took me way too long to set up a listener on one property of the objects in my Observablelist and add a listener to it.
ObservableList<Track> observableResult = FXCollections.observableArrayList((Track tr)-> new Observable[]{tr.selectedProperty()});
observableResult.addListener(new ListChangeListener<Track>() {
#Override
public void onChanged(Change<? extends Track> c) {
c.next();
for(Track k : c.getAddedSubList()){
System.out.println(k.getTrackName());
}
}
});
But I can't seem to be able to locate the actual object the change has been made to. The Change class only appears to support added and removed members, which don't get triggered by the actual changes inside them.
I have a workaround for this, just calling another method that would loop trough the entire ObservableArrayList and get for example, only the selected items, but that gets pretty expensive after I have a couple of thousand objects. Finding the source members that got changed would allow me to just push them to another array and save a bunch of overhead.
You can call getFrom() on the change to get the index of the changed item. I don't think there's a way to actually figure out which property changed (if you have more than one property listed in the extractor) or get the old value, but maybe this is enough.
If you need more, you could consider registering your own listeners with the list to track, which would be tricky but not impossible.
Here's an SSCCE demonstrating the getFrom() call:
import java.util.Random;
import java.util.stream.IntStream;
import javafx.beans.Observable;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
public class ListChangeListenerTest {
public static void main(String[] args) {
ObservableList<Item> itemList = FXCollections.observableArrayList(item -> new Observable[]{item.valueProperty()});
itemList.addListener((Change<? extends Item> c) -> {
while (c.next()) {
if (c.wasUpdated()) {
int index = c.getFrom();
System.out.println("Updated item at "+index+" new value is "+itemList.get(index).getValue());
}
}
});
IntStream.rangeClosed(1, 1000).mapToObj(Item::new).forEach(itemList::add);
Random rng = new Random();
itemList.get(rng.nextInt(itemList.size())).setValue(rng.nextInt(10000));
}
public static class Item {
private final IntegerProperty value = new SimpleIntegerProperty();
public Item(int value) {
setValue(value);
}
public final IntegerProperty valueProperty() {
return this.value;
}
public final int getValue() {
return this.valueProperty().get();
}
public final void setValue(final int value) {
this.valueProperty().set(value);
}
}
}
Here's a version that manages the listeners on the property manually. Note that
This doesn't use an extractor on the list
The property in the Item bean is constructed passing a reference to the bean that owns the property. This allows the listener on the property to get a reference to the Item (via a bit of ugly downcasting)
This gives a bit more flexibility; e.g. if you wanted to check modifications on multiple properties and perform different actions, this would allow this. As you can see, the listener can also access the old value.
import java.util.Random;
import java.util.stream.IntStream;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
public class ListChangeListenerTest {
public static void main(String[] args) {
ChangeListener<Number> valueListener = (obs, oldValue, newValue) -> {
Item item = (Item) ((Property<?>) obs).getBean();
System.out.println("Value for "+item+" changed from " + oldValue + " to "+newValue);
};
ObservableList<Item> itemList = FXCollections.observableArrayList();
itemList.addListener((Change<? extends Item> change) -> {
while (change.next()) {
if (change.wasAdded()) {
for (Item item : change.getAddedSubList()) {
item.valueProperty().addListener(valueListener);
}
}
if (change.wasRemoved()) {
for (Item item : change.getRemoved()) {
item.valueProperty().removeListener(valueListener);
}
}
}
});
IntStream.rangeClosed(1, 1000).mapToObj(Item::new).forEach(itemList::add);
Random rng = new Random();
itemList.get(rng.nextInt(itemList.size())).setValue(rng.nextInt(10000));
}
public static class Item {
private final IntegerProperty value = new SimpleIntegerProperty(this, "value");
private final String id ;
public Item(int value) {
id = "Item "+value ;
setValue(value);
}
public final IntegerProperty valueProperty() {
return this.value;
}
public final int getValue() {
return this.valueProperty().get();
}
public final void setValue(final int value) {
this.valueProperty().set(value);
}
#Override
public String toString() {
return id ;
}
}
}
Finally, if you want to account for "bulk" updates, you need to implement ObservableList yourself. You can do this by subclassing ModifiableObservableListBase, and the basic idea is pretty straightforward. The implementation is made a bit tedious by having to create the Change object representing the update, but it's not too bad. Here's an example that allows updating a contiguous range:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ModifiableObservableListBase;
public class UpdatingObservableList<T> extends ModifiableObservableListBase<T> {
private final List<T> list ;
public UpdatingObservableList(List<T> list) {
this.list = list ;
}
public UpdatingObservableList() {
this(new ArrayList<>());
}
public void updateSublist(int start, int end, Consumer<T> updater) {
if (start < 0) throw new ArrayIndexOutOfBoundsException("Start ("+start+") cannot be < 0");
if (end < start) throw new IllegalArgumentException("End ("+end+") cannot be less than start ("+start+")");
if (end > size()) throw new ArrayIndexOutOfBoundsException("End ("+end+") cannot be greater than list size ("+size()+")");
for (T element : list.subList(start, end)) {
updater.accept(element);
}
fireChange(createUpdate(start, end));
}
#Override
public T get(int index) {
return list.get(index);
}
#Override
public int size() {
return list.size();
}
#Override
protected void doAdd(int index, T element) {
list.add(index, element);
}
#Override
protected T doSet(int index, T element) {
return list.set(index, element);
}
#Override
protected T doRemove(int index) {
return list.remove(index);
}
private Change<T> createUpdate(int start, int end) {
return new Change<T>(this) {
private boolean initialState = true ;
#Override
public boolean next() {
if (initialState) {
initialState = false ;
return true ;
}
return false ;
}
#Override
public void reset() {
initialState = true ;
}
#Override
public int getFrom() {
checkState();
return start ;
}
#Override
public int getTo() {
checkState();
return end ;
}
#Override
public List<T> getAddedSubList() {
checkState();
return Collections.emptyList();
}
#Override
public List<T> getRemoved() {
checkState();
return Collections.emptyList();
}
#Override
protected int[] getPermutation() {
checkState();
return new int[0];
}
#Override
public boolean wasAdded() {
checkState();
return false ;
}
#Override
public boolean wasRemoved() {
checkState();
return false ;
}
#Override
public boolean wasUpdated() {
return true ;
}
#Override
public boolean wasPermutated() {
checkState();
return false ;
}
#Override
public int getRemovedSize() {
checkState();
return 0 ;
}
#Override
public int getAddedSize() {
checkState();
return 0 ;
}
private void checkState() {
if (initialState) {
throw new IllegalStateException("Must call Change.next()");
}
}
};
}
}
and here's a version of the test class that uses this. Note that the update is performed via the list:
import java.util.Random;
import java.util.stream.IntStream;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.ListChangeListener.Change;
public class ListChangeListenerTest {
public static void main(String[] args) {
UpdatingObservableList<Item> itemList = new UpdatingObservableList<Item>();
itemList.addListener((Change<? extends Item> change) -> {
while (change.next()) {
if (change.wasUpdated()) {
for (int i = change.getFrom() ; i < change.getTo() ; i++) {
System.out.println(itemList.get(i) + " updated - new value: "+itemList.get(i).getValue());
}
}
}
});
IntStream.rangeClosed(1, 1000).mapToObj(Item::new).forEach(itemList::add);
Random rng = new Random();
int start = rng.nextInt(itemList.size());
int end = Math.min(itemList.size(), start + 1 + rng.nextInt(15));
itemList.updateSublist(start, end, item -> item.setValue(rng.nextInt(10000)));
}
public static class Item {
private final IntegerProperty value = new SimpleIntegerProperty(this, "value");
private final String id ;
public Item(int value) {
id = "Item "+value ;
setValue(value);
}
public final IntegerProperty valueProperty() {
return this.value;
}
public final int getValue() {
return this.valueProperty().get();
}
public final void setValue(final int value) {
this.valueProperty().set(value);
}
#Override
public String toString() {
return id ;
}
}
}

get vs getProperty in groovy

It surprise me!
According to the document of groovy, groovy may use "getProperty" method to get the property of a object. So when I want to change the behavier of getting property on the special object, I use a category class to override the "getProperty" method. However, it does not work.
At last, I found groovy framework use the "get" method in the category class to get property, even if the object is not a map.
My question is that is it a bug or groovy just work like that.
This is the category class.
class DynaBeanExtension {
public static void setProperty(DynaBean bean, String propertyName, def newValue) {
try {
PropertyUtilsBean pu = null;
if (bean instanceof CustomWrapDynaBean) {
pu = bean.propertyUtilsBean;
}
if (pu != null) {
pu.setProperty(bean, propertyName, newValue);
} else {
PropertyUtils.setProperty(bean, propertyName, newValue);
}
} catch (IllegalArgumentException ex) {
bean.propertyMissing(propertyName, newValue);
}
}
public static def getProperty(DynaBean bean, String propertyName) {
try {
PropertyUtilsBean pu = null;
if (bean instanceof CustomWrapDynaBean) {
pu = bean.propertyUtilsBean;
}
if (pu != null) {
return pu.getProperty(bean, propertyName);
} else {
return PropertyUtils.getProperty(bean, propertyName);
}
} catch (IllegalArgumentException ex) {
return bean.propertyMissing(propertyName);
}
}
public static def get(DynaBean bean, String propertyName) {
try {
PropertyUtilsBean pu = null;
if (bean instanceof CustomWrapDynaBean) {
pu = bean.propertyUtilsBean;
}
if (pu != null) {
return pu.getProperty(bean, propertyName);
} else {
return PropertyUtils.getProperty(bean, propertyName);
}
} catch (IllegalArgumentException ex) {
return bean.propertyMissing(propertyName);
}
}
This is the test code:
public static class TestSubClass {
private final int e = 3, f = 4;
private final Map<String, Object> m = new HashMap<>();
public int getE() {
return e;
}
public int getF() {
return f;
}
public Map<String, Object> getM() {
return m;
}
#Override
public String toString() {
return "TestSubClass{" + "e=" + e + ", f=" + f + ", m=" + m + '}';
}
}
public static class TestClass {
private final int a = 1;
private final TestSubClass b = new TestSubClass();
public int getA() {
return a;
}
public TestSubClass getB() {
return b;
}
#Override
public String toString() {
return "TestClass{" + "a=" + a + ", b=" + b + '}';
}
}
Map<String, String> pMap = new HashMap<>();
pMap.put("b.e", "c");
PropertyUtilsBean pu = new PropertyUtilsBean();
pu.setResolver(new ExResolver(pMap));
TestClass testObj = new TestClass();
DynaBean bean = new CustomWrapDynaBean(testObj, pu);
int c = use(DynaBeanExtension) {
bean.c;
}
This is the code of ExResolver:
public class ExResolver implements Resolver {
private static final char NESTED = '.';
private static final char MAPPED_START = '(';
private static final char MAPPED_END = ')';
private static final char INDEXED_START = '[';
private static final char INDEXED_END = ']';
private final Resolver resolver;
private final Map<String, String> pMap;
public ExResolver(Map<String, String> pMap) {
this(new DefaultResolver(), pMap);
}
public ExResolver(Resolver resolver, Map<String, String> pMap) {
this.resolver = resolver;
this.pMap = new HashMap<>(pMap);
}
private String resolveExpr(String expression) {
for (Map.Entry<String, String> entry : pMap.entrySet()) {
if (expression.startsWith(entry.getValue())) {
String to = entry.getValue();
if (expression.length() == entry.getValue().length()) {
return entry.getKey();
} else {
int toTest = expression.codePointAt(to.length());
if (toTest == NESTED || toTest == MAPPED_START || toTest == INDEXED_START) {
return entry.getKey() + expression.substring(to.length(), expression.length());
} else {
return expression;
}
}
}
}
return expression;
}
#Override
public int getIndex(String expression) {
expression = resolveExpr(expression);
return resolver.getIndex(expression);
}
#Override
public String getKey(String expression) {
expression = resolveExpr(expression);
return resolver.getKey(expression);
}
#Override
public String getProperty(String expression) {
expression = resolveExpr(expression);
return resolver.getProperty(expression);
}
#Override
public boolean hasNested(String expression) {
expression = resolveExpr(expression);
return resolver.hasNested(expression);
}
#Override
public boolean isIndexed(String expression) {
expression = resolveExpr(expression);
return resolver.isIndexed(expression);
}
#Override
public boolean isMapped(String expression) {
expression = resolveExpr(expression);
return resolver.isMapped(expression);
}
#Override
public String next(String expression) {
expression = resolveExpr(expression);
return resolver.next(expression);
}
#Override
public String remove(String expression) {
expression = resolveExpr(expression);
return resolver.remove(expression);
}
}
"get" is invoked, not "getProperty"
What's more, in the real situation DynaBeanExtension is compiled with groovy. The construction of bean is compiled with java. Then by using binding, I put it into the test code which is a runtime script executed by java code.
This happens in the compilation itself. Let's look at a simpler example.
class Main {
static void main(def args) {
Foo foo = new Foo()
foo.str = ""
foo.str
}
}
For Groovy classes
class Foo {
String str
}
If you decompile the Main class, you'll see it is
public class Main implements GroovyObject {
public Main() {
Main this;
CallSite[] arrayOfCallSite = $getCallSiteArray();
MetaClass localMetaClass = $getStaticMetaClass();
this.metaClass = localMetaClass;
}
public static void main(String... args) {
CallSite[] arrayOfCallSite = $getCallSiteArray();
Foo foo = (Foo)ScriptBytecodeAdapter.castToType(arrayOfCallSite[0].callConstructor(Foo.class), Foo.class);
String str = "";
ScriptBytecodeAdapter.setGroovyObjectProperty(str, Main.class, foo, (String)"str");
arrayOfCallSite[1].callGroovyObjectGetProperty(foo);
}
}
A .[property] = call gets compiled to a ScriptBytecodeAdapter.setGroovyObjectProperty, that in turn calls the chain MetaClassImpl.setProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [setter]
And a .[property] call gets compiled to a arrayOfCallSite[1].callGroovyObjectGetProperty, that in turn calls the chain
AbstractCallSite.callGroovyObjectGetProperty > GetEffectivePogoPropertySite.getProperty > MethodMetaProperty$GetBeanMethodMetaProperty.getProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [getter]
For Java classes
If you use a Java version of the class being called, like this
public class Foo {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
The same Main decompiles to
public class Main implements GroovyObject {
public Main() {
Main this;
CallSite[] arrayOfCallSite = $getCallSiteArray();
MetaClass localMetaClass = $getStaticMetaClass();
this.metaClass = localMetaClass;
}
public static void main(String... args) {
CallSite[] arrayOfCallSite = $getCallSiteArray();
Foo foo = (Foo)ScriptBytecodeAdapter.castToType(arrayOfCallSite[0].callConstructor(Foo.class), Foo.class);
String str = "";
ScriptBytecodeAdapter.setProperty(str, null, foo, (String)"str");
arrayOfCallSite[1].callGetProperty(foo);
}
}
A .[property] = call gets compiled to a ScriptBytecodeAdapter.setProperty, that in turn calls the chain [Class].setProperty > InvokerHelper.setProperty -> MetaClassImpl.setProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [setter]
And a .[property] call gets compiled to a arrayOfCallSite[1].callGroovyObjectGetProperty, that in turn calls the chain
AbstractCallSite.callGetProperty > GetEffectivePojoPropertySite.getProperty > MethodMetaProperty$GetBeanMethodMetaProperty.getProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [getter]
To correct your code
As you can see from these dispatch chains, you've overridden the getter correctly (since it happens in the class itself), but if you want to override getProperty or setProperty, you have to do this in metaClass, and not the class itself. The behavior you're seeing is expected. This code demonstrates how to override each
class Foo {
String bar
}
// override using setter in category
#Category(Foo)
class FooCategory {
public String getBar() {
println "in getter"
}
public void setBar(String bar) {
println "in setter"
}
}
use (FooCategory) {
Foo foo = new Foo()
foo.bar = ""
foo.bar
}
// override using metaClass
Foo.metaClass.getProperty { String pname ->
println "in getProperty"
}
Foo.metaClass.setProperty { String pname, Object pValue ->
println "in setProperty"
}
Foo foo = new Foo()
foo.bar = ""
foo.bar
outputs
in setter
in getter
in setProperty
in getProperty
And because the getProperty/setProperty call makes the dispatch (eventually) to the getter/setter, you can prevent the getter/setter from being called at all, like this
class Foo {
String bar
}
Foo.metaClass.getProperty { String pname ->
println "in getProperty"
}
Foo.metaClass.setProperty { String pname, Object pValue ->
println "in setProperty"
}
#Category(Foo)
class FooCategory {
String getBar() {
println "in getter"
}
void setBar(String bar) {
println "in setter"
}
}
use (FooCategory) {
Foo foo = new Foo()
foo.bar = "hi foo1"
foo.bar
}
outputs
in setProperty
in getProperty

Binding a Table to a Sub Property

There are a couple of answers out there for this already, but I have not been able to find anything conclusive. This is the jist of what I am trying to do:
EquityInstrument
public class EquityInstrument : INotifyPropertyChanged
{
private string _Symbol;
public string Symbol
{
get
{
return _Symbol;
}
set
{
_Symbol = value;
OnPropertyChanged("Symbol");
}
}
public EquityInstrument(string Symbol)
{
this.Symbol = Symbol;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string FieldName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(FieldName);
}
}
}
OptionInstrument
public class OptionInstrument : INotifyPropertyChanged;
{
public readonly EquityInstrument UnderlyingInstrument;
private double _StrikePrice;
public double StrikePrice
{
get
{
return _StrikePrice;
}
set
{
_StrikePrice = value;
OnPropertyChanged("StrikePrice");
}
}
private DateTime _Expiration;
public DateTime Expiration;
{
get
{
return _Expiration;
}
set
{
_Expiration = value;
OnPropertyChanged("Expiration");
}
}
public OptionInstrument(string Symbol, double StrikePrice, DateTime Expiration)
{
this.Symbol = Symbol;
this.StrikePrice = StrikePrice;
this.Expiration = Expiration;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string FieldName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(FieldName);
}
}
}
This code initiates the Option Table...
GridControl OptionGrid = new GridControl();
BindingList<OptionInstrument> BoundList = new BindingList<OptionInstrument>();
public void InitializeDataTable()
{
OptionGrid.DataSource = new BindingSource() { DataSource = BoundList };
BandedGridColumn Column0 = new BandedGridColumn();
Column0.FieldName = "Symbol";
BandedGridColumn Column1 = new BandedGridColumn();
Column1.FieldName = "StrikePrice";
BandedGridColumn Column2 = new BandedGridColumn();
Column2.FieldName = "Expiration";
BandedGridView MainView = (BandedGridView)OptionGrid.MainView;
MainView.Columns.Add(Column0);
MainView.Columns.Add(Column1);
MainView.Columns.Add(Column2);
BoundList.Add(new OptionInstrument("DELL", 12.22, new DateTime(2012, 10, 12));
BoundList.Add(new OptionInstrument("MSFT", 13.23, new DateTime(2012, 09, 16));
BoundList.Add(new OptionInstrument("SPY", 12.23, new DateTime(2012, 07, 18));
}
What do you think? Are there any good design patterns to accomplish this?

Resources