package com.vaadin.demo.sampler;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
import com.vaadin.ui.Component;
/**
* Represents one feature or sample, with associated example.
* <p>
*
* </p>
*
*/
@SuppressWarnings("serial")
abstract public class Feature implements Serializable {
public static final Object PROPERTY_ICON = "Icon";
public static final Object PROPERTY_NAME = "Name";
public static final Object PROPERTY_DESCRIPTION = "Description";
private static final String MSG_SOURCE_NOT_AVAILABLE = "I'm terribly sorry,"
+ " but it seems the source could not be found.\n"
+ "Please try adding the source folder to the classpath for your"
+ " server, or tell the administrator to do so!";
private static final Object MUTEX = new Object();
private String javaSource = null;
private FeatureSet parentFeatureSet;
/**
* Gets the name of this feature. Try not to exceed 25 characters too much.
*
* @return
*/
abstract public String getName();
/**
* Gets the description for this feature. Should describe what the example
* intends to showcase. May contain HTML. 100 words should be enough, and
* about 7 rows...
*
* @return the description
*/
abstract public String getDescription();
/**
* Gets related resources, i.e links to resources related to the example.
* <p>
* Good candidates are resources used to make the example (CSS, images,
* custom layouts), documentation links (reference manual), articles (e.g.
* pattern description, usability discussion).
* </p>
* <p>
* May return null, if the example has no related resources.
* </p>
* <p>
* The name of the NamedExternalResource will be shown in the UI. <br/>
* Note that Javadoc should be referenced via {@link #getRelatedAPI()}.
* </p>
*
* @see #getThemeBase()
* @return related external stuff
*/
abstract public NamedExternalResource[] getRelatedResources();
/**
* Gets related API resources, i.e links to javadoc of used classes.
* <p>
* Good candidates are Vaadin classes being demoed in the example, or other
* classes playing an important role in the example.
* </p>
* <p>
* May return null, if the example uses no interesting classes.
* <p>
*
* @return
*/
abstract public APIResource[] getRelatedAPI();
/**
* Gets related Features; the classes returned should extend Feature.
* <p>
* Good candidates are Features similar to this one, Features using the
* functionality demoed in this one, and Features being used in this one.
* </p>
* <p>
* May return null, if no other Features are related to this one.
* <p>
*
* @return
*/
abstract public Class<? extends Feature>[] getRelatedFeatures();
/**
* Gets the name of the icon for this feature, usually simpleName +
* extension.
*
* @return
*/
public String getIconName() {
String icon = getClass().getSimpleName() + ".gif";
return icon;
}
/**
* Get the example instance. Override if instantiation needs parameters.
*
* @return
*/
public Component getExample() {
String className = this.getClass().getName() + "Example";
try {
Class<?> classObject = getClass().getClassLoader().loadClass(
className);
return (Component) classObject.newInstance();
} catch (ClassNotFoundException e) {
return null;
} catch (InstantiationException e) {
return null;
} catch (IllegalAccessException e) {
return null;
}
}
public String getSource() {
synchronized (MUTEX) {
if (javaSource == null) {
try {
javaSource = SourceReader.getSourceForClass(getExample()
.getClass());
} catch (IOException e) {
System.err.println(MSG_SOURCE_NOT_AVAILABLE + " ("
+ getFragmentName() + ")");
return MSG_SOURCE_NOT_AVAILABLE;
}
}
}
return javaSource;
}
public String getSourceHTML() {
return getSource();
}
/**
* Gets the name used when resolving the path for this feature. Usually no
* need to override, but NOTE that this must be unique within Sampler.
*
* @return
*/
public String getFragmentName() {
return getClass().getSimpleName();
}
public enum Version {
OLD(0), BUILD(Integer.parseInt(AbstractApplicationServlet.VERSION_MAJOR
+ "" + AbstractApplicationServlet.VERSION_MINOR)), V62(62), V63(
63), V64(64), V65(65), V66(66), V67(67);
private final int version;
Version(int version) {
this.version = version;
}
/**
* Checks whether this version is newer or as new as the build that it
* is included in.
*
* You can use Version.BUILD if you wish for a Feature to always appear
* as new.
*
* @return
*/
boolean isNew() {
return BUILD.version <= version;
}
}
/**
* Returns the Vaadin version number in which this feature was added to
* Sampler. Usually features should only be added in major and minor
* version, not in maintenance versions.
*
* Uses int internally for easy comparison: version 6.1.4 -> 61 (maintenance
* version number is ignored)
*
* Override in each feature. Returns Version.OLD otherwise.
*
* @return Version Vaadin version when this feature was added to Sampler
*/
abstract public Version getSinceVersion();
/**
* Gets the base url used to reference theme resources.
*
* @return
*/
protected static final String getThemeBase() {
return SamplerApplication.getThemeBase();
}
@Override
public String toString() {
return getName();
}
@Override
public boolean equals(Object obj) {
// A feature is uniquely identified by its class name
if (obj == null) {
return false;
}
return obj.getClass() == getClass();
}
@Override
public int hashCode() {
// A feature is uniquely identified by its class name
return getClass().hashCode();
}
public void setParentFeature(FeatureSet parentFeatureSet) {
this.parentFeatureSet = parentFeatureSet;
}
protected Class<? extends Feature>[] getSiblingFeatures() {
if (parentFeatureSet != null) {
Feature[] features = parentFeatureSet.getFeatures();
ArrayList<Class<? extends Feature>> siblingFeatureList = new ArrayList<Class<? extends Feature>>(
features.length - 1);
for (Feature f : features) {
if (f != this) {
siblingFeatureList.add(f.getClass());
}
}
@SuppressWarnings("unchecked")
Class<? extends Feature>[] siblingFeaturs = (Class<? extends Feature>[]) siblingFeatureList
.toArray(new Class<?>[siblingFeatureList.size()]);
return siblingFeaturs;
} else {
return null;
}
}
}