I am facing an issue in storing data in persistence store,i am trying to store events for different dates in persistence store but the data is getting overridden the code is :
public ListEventScreen(Vector v,String timezone) {
for(int i=0;i<v.size();i++){
EventBean bean=(EventBean)v.elementAt(i);
//a normal label in the app, just to display text, anchored left
LabelField label = new LabelField(bean.getSummary(),LabelField.FIELD_LEFT);
//add the label to the screen
add(label);
saveUserInfo(v);
}
}
public void saveUserInfo(Vector vectorData){
// static{
store = PersistentStore.getPersistentObject( 0x1dfc10ec9447eb14L );
synchronized(store) {
store.setContents(vectorData);
store.commit();
}
//}
}
Please let me know what has to be changed.
Every time you call store.setContents(), the current contents of the persistentStore are overwritten with the Vector you are passing in. You need to make sure you are loading the previous events that were already in the persistentStore into your Vector before then adding new events into that Vector that you are then saving.
You are also calling saveUserInfo() on every iteration of your loop in ListEventScreen(). You should be calling it outside of the loop instead.
I would do something like this:
public ListEventScreen(Vector v,String timezone) {
Enumeration e = v.elements();;
while (e.hasMoreElements()){
EventBean bean = (EventBean) e.nextElement();
//a normal label in the app, just to display text, anchored left
LabelField label = new LabelField(bean.getSummary(),LabelField.FIELD_LEFT);
//add the label to the screen
add(label);
}
}
public void loadUserInfo(Vector vectorData){
// static{
store = PersistentStore.getPersistentObject( 0x1dfc10ec9447eb14L );
synchronized(store) {
Vector v = (Vector) store.getContents();
Enumeration e = v.elements();
while (e.hasMoreElemens){
vectorData.add(e.nextElement());
}
}
//}
}
public void saveUserInfo(Vector vectorData){
// static{
store = PersistentStore.getPersistentObject( 0x1dfc10ec9447eb14L );
synchronized(store) {
store.setContents(vectorData);
store.commit();
}
//}
}
.
{
Vector v = new Vector();
loadUserInfo(v);
ListEventScreen(v, ...);
... modify v contents as needed ...
saveUserInfo(v);
}
If you do not mind changing the format of your persistent store contents, I would wrap the store in a singleton class instead:
public class EventBeans extends Vector implements Persistable
{
private static final long persistKey = 0x1dfc10ec9447eb14L;
private static EventBeans _instance = null;
private static PersistentObject _persist = null;
static{
_persist = PersistentStore.getPersistentObject(persistKey);
_instance = (EventBeans) _persist.getContents();
if (_instance == null){
_instance = new EventBeans();
_persist.setContents(_instance);
_persist.commit();
}
}
private EventBeans(){
super();
}
public static EventBeans getInstance(){
return _instance;
}
public static synchronized void save(){
_persist.commit();
}
}
.
{
Vector v = EventBeans.getInstance();
ListEventScreen(v, ...);
... modify v contents as needed ...
EventBeans.save();
}
Related
I have two trees:tree1,tree2 and I want to drag and drop from tree1 to tree2.
The problem is when I want to move a completely folder,his childrens don't take the right place in tree2. They are just added in tree2 but don't get their true parent (the folder that I dragged).In fact I didn't find how to know the new id of the folder in tree2.
Here is my code :
private void Drag() {
tree1().setDragMode(TreeDragMode.NODE);
// Allow all nodes to have children
for (final Object itemId : tree2().getItemIds()) {
tree2().setChildrenAllowed(itemId, true);
}
tree2().setDragMode(TreeDragMode.NODE);
tree2().setDropHandler(new TreeSortDropHandler(tree2(), tree1()));
}
public class TreeSortDropHandler implements DropHandler {
private final Tree tree2;
private final Tree tree1;
/**
* Tree must use {#link HierarchicalContainer}.
*
* #param tree
*/
public TreeSortDropHandler(final Tree tree2,
final Tree tree1) {
this.tree2 = tree2;
this.tree1 = tree1;
}
#Override
public AcceptCriterion getAcceptCriterion() {
// TODO Auto-generated method stub
return AcceptAll.get();
}
#Override
public void drop(DragAndDropEvent dropEvent) {
// TODO Auto-generated method stub
final Transferable t = dropEvent.getTransferable();
// see the comment in getAcceptCriterion()
if (t.getSourceComponent() != tree1
|| !(t instanceof DataBoundTransferable)) {
return;
}
final TreeTargetDetails dropData = ((TreeTargetDetails) dropEvent
.getTargetDetails());
final Object sourceItemId = ((DataBoundTransferable) t).getItemId();
// FIXME: Why "over", should be "targetItemId" or just
// "getItemId"
final Object targetItemId = dropData.getItemIdOver();
// Location describes on which part of the node the drop took
// place
final VerticalDropLocation location = dropData.getDropLocation();
moveNode(sourceItemId, targetItemId, location);
}
private void moveNode(final Object sourceItemId,
Object targetItemId, final VerticalDropLocation location) {
final HierarchicalContainer container = (HierarchicalContainer) tree1
.getContainerDataSource();
if (location == VerticalDropLocation.MIDDLE) {
if (tree1.hasChildren(sourceItemId))
{
Collection<?> children=tree1.getChildren(sourceItemId);
tree2.addItem(tree1.getItemCaption(sourceItemId));
tree2.setParent(tree1.getItemCaption(sourceItemId),targetItemId);
targetItemId=tree2.getItem(tree1.getItemCaption(sourceItemId));
tree2.addItems(children);
tree2.setParent(children,tree2.getItemCaption(sourceItemId));
System.out.print(tree2.getItem(tree1.getItemCaption(sourceItemId)));
System.out.print("//added");
}else {
tree2.addItem(tree1.getItemCaption(sourceItemId));
tree2.setParent(tree1.getItemCaption(sourceItemId),targetItemId);
System.out.print("added");
}
I'm trying to list all the available phone numbers with display name from contacts app in a ListView with search. Also if a contact has more than one phone number, it has to be displayed in separate list item (as different contact not as same contact in Contacts App).
I've successfully retrived all the phone numbers with display name and displayed in ListView. Multiple phone numbers of a contact also displayed as separate contact.
But, when I search - either it gives irrelevant phone number for the display name or it fails to filter when I entered single character. Please suggest on this.
Here is my code:
public class ContactsListFragment extends ListFragment implements
AdapterView.OnItemClickListener, LoaderManager.LoaderCallbacks<Cursor> {
// Defines a tag for identifying log entries
private static final String TAG = "ContactsListFragment";
private ContactsAdapter mAdapter; // The main query adapter
private String mSearchTerm; // Stores the current search query term
// Contact selected listener that allows the activity holding this fragment to be notified of
// a contact being selected
private OnContactsInteractionListener mOnContactSelectedListener;
/**
* Fragments require an empty constructor.
*/
public ContactsListFragment() {}
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
// Let this fragment contribute menu items
setHasOptionsMenu(true);
// Create the main contacts adapter
mAdapter = new ContactsAdapter(getActivity());
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
// Inflate the list fragment layout
return inflater.inflate(R.layout.contact_list_fragment, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
// Set up ListView, assign adapter and set some listeners. The adapter was previously
// created in onCreate().
setListAdapter(mAdapter);
getListView().setOnItemClickListener(this);
getListView().setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
}
});
}
#Override
public void onAttach(Activity activity){
super.onAttach(activity);
try{
// Assign callback listener which the holding activity must implement. This is used
// so that when a contact item is interacted with (selected by the user) the holding
// activity will be notified and can take further action such as extracting the contact
//details and pass it to AddNewUserDialogFragment
mOnContactSelectedListener = (OnContactsInteractionListener) activity;
}catch(ClassCastException e){
throw new ClassCastException(activity.toString()
+ " must implement OnContactsInteractionListener");
}
}
#Override
public void onPause(){
super.onPause();
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// Gets the Cursor object currently bound to the ListView
final Cursor cursor = mAdapter.getCursor();
// Moves to the Cursor row corresponding to the ListView item that was clicked
cursor.moveToPosition(position);
final String displayName = cursor.getString(ContactMobileNumbQuery.DISPLAY_NAME);
final String mobileNumb = cursor.getString(ContactMobileNumbQuery.NUMBER);
mOnContactSelectedListener.onContactSelected(displayName, mobileNumb);
}
/**
* Called when ListView selection is cleared, for example
* when search mode is finished and the currently selected
* contact should no longer be selected.
*/
private void onSelectionCleared() {
// Uses callback to notify activity this contains this fragment
mOnContactSelectedListener.onSelectionCleared();
// Clears currently checked item
getListView().clearChoices();
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
// Inflate the menu items
inflater.inflate(R.menu.contact_list_menu, menu);
// Locate the search item
MenuItem searchItem = menu.findItem(R.id.menu_search);
//sets up and configures the ActionBar SearchView
final SearchManager mSearchManager = (SearchManager)getActivity().getSystemService(Context.SEARCH_SERVICE);
// Retrieves the SearchView from the search menu item
final SearchView mSearchView = (SearchView) searchItem.getActionView();
// Assign searchable info to SearchView
mSearchView.setSearchableInfo(mSearchManager.getSearchableInfo(getActivity().getComponentName()));
// Set listeners for SearchView
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
// Called when the action bar search text has changed. Updates
// the search filter, and restarts the loader to do a new query
// using the new search string.
String newFilter = !TextUtils.isEmpty(newText) ? newText : null;
// Don't do anything if the filter is empty
if(mSearchTerm == null && newText == null){
return true;
}
// Don't do anything if the new filter is the same as the current filter
if(mSearchTerm != null && mSearchTerm.equals(newText)){
return true;
}
// Updates current filter to new filter
mSearchTerm = newFilter;
// Restarts the loader. This triggers onCreateLoader(), which builds the
// necessary content Uri from mSearchTerm.
getLoaderManager().restartLoader(ContactMobileNumbQuery.QUERY_ID, null, ContactsListFragment.this);
return true;
}
});
searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
#Override
public boolean onMenuItemActionExpand(MenuItem item) {
// Nothing to do when the action item is expanded
return true;
}
#Override
public boolean onMenuItemActionCollapse(MenuItem item) {
// When the user collapses the SearchView the current search string is
// cleared and the loader restarted.
if(!TextUtils.isEmpty(mSearchTerm)){
onSelectionCleared();
}
mSearchTerm = null;
getLoaderManager().restartLoader(ContactMobileNumbQuery.QUERY_ID, null, ContactsListFragment.this);
return true;
}
});
getLoaderManager().restartLoader(ContactMobileNumbQuery.QUERY_ID, null, ContactsListFragment.this);
}
#Override
public void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
if(!TextUtils.isEmpty(mSearchTerm)){
// Saves the current search string
outState.putString(SearchManager.QUERY, mSearchTerm);
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item){
return super.onOptionsItemSelected(item);
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Log.i(TAG, "onCreateLoader starts");
//If this is the loader for finding contacts in the Contacts Provider
if(id == ContactMobileNumbQuery.QUERY_ID){
Uri contentUri;
// There are two types of searches, one which displays all contacts and
// one which filters contacts by a search query. If mSearchTerm is set
// then a search query has been entered and the latter should be used.
if(mSearchTerm == null){
// Since there's no search string, use the content URI that searches the entire
// Contacts table
contentUri = ContactMobileNumbQuery.CONTENT_URI;
}else{
// Since there's a search string, use the special content Uri that searches the
// Contacts table. The URI consists of a base Uri and the search string.
contentUri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(mSearchTerm));
}
// Returns a new CursorLoader for querying the Contacts table. No arguments are used
// for the selection clause. The search string is either encoded onto the content URI,
// or no contacts search string is used. The other search criteria are constants. See
// the ContactsQuery interface.
return new CursorLoader(getActivity(),
contentUri,
ContactMobileNumbQuery.PROJECTION,
ContactMobileNumbQuery.SELECTION,
null,
ContactMobileNumbQuery.SORT_ORDER);
}
Log.e(TAG, "onCreateLoader - incorrect ID provided (" + id + ")");
return null;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// This swaps the new cursor into the adapter.
if(loader.getId() == ContactMobileNumbQuery.QUERY_ID){
mAdapter.swapCursor(data);
}
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
if(loader.getId() == ContactMobileNumbQuery.QUERY_ID){
// When the loader is being reset, clear the cursor from the adapter. This allows the
// cursor resources to be freed.
mAdapter.swapCursor(null);
}
}
/**
* This is a subclass of CursorAdapter that supports binding Cursor columns to a view layout.
* If those items are part of search results, it will be bind to the view layout.
*/
private class ContactsAdapter extends CursorAdapter implements SectionIndexer {
private LayoutInflater mInflater; // Stores the layout inflater
private TextAppearanceSpan highlightTextSpan; // Stores the highlight text appearance style
/**
* Instantiates a new Contacts Adapter.
* #param context A context that has access to the app's layout.
*/
public ContactsAdapter(Context context) {
super(context, null, 0);
// Stores inflater for use later
mInflater = LayoutInflater.from(context);
}
#Override
public Object[] getSections() {
// TODO Auto-generated method stub
return null;
}
#Override
public int getPositionForSection(int section) {
// TODO Auto-generated method stub
return 0;
}
#Override
public int getSectionForPosition(int position) {
// TODO Auto-generated method stub
return 0;
}
/**
* Binds data from the Cursor to the provided view.
*/
#Override
public void bindView(View view, Context context, Cursor cursor) {
// Gets handles to individual view resources
final ViewHolder holder = (ViewHolder) view.getTag();
final String displayName = cursor.getString(ContactMobileNumbQuery.DISPLAY_NAME);
final String mobileNumb = cursor.getString(ContactMobileNumbQuery.NUMBER);
final int startIndex = indexOfSearchQuery(displayName);
if (startIndex == -1) {
// If the user didn't do a search, or the search string didn't match a display
// name, show the display name without highlighting
holder.text1.setText(displayName);
holder.mobile_text.setText(mobileNumb);
if (TextUtils.isEmpty(mSearchTerm)) {
} else {
}
}else {
// If the search string matched the display name, applies a SpannableString to
// highlight the search string with the displayed display name
// Wraps the display name in the SpannableString
final SpannableString highlightedName = new SpannableString(displayName);
// Sets the span to start at the starting point of the match and end at "length"
// characters beyond the starting point
highlightedName.setSpan(highlightTextSpan, startIndex,
startIndex + mSearchTerm.length(), 0);
// Binds the SpannableString to the display name View object
holder.text1.setText(highlightedName);
}
}
private int indexOfSearchQuery(String displayName) {
if (!TextUtils.isEmpty(mSearchTerm)) {
return displayName.toLowerCase(Locale.getDefault()).indexOf(
mSearchTerm.toLowerCase(Locale.getDefault()));
}
return -1;
}
/**
* Overrides newView() to inflate the list item views.
*/
#Override
public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
// Inflates the list item layout.
final View itemLayout = mInflater.inflate(R.layout.contacts_list_view_item, viewGroup,false);
// Creates a new ViewHolder in which to store handles to each view resource. This
// allows bindView() to retrieve stored references instead of calling findViewById for
// each instance of the layout.
final ViewHolder holder = new ViewHolder();
holder.text1 = (TextView) itemLayout.findViewById(android.R.id.text1);
holder.text2 = (TextView) itemLayout.findViewById(android.R.id.text2);
holder.mobile_text = (TextView) itemLayout.findViewById(R.id.mobile_text);
// Stores the resourceHolder instance in itemLayout. This makes resourceHolder
// available to bindView and other methods that receive a handle to the item view.
itemLayout.setTag(holder);
// Returns the item layout view
return itemLayout;
}
}
/**
* A class that defines fields for each resource ID in the list item layout. This allows
* ContactsAdapter.newView() to store the IDs once, when it inflates the layout, instead of
* calling findViewById in each iteration of bindView.
*/
private class ViewHolder {
TextView text1;
TextView text2;
TextView mobile_text;
}
/**
* This interface must be implemented by any activity that loads this fragment. When an
* interaction occurs, such as touching an item from the ListView, these callbacks will
* be invoked to communicate the event back to the activity.
*/
public interface OnContactsInteractionListener {
/**
* Called when a contact is selected from the ListView.
* #param contactUri The contact Uri.
*/
public void onContactSelected(String name, String mobile);
/**
* Called when the ListView selection is cleared like when
* a contact search is taking place or is finishing.
*/
public void onSelectionCleared();
// Uses callback to notify activity this contains this fragment
}
/**
* This interface defines constants used by mobile number retrieval queries.
*/
public interface ContactMobileNumbQuery{
final static int QUERY_ID = 1;
//A Content Uri for Phone table
final static Uri CONTENT_URI = Phone.CONTENT_URI;
//The search or filter query Uri
final static Uri FILTER_URI = Phone.CONTENT_FILTER_URI;
final static String SELECTION = Phone.HAS_PHONE_NUMBER + "=1" + " AND " + Phone.DISPLAY_NAME_PRIMARY + "<>''";
final static String SORT_ORDER = Phone.SORT_KEY_PRIMARY;
final static String[] PROJECTION = {
Phone._ID,
Phone.DISPLAY_NAME_PRIMARY,
Phone.LOOKUP_KEY,
Phone.HAS_PHONE_NUMBER,
Phone.NUMBER,
Phone.TYPE,
SORT_ORDER
};
final static int ID = 0;
final static int DISPLAY_NAME = 1;
final static int LOOKUP_KEY = 2;
final static int HAS_PHONE = 3;
final static int NUMBER = 4;
final static int TYPE = 5;
}
}
Can somebody help me to fix it? Thanks in advance!
The below code fixes my issue.
#Override
public void bindView(View view, Context context, Cursor cursor) {
// Gets handles to individual view resources
final ViewHolder holder = (ViewHolder) view.getTag();
final String displayName = cursor.getString(ContactMobileNumbQuery.DISPLAY_NAME);
final String mobileNumb = cursor.getString(ContactMobileNumbQuery.NUMBER);
holder.text1.setText(displayName);
holder.mobile_text.setText(mobileNumb);
}
I'm a bit confused about JavaFx 8 and the listener memory leak problem. The official doc says:
The ObservableValue stores a strong reference to the listener which will prevent the listener from being garbage collected and may result in a memory leak.
I would like to have an example where the usage of ObservableValue<T> addListener method create a memory leak.
For example, if I have a class like this:
public class ConfigurationPane extends AnchorPane {
#FXML
private Label titleLabel;
public ConfigurationPane () {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("view/ConfigurationPane .fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
#FXML
private void initialize() {
titleLabel.sceneProperty().addListener(new MyListener());
}
}
Can I get memory leaks? When a ConfigurationPane object is garbage collected, the MyListener object is garbage collected too? I'm not able to see a scenario where
a strong reference to the listener will prevent the listener from being garbage collected
P.S. I see other S.O. questions about this but none of these helped me to understand the problem.
Thanks.
It means that map which store your listener is not using weak references, and you have to remove listeners youself to avoid memory leaks.
In the example below LeakingListener objects will never be freed although corresponding TextFields being removed from scene:
public class LeakListener extends Application {
private static class LeakingListener implements InvalidationListener {
private final TextField tf;
private final int[] placeHolder = new int[50000]; // to simplify monitoring
public LeakingListener(TextField tf) {
this.tf = tf;
}
public void invalidated(Observable i) {
tf.setText(tf.getText() + ".");
}
}
#Override
public void start(Stage primaryStage) {
final Pane root = new VBox(3);
final Button btnType = new Button("Type in all");
Button btnAdd = new Button("Add");
btnAdd.setOnAction((e) -> {
TextField tf = new TextField();
root.getChildren().add(tf);
// memory leaking listener which never gets cleaned
btnType.armedProperty().addListener(new LeakingListener(tf));
});
Button btnRemove = new Button("Remove");
btnRemove.setOnAction((ActionEvent e) -> {
// find random TextEdit element
Optional<Node> toRemove = root.getChildren().stream().filter((Node t) -> t instanceof TextField).findAny();
// if any, and remove it
if (toRemove.isPresent()) {
root.getChildren().remove(toRemove.get());
}
});
Button btnMemory = new Button("Check Memory");
btnMemory.setOnAction((e) -> {
System.gc();
System.out.println("Free memory (bytes): " + Runtime.getRuntime().freeMemory());
});
root.getChildren().addAll(btnAdd, btnRemove, btnType, btnMemory);
Scene scene = new Scene(root, 200, 350);
primaryStage.setScene(scene);
primaryStage.show();
}
}
If ObservableValue stores weak reference to a listener, you wouldn't have a problem. It can be mimicked by next example:
public class LeakListener extends Application {
private static class NonLeakingListener implements InvalidationListener {
// we need listener to don't hold reference on TextField as well
private final WeakReference<TextField> wtf;
private final int[] placeHolder = new int[10000];
public NonLeakingListener(TextField tf) {
this.wtf = new WeakReference<>(tf);
}
public void invalidated(Observable i) {
if (wtf.get() != null) {
wtf.get().setText(wtf.get().getText() + ".");
}
}
}
#Override
public void start(Stage primaryStage) {
final Pane root = new VBox(3);
final Button btnType = new Button("Type in all");
// Here is rough weak listeners list implementation
WeakHashMap<TextField, NonLeakingListener > m = new WeakHashMap<>();
btnType.armedProperty().addListener((e)-> {
for (TextField tf : m.keySet()) {
m.get(tf).invalidated(null);
}
});
Button btnAdd = new Button("Add");
btnAdd.setOnAction((e) -> {
TextField tf = new TextField();
root.getChildren().add(tf);
m.put(tf, new NonLeakingListener(tf));
});
Button btnRemove = new Button("Remove");
btnRemove.setOnAction((e) -> {
// find random TextEdit element
Optional<Node> toRemove = root.getChildren().stream().filter((Node t) -> t instanceof TextField).findAny();
// if any, and remove it
if (toRemove.isPresent()) {
root.getChildren().remove(toRemove.get());
}
});
Button btnMemory = new Button("Check Memory");
btnMemory.setOnAction((e)-> {
System.gc();
System.out.println("Free memory (bytes): " + Runtime.getRuntime().freeMemory());
});
root.getChildren().addAll(btnAdd, btnRemove, btnType, btnMemory);
Scene scene = new Scene(root, 200, 350);
primaryStage.setScene(scene);
primaryStage.show();
}
}
I need to allow my content pipeline extension to use a pattern similar to a factory. I start with a dictionary type:
public delegate T Mapper<T>(MapFactory<T> mf, XElement d);
public class MapFactory<T>
{
Dictionary<string, Mapper<T>> map = new Dictionary<string, Mapper<T>>();
public void Add(string s, Mapper<T> m)
{
map.Add(s, m);
}
public T Get(XElement xe)
{
if (xe == null) throw new ArgumentNullException(
"Invalid document");
var key = xe.Name.ToString();
if (!map.ContainsKey(key)) throw new ArgumentException(
key + " is not a valid key.");
return map[key](this, xe);
}
public IEnumerable<T> GetAll(XElement xe)
{
if (xe == null) throw new ArgumentNullException(
"Invalid document");
foreach (var e in xe.Elements())
{
var val = e.Name.ToString();
if (map.ContainsKey(val))
yield return map[val](this, e);
}
}
}
Here is one type of object I want to store:
public partial class TestContent
{
// Test type
public string title;
// Once test if true
public bool once;
// Parameters
public Dictionary<string, object> args;
public TestContent()
{
title = string.Empty;
args = new Dictionary<string, object>();
}
public TestContent(XElement xe)
{
title = xe.Name.ToString();
args = new Dictionary<string, object>();
xe.ParseAttribute("once", once);
}
}
XElement.ParseAttribute is an extension method that works as one might expect. It returns a boolean that is true if successful.
The issue is that I have many different types of tests, each of which populates the object in a way unique to the specific test. The element name is the key to MapFactory's dictionary. This type of test, while atypical, illustrates my problem.
public class LogicTest : TestBase
{
string opkey;
List<TestBase> items;
public override bool Test(BehaviorArgs args)
{
if (items == null) return false;
if (items.Count == 0) return false;
bool result = items[0].Test(args);
for (int i = 1; i < items.Count; i++)
{
bool other = items[i].Test(args);
switch (opkey)
{
case "And":
result &= other;
if (!result) return false;
break;
case "Or":
result |= other;
if (result) return true;
break;
case "Xor":
result ^= other;
break;
case "Nand":
result = !(result & other);
break;
case "Nor":
result = !(result | other);
break;
default:
result = false;
break;
}
}
return result;
}
public static TestContent Build(MapFactory<TestContent> mf, XElement xe)
{
var result = new TestContent(xe);
string key = "Or";
xe.GetAttribute("op", key);
result.args.Add("key", key);
var names = mf.GetAll(xe).ToList();
if (names.Count() < 2) throw new ArgumentException(
"LogicTest requires at least two entries.");
result.args.Add("items", names);
return result;
}
}
My actual code is more involved as the factory has two dictionaries, one that turns an XElement into a content type to write and another used by the reader to create the actual game objects.
I need to build these factories in code because they map strings to delegates. I have a service that contains several of these factories. The mission is to make these factory classes available to a content processor. Neither the processor itself nor the context it uses as a parameter have any known hooks to attach an IServiceProvider or equivalent.
Any ideas?
I needed to create a data structure essentially on demand without access to the underlying classes as they came from a third party, in this case XNA Game Studio. There is only one way to do this I know of... statically.
public class TestMap : Dictionary<string, string>
{
private static readonly TestMap map = new TestMap();
private TestMap()
{
Add("Logic", "LogicProcessor");
Add("Sequence", "SequenceProcessor");
Add("Key", "KeyProcessor");
Add("KeyVector", "KeyVectorProcessor");
Add("Mouse", "MouseProcessor");
Add("Pad", "PadProcessor");
Add("PadVector", "PadVectorProcessor");
}
public static TestMap Map
{
get { return map; }
}
public IEnumerable<TestContent> Collect(XElement xe, ContentProcessorContext cpc)
{
foreach(var e in xe.Elements().Where(e => ContainsKey(e.Name.ToString())))
{
yield return cpc.Convert<XElement, TestContent>(
e, this[e.Name.ToString()]);
}
}
}
I took this a step further and created content processors for each type of TestBase:
/// <summary>
/// Turns an imported XElement into a TestContent used for a LogicTest
/// </summary>
[ContentProcessor(DisplayName = "LogicProcessor")]
public class LogicProcessor : ContentProcessor<XElement, TestContent>
{
public override TestContent Process(XElement input, ContentProcessorContext context)
{
var result = new TestContent(input);
string key = "Or";
input.GetAttribute("op", key);
result.args.Add("key", key);
var items = TestMap.Map.Collect(input, context);
if (items.Count() < 2) throw new ArgumentNullException(
"LogicProcessor requires at least two items.");
result.args.Add("items", items);
return result;
}
}
Any attempt to reference or access the class such as calling TestMap.Collect will generate the underlying static class if needed. I basically moved the code from LogicTest.Build to the processor. I also carry out any needed validation in the processor.
When I get to reading these classes I will have the ContentService to help.
The task is to backup/restore Persistable object with BB Desktop Manager or in any other way. The main aim is to keep data between device firmware updates...
I have:
public final class UserList implements Persistable {
//The persistable objects.
private Hashtable fData;
//Initialize the class with empty values.
public UserList() {
fData = new Hashtable();
}
//Initialize the class with the specified values.
public UserList(Hashtable p) {
fData = p;
}
public Hashtable getData() {
return fData;
}}
I also have implemented SyncItem (as found in one of the examples)
public final class UserListSync extends SyncItem {
private static UserList fList;
private static final int FIELDTAG_NAME = 1;
private static final int FIELDTAG_AGE = 2;
private static PersistentObject store;
static {
store = PersistentStore.getPersistentObject(0x3167239af4aa40fL);
}
public UserListSync() {
}
public String getSyncName() {
return "Sync Item Sample";
}
public String getSyncName(Locale locale) {
return null;
}
public int getSyncVersion() {
return 1;
}
public boolean getSyncData(DataBuffer db, int version) {
boolean retVal = true;
synchronized (store) {
if (store.getContents() != null) {
fList = (UserList)store.getContents();
}
}
try {
Enumeration e = fList.getData().keys();
while (e.hasMoreElements()) {
String key = (String) e.nextElement();
String value = (String) fList.getData().get(key);
//Write the name.
db.writeShort(key.length() + 1);
db.writeByte(FIELDTAG_NAME);
db.write(key.getBytes());
db.writeByte(0);
//Write the age.
db.writeShort(value.length() + 1);
db.writeByte(FIELDTAG_AGE);
db.write(value.getBytes());
db.writeByte(0);
}
} catch (Exception e) {
retVal = false;
}
return retVal;
}
//Interprets and stores the data sent from the Desktop Manager.
public boolean setSyncData(DataBuffer db, int version) {
int length;
Hashtable table = new Hashtable();
Vector keys = new Vector();
Vector values = new Vector();
boolean retVal = true;
try {
//Read until the end of the Databuffer.
while (db.available() > 0) {
//Read the length of the data.
length = db.readShort();
//Set the byte array to the length of the data.
byte[] bytes = new byte[length];
//Determine the type of data to be read (name or age).
switch (db.readByte()) {
case FIELDTAG_NAME:
db.readFully(bytes);
keys.addElement(new String(bytes).trim());
break;
case FIELDTAG_AGE:
db.readFully(bytes);
values.addElement(new String(bytes).trim());
break;
}
}
} catch (Exception e) {
retVal = false;
}
for (int i = 0; i < keys.size(); i++) {
table.put(keys.elementAt(i), values.elementAt(i));
}
try {
//Store the new data in the persistent store object.
fList = new UserList(table);
store.setContents(fList);
store.commit();
} catch (Exception e) {
retVal = false;
}
return retVal;
}}
The entry poing is following:
public class SyncItemSample extends UiApplication {
private static PersistentObject store;
private static UserList userList;
static {
store = PersistentStore.getPersistentObject(0x3167239af4aa40fL);
}
public static void main(String[] args) {
SyncItemSample app = new SyncItemSample();
app.enterEventDispatcher();
}
public SyncItemSample() {
UserListScreen userListScreen;
//Check to see if the store exists on the BlackBerry.
synchronized (store) {
if (store.getContents() == null) {
//Store does not exist, create it with default values
userList = new UserList();
store.setContents(userList);
store.commit();
} else {
//Store exists, retrieve data from store.
userList = (UserList)store.getContents();
}
}
//Create and push the UserListScreen.
userListScreen = new UserListScreen(userList);
pushScreen(userListScreen);
}}
And here is an implementation of screen:
public final class UserListScreen extends MainScreen {
Vector fLabels = new Vector();
Vector fValues = new Vector();
VerticalFieldManager leftColumn = new VerticalFieldManager();
VerticalFieldManager rightColumn = new VerticalFieldManager();
UserList fList;
public UserListScreen(UserList list) {
super();
fList = list;
//Create a horizontal field manager to hold the two vertical field
//managers to display the names and ages in two columns.
VerticalFieldManager inputManager = new VerticalFieldManager();
HorizontalFieldManager backGround = new HorizontalFieldManager();
//Array of fields to display the names and ages.
LabelField title = new LabelField("User List",
LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH);
setTitle(title);
final TextField fld1 = new TextField(TextField.NO_NEWLINE);
fld1.setLabel("input label");
inputManager.add(fld1);
final TextField fld2 = new TextField(TextField.NO_NEWLINE);
fld2.setLabel("input value");
inputManager.add(fld2);
final ButtonField fld3 = new ButtonField();
fld3.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
fList.getData().put(fld1.getText().trim(), fld2.getText().trim());
refresh();
}
});
fld3.setLabel("add");
inputManager.add(fld3);
add(inputManager);
//Add the column titles and a blank field to create a space.
LabelField leftTitle = new LabelField("label ");
leftColumn.add(leftTitle);
LabelField rightTitle = new LabelField("value");
rightColumn.add(rightTitle);
refresh();
//Add the two vertical columns to the horizontal field manager.
backGround.add(leftColumn);
backGround.add(rightColumn);
//Add the horizontal field manager to the screen.
add(backGround);
}
private void refresh() {
leftColumn.deleteAll();
rightColumn.deleteAll();
fLabels.removeAllElements();
fValues.removeAllElements();
//Populate and add the name and age fields.
Enumeration e = fList.getData().keys();
while (e.hasMoreElements()) {
String key = (String) e.nextElement();
String value = (String) fList.getData().get(key);
final LabelField tmp1 = new LabelField(key);
final LabelField tmp2 = new LabelField(value);
leftColumn.add(tmp1);
rightColumn.add(tmp2);
fLabels.addElement(tmp1);
fValues.addElement(tmp2);
}
}
public boolean onClose() {
System.exit(0);
return true;
}}
So as you see it should be very easy...
So all of these I run application, add values to Persistent object and they are added correctly, are stored during device resets and so on...
When I run Desktop Manager and make a Backup it seems that UserList is backed-up, as size of backup grows together with adding new data into persistent store.
But when I run "Wipe device" on my BB 9300 (and all data from Persistent store is cleared as it is expected) and then run Restore from just made backup file - nothing is updated in the Application and persistent store is seems to be empty.
In some examples I have found adding alternate entry point "init" but I can't tune eveything like it is described with my EclipsePlugin
Could you advice me how to store data in backup file and the to retrieve the same data from backup and load it back to the application, or how to log any of events with Desktop Manager?
If someone has experienced the same problem you can try to disconnect the device before wiping it. It is strange but it helped :)