I'm having trouble with an h:selectOneMenu not having a selected item when there is already something set on the backing bean. I am using seam and have specified a customer converter. When working on my 'creation' page, everything works fine, something from the menu can be selected, and when the page is submitted, the correct value is assigned and persisted to the database as well.
However when I work on my 'edit' page the menu's default selection is not the current selection. I have gone through and confirmed that something is definitely set etc.
My selectOneMenu looks like this:
<h:selectOneMenu id="selVariable"
value="#{customer.variableLookup}"
converter="#{variableLookupConverter}">
<s:selectItems var="source"
value="#{customerReferenceHelper.variableLookups()}"
label="#{source.name}" />
</h:selectOneMenu>
And the converter is below. It very simple and just turns the id from string to int and back etc:
#Name( "variableLookupConverter" )
public class VariableLookupConverter implements Serializable, Converter {
#In
private CustomerReferenceHelper customerReferenceHelper;
#Override
public Object getAsObject( FacesContext arg0, UIComponent arg1, String arg2 ) {
VariableLookup variable= null;
try {
if ( "org.jboss.seam.ui.NoSelectionConverter.noSelectionValue".equals( arg2 ) ) {
return null;
}
CustomerReferenceHelper customerReferenceHelper = ( CustomerReferenceHelper ) Contexts.getApplicationContext().get(
"customerReferenceHelper" );
Integer id = Integer.parseInt( arg2 );
source = customerReferenceHelper.getVariable( id );
} catch ( NumberFormatException e ) {
log.error( e, e );
}
return variable;
}
#Override
public String getAsString( FacesContext arg0, UIComponent arg1, Object arg2 ) {
String result = null;
VariableLookup variable= ( VariableLookup ) arg2;
Integer id = variable.getId();
result = String.valueOf( id );
return result;
}
}
I've seen a few things about it possibly being the equals() method on the class, (that doesn't add up with everything else working, but I overrode it anyway as below, where the hashcode is just the id (id is a unique identifier for each item).
Equals method:
#Override
public boolean equals( Object other ) {
if ( other == null ) {
return false;
}
if ( this == other ) {
return true;
}
if ( !( other instanceof VariableLookup ) ) {
return false;
}
VariableLookup otherVariable = ( VariableLookup ) other;
if ( this.hashCode() == otherVariable.hashCode() ) {
return true;
}
return false;
}
I'm at my wits end with this, I can't find what I could have missed?! Any help would be much appreciated.
UPDATE: As I understand it, when the list is built it checks the binded value against each item to see if they match to set the selected item. Putting some debug statements in the equals method shows that all comparisons during the building of the list fail due to a comparison to a null object, but checking the seam debug page, it shows that the value is definitely set.
Try this;
<h:selectOneMenu id="selVariable"
value="#{customer.variableLookup}">
<s:selectItems var="source"
value="#{customerReferenceHelper.variableLookups()}"
label="#{source.name}" itemValue="#{source}/>
<s:convertEntity />
</h:selectOneMenu>
I'm using this notation for almost all of my entities.
EDIT
Omnifaces has a SelectItemsConverter it may solve your problem.
Related
I am doing a project for nand2tetris. We write a program in Jack and test it on VMEmulator. The class looks like this:
class List {
field int data;
field List next;
/* Creates a new List object. */
constructor List new(int car, List cdr) {
let data = car;
let next = cdr;
return this;
}
/* Disposes this List by recursively disposing its tail. */
method void dispose() {
if (~(next = null)) {
do next.dispose();
}
// Use an OS routine to recycle the memory held by this object.
do Memory.deAlloc(this);
return;
}
/* Prints the list*/
method void print() {
do Output.printString(" -> ");
do Output.printInt(data);
if (~(next = null)) {
do next.print();
}
return;
}
/* Inserts the argument in the right position of the list (ascending order)*/
method void insertInOrder(int ins){
var List prev, curr, insert;
let prev = this;
let curr = prev.getnext();
while (ins > prev.getdata()){
if (ins < curr.getdata()){
let insert = List.new(ins, curr);
do prev.setnext(insert);
}
else{
let prev = prev.getnext();
let curr = prev.getnext();
}
}
return;
}
/* Searches the argument in the list, if found, it returns the corresponding List object*/
method List find(int toFind){
var List temp;
var List equal;
var boolean found;
let temp = this;
let found = false;
while (~(next = null)){
if(toFind = temp.getdata()){
let equal = temp;
let found = true;
}
let temp = temp.getnext();
}
if (found){
return equal;
}
else{
return null;
}
}
method List getnext(){
return next;
}
method void setnext(List object){
let next = object;
return;
}
method int getdata(){
return data;
}
}
It has one private variable data and a pointer next. So I wrote getter and setter method to return those values. Other methods are fine only the getdata()method is incorrect. When it runs through the VMEmulator, it shows the error Out of segment space in List.getdata.3. This shows in the VMEmulator.
0function List.getdata0
1push argument0
2pop pointer0
3push this 0
4return
the error is at the 4th line return. When I change the Jack code, the same error is still at the 4th line.
What exactly is the problem in my getter method?
When you run a VM program on the VMEmulator you must first manually set the pointers to the various segments, otherwise you may get an "Out of segment space" error.
To understand the necessary settings, look at what the corresponding .tst file does. An alternative method is to insert the proposed code inside a function, since the function call automatically makes this type of setting.
You can get this error when you try to access member data of an object which is not constructed. Could it be that the List cdr in the constructor was not properly constructed?
I have a JFace TableViewer with a ViewerComparator. The direction and column of the sort is set on the selection listener of the table. This works just fine.
However, I recently changed my LabelProvider from a DelegatingStyledCellLabelProvider to a DecoratingStyledCellLabelProvider. After I made that change, the table seems to be sorting - the column gets highlighted and the directional arrow is added to the column header. The first column gets sorted the way I was expecting based on the values of the sorting column, but the rest of the rows aren't moving with it.
This is how the table looks before I try to sort. Notice the value of the first column in relation to the other values.
And after I sort. See how the 2nd column's values are in the same order they were before even though the first column is alphabetical?
My code for adding the DecoratingStyledCellLabelProvider is as follows:
column.setLabelProvider(
new DecoratingStyledCellLabelProvider(
new MyStyledCellLabelProvider(),
MyPlugin.getDefault().getWorkbench().getDecoratorManager(),
null ) );
Edited to add the compare code
#Override
public int compare( final Viewer viewer, final Object e1, final Object e2 ) {
if( !( e1 instanceof TreeElement<?> && e2 instanceof TreeElement<?> ) ) {
return super.compare(viewer, e1, e2);
}
if(this.sortDirection == SortDirection.NONE) {
return ((TreeElement<?>) e1).getSortIndex() - ((TreeElement<?>) e2).getSortIndex();
}
final IBaseLabelProvider labelProvider = ((ColumnViewer) viewer).getLabelProvider(this.columnNumber);
if( !(labelProvider instanceof DelegatingStyledCellLabelProvider) ) {
return super.compare(viewer, e1, e2);
}
final IStyledLabelProvider columnLabelProvider = ((DelegatingStyledCellLabelProvider) labelProvider).getStyledStringProvider();
final TreeElement<?> treeElement1 = (TreeElement<?>) e1;
final String text1 = columnLabelProvider.getStyledText(treeElement1).getString();
final TreeElement<?> treeElement2 = (TreeElement<?>) e2;
final String text2 = columnLabelProvider.getStyledText(treeElement2).getString();
// either one could be null
int result;
if(text1 == null) {
result = text2 == null ?
0 : // both null, so they're the same
1; // only text1 is null, so text2 is greater
} else {
result = text2 == null ?
-1 : // only text2 is null, so text1 is greater
text1.compareToIgnoreCase(text2); // both not null - do a real text compare
}
// If descending order, flip the direction
return this.sortDirection == SortDirection.DESCENDING ? -result : result;
}
EDIT 2: Adding label provider code.
My label provider extends ColumnLabelProvider and implements IStyledLabelProvider
#Override
public StyledString getStyledText(final Object element) {
if( !(element instanceof TreeElement<?>) ) {
return null;
}
final String elemText = getText(element);
final StyledString styledString = new StyledString(elemText == null ? "" : elemText);
if( !(element instanceof MyElement<?,?>) ) {
return styledString;
}
// apply styles as needed
return styledString;
}
EDIT 3: Resolution
I wasn't able to fix the issue. However, it became moot as I was able to work out a change in the design so I didn't need the DecoratingStyledCellLableProvider anyway. Thanks for all your help!
I am trying to serialize Guava Range objects to JSON using Gson, however the default serialization fails, and I'm unsure how to correctly implement a TypeAdapter for this generic type.
Gson gson = new Gson();
Range<Integer> range = Range.closed(10, 20);
String json = gson.toJson(range);
System.out.println(json);
Range<Integer> range2 = gson.fromJson(json,
new TypeToken<Range<Integer>>(){}.getType());
System.out.println(range2);
assertEquals(range2, range);
This fails like so:
{"lowerBound":{"endpoint":10},"upperBound":{"endpoint":20}}
PASSED: typeTokenInterface
FAILED: range
java.lang.RuntimeException: Unable to invoke no-args constructor for
com.google.common.collect.Cut<java.lang.Integer>. Register an
InstanceCreator with Gson for this type may fix this problem.
at com.google.gson.internal.ConstructorConstructor$12.construct(
ConstructorConstructor.java:210)
...
Note that the default serialization actually loses information - it fails to report whether the endpoints are open or closed. I would prefer to see it serialized similar to its toString(), e.g. [10‥20] however simply calling toString() won't work with generic Range instances, as the elements of the range may not be primitives (Joda-Time LocalDate instances, for example). For the same reason, implementing a custom TypeAdapter seems difficult, as we don't know how to deserialize the endpoints.
I've implemented most of a TypeAdaptorFactory based on the template provided for Multimap which ought to work, but now I'm stuck on the generics. Here's what I have so far:
public class RangeTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (typeToken.getRawType() != Range.class
|| !(type instanceof ParameterizedType)) {
return null;
}
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
TypeAdapter<?> elementAdapter = (TypeAdapter<?>)gson.getAdapter(TypeToken.get(elementType));
// Bound mismatch: The generic method newRangeAdapter(TypeAdapter<E>) of type
// GsonUtils.RangeTypeAdapterFactory is not applicable for the arguments
// (TypeAdapter<capture#4-of ?>). The inferred type capture#4-of ? is not a valid
// substitute for the bounded parameter <E extends Comparable<?>>
return (TypeAdapter<T>) newRangeAdapter(elementAdapter);
}
private <E extends Comparable<?>> TypeAdapter<Range<E>> newRangeAdapter(final TypeAdapter<E> elementAdapter) {
return new TypeAdapter<Range<E>>() {
#Override
public void write(JsonWriter out, Range<E> value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
String repr = (value.lowerBoundType() == BoundType.CLOSED ? "[" : "(") +
(value.hasLowerBound() ? elementAdapter.toJson(value.lowerEndpoint()) : "-\u221e") +
'\u2025' +
(value.hasLowerBound() ? elementAdapter.toJson(value.upperEndpoint()) : "+\u221e") +
(value.upperBoundType() == BoundType.CLOSED ? "]" : ")");
out.value(repr);
}
public Range<E> read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String[] endpoints = in.nextString().split("\u2025");
E lower = elementAdapter.fromJson(endpoints[0].substring(1));
E upper = elementAdapter.fromJson(endpoints[1].substring(0,endpoints[1].length()-1));
return Range.range(lower, endpoints[0].charAt(0) == '[' ? BoundType.CLOSED : BoundType.OPEN,
upper, endpoints[1].charAt(endpoints[1].length()-1) == '[' ? BoundType.CLOSED : BoundType.OPEN);
}
};
}
}
However the return (TypeAdapter<T>) newRangeAdapter(elementAdapter); line has a compilation error and I'm now at a loss.
What's the best way to resolve this error? Is there a better way to serialize Range objects that I'm missing? What about if I want to serialize RangeSets?
Rather frustrating that the Google utility library and Google serialization library seem to require so much glue to work together :(
This feels somewhat like reinventing the wheel, but it was a lot quicker to put together and test than the time spent trying to get Gson to behave, so at least presently I'll be using the following Converters to serialize Range and RangeSet*, rather than Gson.
/**
* Converter between Range instances and Strings, essentially a custom serializer.
* Ideally we'd let Gson or Guava do this for us, but presently this is cleaner.
*/
public static <T extends Comparable<? super T>> Converter<Range<T>, String> rangeConverter(final Converter<T, String> elementConverter) {
final String NEG_INFINITY = "-\u221e";
final String POS_INFINITY = "+\u221e";
final String DOTDOT = "\u2025";
return new Converter<Range<T>, String>() {
#Override
protected String doForward(Range<T> range) {
return (range.hasLowerBound() && range.lowerBoundType() == BoundType.CLOSED ? "[" : "(") +
(range.hasLowerBound() ? elementConverter.convert(range.lowerEndpoint()) : NEG_INFINITY) +
DOTDOT +
(range.hasUpperBound() ? elementConverter.convert(range.upperEndpoint()) : POS_INFINITY) +
(range.hasUpperBound() && range.upperBoundType() == BoundType.CLOSED ? "]" : ")");
}
#Override
protected Range<T> doBackward(String range) {
String[] endpoints = range.split(DOTDOT);
Range<T> ret = Range.all();
if(!endpoints[0].substring(1).equals(NEG_INFINITY)) {
T lower = elementConverter.reverse().convert(endpoints[0].substring(1));
ret = ret.intersection(Range.downTo(lower, endpoints[0].charAt(0) == '[' ? BoundType.CLOSED : BoundType.OPEN));
}
if(!endpoints[1].substring(0,endpoints[1].length()-1).equals(POS_INFINITY)) {
T upper = elementConverter.reverse().convert(endpoints[1].substring(0,endpoints[1].length()-1));
ret = ret.intersection(Range.upTo(upper, endpoints[1].charAt(endpoints[1].length()-1) == ']' ? BoundType.CLOSED : BoundType.OPEN));
}
return ret;
}
};
}
/**
* Converter between RangeSet instances and Strings, essentially a custom serializer.
* Ideally we'd let Gson or Guava do this for us, but presently this is cleaner.
*/
public static <T extends Comparable<? super T>> Converter<RangeSet<T>, String> rangeSetConverter(final Converter<T, String> elementConverter) {
return new Converter<RangeSet<T>, String>() {
private final Converter<Range<T>, String> rangeConverter = rangeConverter(elementConverter);
#Override
protected String doForward(RangeSet<T> rs) {
ArrayList<String> ls = new ArrayList<>();
for(Range<T> range : rs.asRanges()) {
ls.add(rangeConverter.convert(range));
}
return Joiner.on(", ").join(ls);
}
#Override
protected RangeSet<T> doBackward(String rs) {
Iterable<String> parts = Splitter.on(",").trimResults().split(rs);
ImmutableRangeSet.Builder<T> build = ImmutableRangeSet.builder();
for(String range : parts) {
build.add(rangeConverter.reverse().convert(range));
}
return build.build();
}
};
}
*For inter-process communication, Java serialization would likely work just fine, as both classes implement Serializable. However I'm serializing to disk for more permanent storage, meaning I need a format I can trust won't change over time. Guava's serialization doesn't provide that guarantee.
Here is a Gson JsonSerializer and JsonDeserializer that generically supports a Range: https://github.com/jamespedwards42/Fava/wiki/Range-Marshaller
#Override
public JsonElement serialize(final Range src, final Type typeOfSrc, final JsonSerializationContext context) {
final JsonObject jsonObject = new JsonObject();
if ( src.hasLowerBound() ) {
jsonObject.add( "lowerBoundType", context.serialize( src.lowerBoundType() ) );
jsonObject.add( "lowerBound", context.serialize( src.lowerEndpoint() ) );
} else
jsonObject.add( "lowerBoundType", context.serialize( BoundType.OPEN ) );
if ( src.hasUpperBound() ) {
jsonObject.add( "upperBoundType", context.serialize( src.upperBoundType() ) );
jsonObject.add( "upperBound", context.serialize( src.upperEndpoint() ) );
} else
jsonObject.add( "upperBoundType", context.serialize( BoundType.OPEN ) );
return jsonObject;
}
#Override
public Range<? extends Comparable<?>> deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
if ( !( typeOfT instanceof ParameterizedType ) )
throw new IllegalStateException( "typeOfT must be a parameterized Range." );
final JsonObject jsonObject = json.getAsJsonObject();
final JsonElement lowerBoundTypeJsonElement = jsonObject.get( "lowerBoundType" );
final JsonElement upperBoundTypeJsonElement = jsonObject.get( "upperBoundType" );
if ( lowerBoundTypeJsonElement == null || upperBoundTypeJsonElement == null )
throw new IllegalStateException( "Range " + json
+ "was not serialized with this serializer! The default serialization does not store the boundary types, therfore we can not deserialize." );
final Type type = ( ( ParameterizedType ) typeOfT ).getActualTypeArguments()[0];
final BoundType lowerBoundType = context.deserialize( lowerBoundTypeJsonElement, BoundType.class );
final JsonElement lowerBoundJsonElement = jsonObject.get( "lowerBound" );
final Comparable<?> lowerBound = lowerBoundJsonElement == null ? null : context.deserialize( lowerBoundJsonElement, type );
final BoundType upperBoundType = context.deserialize( upperBoundTypeJsonElement, BoundType.class );
final JsonElement upperBoundJsonElement = jsonObject.get( "upperBound" );
final Comparable<?> upperBound = upperBoundJsonElement == null ? null : context.deserialize( upperBoundJsonElement, type );
if ( lowerBound == null && upperBound != null )
return Range.upTo( upperBound, upperBoundType );
else if ( lowerBound != null && upperBound == null )
return Range.downTo( lowerBound, lowerBoundType );
else if ( lowerBound == null && upperBound == null )
return Range.all();
return Range.range( lowerBound, lowerBoundType, upperBound, upperBoundType );
}
Here is a straight forward solution. Works very well
import com.google.common.collect.BoundType;
import com.google.common.collect.Range;
import com.google.gson.*;
import java.lang.reflect.Type;
public class GoogleRangeAdapter implements JsonSerializer, JsonDeserializer {
public static String TK_hasLowerBound = "hasLowerBound";
public static String TK_hasUpperBound = "hasUpperBound";
public static String TK_lowerBoundType = "lowerBoundType";
public static String TK_upperBoundType = "upperBoundType";
public static String TK_lowerBound = "lowerBound";
public static String TK_upperBound = "upperBound";
#Override
public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = (JsonObject)json;
boolean hasLowerBound = jsonObject.get(TK_hasLowerBound).getAsBoolean();
boolean hasUpperBound = jsonObject.get(TK_hasUpperBound).getAsBoolean();
if (!hasLowerBound && !hasUpperBound) {
return Range.all();
}
else if (!hasLowerBound && hasUpperBound){
double upperBound = jsonObject.get(TK_upperBound).getAsDouble();
BoundType upperBoundType = BoundType.valueOf(jsonObject.get(TK_upperBoundType).getAsString());
if (upperBoundType == BoundType.OPEN)
return Range.lessThan(upperBound);
else
return Range.atMost(upperBound);
}
else if (hasLowerBound && !hasUpperBound){
double lowerBound = jsonObject.get(TK_lowerBound).getAsDouble();
BoundType lowerBoundType = BoundType.valueOf(jsonObject.get(TK_lowerBoundType).getAsString());
if (lowerBoundType == BoundType.OPEN)
return Range.greaterThan(lowerBound);
else
return Range.atLeast(lowerBound);
}
else {
double lowerBound = jsonObject.get(TK_lowerBound).getAsDouble();
double upperBound = jsonObject.get(TK_upperBound).getAsDouble();
BoundType upperBoundType = BoundType.valueOf(jsonObject.get(TK_upperBoundType).getAsString());
BoundType lowerBoundType = BoundType.valueOf(jsonObject.get(TK_lowerBoundType).getAsString());
if (lowerBoundType == BoundType.OPEN && upperBoundType == BoundType.OPEN)
return Range.open(lowerBound, upperBound);
else if (lowerBoundType == BoundType.OPEN && upperBoundType == BoundType.CLOSED)
return Range.openClosed(lowerBound, upperBound);
else if (lowerBoundType == BoundType.CLOSED && upperBoundType == BoundType.OPEN)
return Range.closedOpen(lowerBound, upperBound);
else
return Range.closed(lowerBound, upperBound);
}
}
#Override
public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
Range<Double> range = (Range<Double>)src;
boolean hasLowerBound = range.hasLowerBound();
boolean hasUpperBound = range.hasUpperBound();
jsonObject.addProperty(TK_hasLowerBound, hasLowerBound);
jsonObject.addProperty(TK_hasUpperBound, hasUpperBound);
if (hasLowerBound) {
jsonObject.addProperty(TK_lowerBound, range.lowerEndpoint());
jsonObject.addProperty(TK_lowerBoundType, range.lowerBoundType().name());
}
if (hasUpperBound) {
jsonObject.addProperty(TK_upperBound, range.upperEndpoint());
jsonObject.addProperty(TK_upperBoundType, range.upperBoundType().name());
}
return jsonObject;
}
}
I cannot seem to find anywhere, any examples on how to make use of the GroupFormatter delegate to allow me to add footers to my groups when using the ObjectListView control.
Does anyone have any examples that could demonstrate this? I want to remove the text from the group header and add a footer (different text per footer). As well as changing font, etc.
Any examples would be very helpful.
You can analyze the code for the
public void MakeGroupies<T>(T[] values, string[] descriptions, object[] images, string[] subtitles, string[] tasks)
method of the ObjectListView class. That explicitly sets the GroupKeyGetter, GroupKeyToTitleConverter and GroupFormatter property delegates.
This is C# but your VB adaptation should be straightforward. I am using this small test class as the object type to bind to the list view.
public class TestClass
{
private readonly string _s;
private readonly float _f;
public TestClass( string p1, float p2 )
{
this._s = p1;
this._f = p2;
}
[OLVColumn(DisplayIndex = 1, Name="S", Title="String")]
public string S {get {return this._s;}}
[OLVColumn( DisplayIndex = 2, Name = "F", Title = "Float" )]
public float F {get {return this._f;}}
}
So as not to manually define column traits I am using attributes inside the bound object and a
BrightIdeasSoftware.Generator.GenerateColumns( this.olv, typeof( TestClass ) );
call in the form/user control where I am using the list view. In fact here is the method that completely isolates ObjectListView configuration:
void SetData( TestClass[] objects )
{
// build list columns
Generator.GenerateColumns( this.olv, typeof( TestClass ) );
// use groups and make current column the priimary sort column
this.olv.ShowGroups = true;
this.olv.SortGroupItemsByPrimaryColumn = false;
// loop through columns and set properties
foreach( OLVColumn col in this.olv.Columns )
{
col.Groupable = true;
col.Sortable = true;
if( col.Name == "F" )
{
col.MakeGroupies<float>( new float[] { 10f, 100f, 1000f }, new string[] { "<10", "10-100", "100-1000", ">1000" } );
}
else if( col.Name == "S" )
{
col.UseInitialLetterForGroup = false;
//
col.GroupKeyGetter = ( obj ) =>
{
TestClass tc = (TestClass)obj;
switch( char.ToLower( tc.S[0] ) )
{
case 'a':
case 'e':
case 'i':
case 'o':
case 'u': return true;
default: return false;
}
};
//
col.GroupKeyToTitleConverter = ( o ) => { bool b = (bool)o; return b ? "vowel" : "consonant"; };
//
col.GroupFormatter = ( /*OLVGroup*/ group, /*GroupingParameters*/ parms ) =>
{
string s = string.Format ("{0} {1}", group.GroupId, group.Id);
//group.BottomDescription = "BottomDescription: " + s;
//group.TopDescription = "TopDescription: " + s;
group.Footer = "Footer: " + s;
};
}
}
//
this.olv.RebuildColumns();
//
this.olv.SetObjects( objects );
}
You will definitely have one different footer per each group.
Ok this is the code for one section of my switch statement:
case 1: {
System.out.print("Member ID: ");
int key = in.nextInt();
while(members.findItemByKey(key) == -1){
System.out.print("That is an invalid member ID!\nEnter a new one: ");
key = in.nextInt();
}
System.out.print("ISBN: ");
int book = in.nextInt();
while(books.findItemByKey(book) == -1){
System.out.println("That book is not in the system.\nPlease make a new choice: ");
book = in.nextInt();
}
while(stock.findItemByKey(book) != -1){
try {
m = members.get(members.findItemByKey(key));
t = books.get(books.findItemByKey(book));
} catch (Exception e) {
e.printStackTrace();
}
if(m.checkOut(t) == true){
stock.removeItem(t);
}
}
}
Here is the method that is calling:
public int findItemByKey(int key){
for(E e: list)
{
if(e.getKey() == key){
return findItem(e);
}
}
return -1;
}
public int findItem(E item){
if (item == null){
for (int i = 0; i < numElements; i++)
if(list[i]==null)
return i;
}else {
for( int i = 0; i < numElements; i++)
if (item.equals(list[i]))
return i;
}
return -1;
}
Ok, I know there's a lot to look at here, but here's what's happening. When I enter an invalid member ID, it functions properly and keeps asking the user for a new member ID until a valid one is entered. Now when I enter a book, regardless of whether I enter a valid or invalid book, I am getting a null pointer exception thrown by this line:
if(e.getKey() == key)
books, members, and stock are all arraylists defined the same way in my code. I don't understand why I'm having this exception thrown with books and not with the members. The classes for book and member are defined the same way, both have the same getKey method within them.
Maybe there's just too much going on in this question for anyone to be able to really see what's going on. Basically I just can't understand why I get a null pointer exception with the one and not with the other.
Edit: Decided I should post the getKey() method for each class.
public int getKey()
{
return ISBN;
}
Is the one for books
public int getKey()
{
return memberId;
}
Is the one for members.
ISBN is the identifier for books and memberId is the identifier for my members. Everything looks like it's calling the same things, but it errors for books and not for members. Just don't get it.
Either this e is null or the value returned from the statement e.getKey() is null.
You have to make sure that the list doesn't contain a null element and then their keys should also be not-null.
If you want to ignore these values being null, you can do like:
if(e!=null && e.getKey()!=null && e.getKey() == key){
//statements here.
}