// Copyright 2011 Palantir Technologies
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.palantir.ptoss.cinch;
import java.awt.BorderLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import com.palantir.ptoss.cinch.core.Bindings;
import com.palantir.ptoss.cinch.core.DefaultBindableModel;
import com.palantir.ptoss.cinch.swing.Bound;
import junit.framework.TestCase;
/**
* This is meant to capture some feedback that was received about using Bindings with subclasses.
*/
public class SubclassEdgeCasesTest extends TestCase {
class BaseModel extends DefaultBindableModel {
private String displayText;
public String getDisplayText() {
return displayText;
}
public void setDisplayText(String value) {
displayText = value;
update();
}
}
class CommentableModel extends BaseModel {
private String comment;
public String getComment() {
return comment;
}
public void setComment(String value) {
comment = value;
update();
}
}
class BaseView<T extends BaseModel> {
protected final T model;
protected Bindings bindings = new Bindings();
protected JPanel panel = new JPanel(new BorderLayout());
// "model." is required to prevent subclasses from clobbering
@Bound(to = "model.displayText")
private final JLabel label = new JLabel();
public BaseView(T model) {
this.model = model;
panel.add(label, BorderLayout.CENTER);
}
}
class CommentsView extends BaseView<CommentableModel> {
@Bound(to = "comment")
private JLabel countLabel = new JLabel();
public CommentsView(CommentableModel model) {
super(model);
panel.add(countLabel, BorderLayout.EAST);
bindings.bind(this);
}
}
/**
* Bindings are done against the declared type of models, not the runtime type.
*/
public void testReflectionUponDeclaredType() {
CommentableModel model = new CommentableModel();
// should throw IllegalArgumentException: could not find getter/setter for comment
// because it's performing introspection against the declared type of the model in BaseView,
// which is BaseModel
// even though the runtime type is CommentableModel
try {
CommentsView view = new CommentsView(model);
fail("should have thrown exception");
} catch (IllegalArgumentException ex) {
assertEquals("could not find either getter/setter for comment", ex.getMessage());
}
}
public class CommentsView2 extends BaseView<CommentableModel> {
private final CommentableModel commentableModel;
@Bound(to="comment")
private JLabel countLabel = new JLabel();
private Bindings bindings = new Bindings();
public CommentsView2(CommentableModel model) {
super(model);
this.commentableModel = model;
panel.add(countLabel, BorderLayout.EAST);
bindings.bind(this);
}
}
/**
* Views that are intended to be subclassed should explicitly name their bound models:
* Example:
* <pre>
* @Bound(to = "model.displayText")
* </pre>
* instead of
* <pre>
* @Bound(to = "displayText")
* </pre>
*/
public void testSuperclassExplicitBinding() {
CommentableModel model = new CommentableModel();
// should throw IllegalArgumentException: could not find getter/setter for displayText
// actually caused by there being more than one displayText property available in the context
CommentsView2 view = new CommentsView2(model);
}
}