/** * */ package cz.cuni.mff.peckam.java.origamist.model; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ResourceBundle; import javax.swing.ImageIcon; import javax.xml.bind.annotation.XmlTransient; import cz.cuni.mff.peckam.java.origamist.configuration.Configuration; import cz.cuni.mff.peckam.java.origamist.exceptions.InvalidOperationException; import cz.cuni.mff.peckam.java.origamist.math.Segment3d; import cz.cuni.mff.peckam.java.origamist.model.jaxb.Operations; import cz.cuni.mff.peckam.java.origamist.modelstate.ModelState; import cz.cuni.mff.peckam.java.origamist.modelstate.arguments.OperationArgument; import cz.cuni.mff.peckam.java.origamist.services.ServiceLocator; import cz.cuni.mff.peckam.java.origamist.services.interfaces.ConfigurationManager; import cz.cuni.mff.peckam.java.origamist.utils.HasBoundProperties; /** * An operation on the model. * <p> * Provides property: icon * * @author Martin Pecka */ @XmlTransient public abstract class Operation extends cz.cuni.mff.peckam.java.origamist.model.jaxb.Operation implements HasBoundProperties { /** The icon property. */ public static final String ICON_PROPERTY = "icon:cz.cuni.mff.peckam.java.origamist.model.Operation"; /** The type property. */ public static final String TYPE_PROPERTY = "type:cz.cuni.mff.peckam.java.origamist.model.Operation"; /** The list of arguments of this operation. */ protected transient List<OperationArgument> arguments = null; /** The bundle for Operation. */ protected transient ResourceBundle messages; /** The name of the resource bundle that contains operations string. */ public static final String BUNDLE_NAME = Operation.class.getName().replaceAll("\\.[^.]*$", "") + ".Operations"; /** * */ public Operation() { ServiceLocator.get(ConfigurationManager.class).get() .addAndRunResourceBundleListener(new Configuration.ResourceBundleLocaleListener(BUNDLE_NAME) { @Override public void bundleChanged() { messages = this.bundle; } }); } /** * @return The icon of this operation */ @XmlTransient public ImageIcon getIcon() { return OperationsHelper.getIcon(type); } @Override public void setType(Operations value) { Operations oldType = this.type; super.setType(value); if (oldType != value) support.firePropertyChange(TYPE_PROPERTY, oldType, value); } /** * Perform folding from the previous state to a new state by this operation. Calling this method will alter the * passed ModelState. * * <p> * Subclasses of {@link Operation} should overwrite this method. They shouldn't call * <code>super.getModelState(previousState)</code>. * * @param previousState The state the model has now. * @return The passed-in state of the model altered by performing this operation. * * @throws InvalidOperationException If the operation cannot be completed. */ public ModelState getModelState(ModelState previousState) throws InvalidOperationException { throw new UnsupportedOperationException("Class " + getClass() + " is a subclass of " + Operation.class + " and therefore must overwrite the getModelState() method."); } /** * @return True if this operation should be skipped for its originating step in delayed operations mode. Only the * operations sign will be added to the originating step. */ public boolean isCompletelyDelayedToNextStep() { return false; } /** * Return a segment with the operation's furthest rotated point as P1 and the center of the operation as P2 (in the * last modelState this operation was applied to). If this method returns <code>null</code>, no specific location is * required. * * @return A segment with the operation's furthest rotated point as P1 and the center of the operation as P2. If * this method returns <code>null</code>, no specific location is required. * * @throws IllegalStateException Can be thrown if this operation hasn't been applied to a modelState yet (but * doesn't have to). */ public Segment3d getMarkerPosition() throws IllegalStateException { return null; } /** * Create and return the list of arguments of this operation. Do not set it directly into this.arguments. */ protected List<OperationArgument> initArguments() { return new ArrayList<OperationArgument>(0); } /** * @return The list of arguments of this operation. The returned list is unmodifiable. */ @XmlTransient public List<OperationArgument> getArguments() { if (arguments == null) { arguments = Collections.unmodifiableList(initArguments()); for (int i = 0; i + 1 < arguments.size(); i++) arguments.get(i).setNext(arguments.get(i + 1)); } return arguments; } /** * @return True if all required arguments of this operation are completely filled-in. */ public boolean areRequiredAgrumentsComplete() { for (OperationArgument arg : getArguments()) { if (!arg.isComplete() && arg.isRequired()) return false; } return true; } /** * @return True if all arguments of this operation are completely filled-in. */ public boolean areAgrumentsComplete() { for (OperationArgument arg : getArguments()) { if (!arg.isComplete()) return false; } return true; } /** * Fill the properties of this operation from this.arguments. * * @throws IllegalStateException If {@link #areRequiredAgrumentsComplete()} returns false. */ public void fillFromArguments() throws IllegalStateException { if (!areRequiredAgrumentsComplete()) throw new IllegalStateException("Cannot fill operation properties from uncompleted argument list."); } /** * @return The name of this operation in the current locale. */ public String getL7dName() { return messages.getString(type.toString()); } /** * Return the text that should be displayed to the user when constructing the given argument. * * @param argument The argument that is constructed (pass only the instances from {@link #getArguments()}). * @return The text to display (may contain HTML). */ public String getL7dUserTip(OperationArgument argument) { return null; } /** * @return The description of the operation. */ public String getDefaultDescription() { return OperationsHelper.toString(type); } /** * Return the description of this operation while it is being constructed. * * @param currentArgument The argument the user currently sets. * @return The description. */ public String getConstructDescription(OperationArgument currentArgument) { StringBuilder text = new StringBuilder("<html><body><span style=\"color:gray;\">").append( OperationsHelper.toString(type)).append("</span>"); if (getArguments().size() > 0) { text.append("<ol style=\"margin: 0px; margin-left: 18px;\">"); String currentArgStyle = "font-weight: bold; font-size: 120%; background-color: rgb(240,240,255);"; String optionalArgStyle = "font-style: italic;"; String completedArgStyle = "color: black; font-weight: bold;"; for (OperationArgument arg : getArguments()) { text.append("<li><span style=\"font-size: 90%; color: gray; font-weight: normal;"); if (currentArgument == arg) text.append(currentArgStyle); if (!arg.isRequired()) text.append(optionalArgStyle); if (arg.isComplete()) text.append(completedArgStyle); text.append("\">"); if (currentArgument == arg) text.append(" > "); text.append( ResourceBundle.getBundle("editor", ServiceLocator.get(ConfigurationManager.class).get().getLocale()).getString( arg.getResourceBundleKey())).append(" </span></li>"); } text.append("</ol>"); } text.append("</body></html>"); return text.toString(); } }