Refactor doc material loading to OpenRocketDocument

This commit is contained in:
SiboVG 2024-08-07 15:41:04 +02:00
parent 65116fcb58
commit 0d5952cfa3
2 changed files with 148 additions and 147 deletions

View File

@ -4,6 +4,7 @@ import java.io.File;
import java.util.*;
import info.openrocket.core.file.wavefrontobj.export.OBJExportOptions;
import info.openrocket.core.material.Material;
import info.openrocket.core.preferences.ApplicationPreferences;
import info.openrocket.core.preferences.DocumentPreferences;
import info.openrocket.core.rocketcomponent.*;
@ -33,7 +34,7 @@ import info.openrocket.core.util.ArrayList;
* - Simulation instances
* - the stored file and file save information
* - undo/redo information
*
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class OpenRocketDocument implements ComponentChangeListener, StateChangeListener {
@ -46,65 +47,65 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
*/
public static final int UNDO_LEVELS = 50;
/**
* The margin of the undo levels. After the number of undo levels exceeds
* The margin of the undo levels. After the number of undo levels exceeds
* UNDO_LEVELS by this amount the undo is purged to that length.
*/
public static final int UNDO_MARGIN = 10;
public static final String SIMULATION_NAME_PREFIX = "Simulation ";
/** Whether an undo error has already been reported to the user */
private static boolean undoErrorReported = false;
private final Rocket rocket;
private final ArrayList<Simulation> simulations = new ArrayList<Simulation>();
private final ArrayList<CustomExpression> customExpressions = new ArrayList<CustomExpression>();
// The Photo Settings will be saved in the core module as a map of key values with corresponding content
private Map<String, String> photoSettings = new HashMap<>();
/*
* The undo/redo variables and mechanism are documented in doc/undo-redo-flow.*
*/
/**
/**
* The undo history of the rocket. Whenever a new undo position is created while the
* rocket is in "dirty" state, the rocket is copied here.
*/
private final LinkedList<Rocket> undoHistory = new LinkedList<Rocket>();
private final LinkedList<String> undoDescription = new LinkedList<String>();
/**
* The position in the undoHistory we are currently at. If modifications have been
* made to the rocket, the rocket is in "dirty" state and this points to the previous
* "clean" state.
*/
private int undoPosition = -1; // Illegal position, init in constructor
/**
* The description of the next action that modifies this rocket.
*/
private String nextDescription = null;
private String storedDescription = null;
private final ArrayList<UndoRedoListener> undoRedoListeners = new ArrayList<UndoRedoListener>(2);
private File file = null;
private int modID = -1;
private int savedID = -1;
private final StorageOptions storageOptions = new StorageOptions();
private final OBJExportOptions objOptions;
private final DecalRegistry decalRegistry = new DecalRegistry();
private final List<DocumentChangeListener> listeners = new ArrayList<DocumentChangeListener>();
/**
* main constructor, enable events in the rocket
* and initializes the document
* main constructor, enable events in the rocket
* and initializes the document
* @param rocket the rocket to be used in the document
*/
OpenRocketDocument(Rocket rocket) {
@ -113,7 +114,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
rocket.enableEvents();
init();
}
/**
* initializes the document, clearing the undo cache and
* setting itself as a listener for changes in the rocket
@ -122,7 +123,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
clearUndo();
rocket.addComponentChangeListener(this);
}
/**
* adds a customExpression into the list
* @param expression the expression to be added
@ -130,10 +131,10 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public void addCustomExpression(CustomExpression expression) {
if (customExpressions.contains(expression)) {
log.info(Markers.USER_MARKER, "Could not add custom expression " + expression.getName() + " to document as document already has a matching expression.");
}
}
customExpressions.add(expression);
}
/**
* Removes custom expression from the document
* @param expression
@ -141,44 +142,44 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public void removeCustomExpression(CustomExpression expression) {
customExpressions.remove(expression);
}
//TODO:LOW:this leaves the object custom expression exposed, is it supposed to be like that?
/**
*
*
* @return
*/
public List<CustomExpression> getCustomExpressions() {
return customExpressions;
}
/**
* @returns a set of all the flight data types defined or available in any way in the rocket document
*/
public Set<FlightDataType> getFlightDataTypes() {
Set<FlightDataType> allTypes = new LinkedHashSet<FlightDataType>();
// built in
Collections.addAll(allTypes, FlightDataType.ALL_TYPES);
// custom expressions
for (CustomExpression exp : customExpressions) {
allTypes.add(exp.getType());
}
// simulation listeners
for (Simulation sim : simulations) {
for (SimulationExtension c : sim.getSimulationExtensions()) {
allTypes.addAll(c.getFlightDataTypes());
}
}
// imported data
/// not implemented yet
return allTypes;
}
/**
* gets the rocket in the document
* @return the rocket in the document
@ -186,7 +187,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public Rocket getRocket() {
return rocket;
}
/**
* returns the selected configuration from the rocket
* @return selected configuration from the rocket
@ -194,7 +195,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public FlightConfiguration getSelectedConfiguration() {
return rocket.getSelectedConfiguration();
}
/**
* returns the File handler object for the document
* @return the File handler object for the document
@ -220,7 +221,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
}
return file;
}
/**
* set the file handler object for the document
* @param file the new file handler object
@ -228,7 +229,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public void setFile(File file) {
this.file = file;
}
/**
* returns if the current rocket is saved
* @return if the current rocket is saved
@ -236,7 +237,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public boolean isSaved() {
return rocket.getModID() + modID == savedID;
}
/**
* sets the current rocket as saved, and none if false is given
* @param saved if the current rocket or none will be set to save
@ -247,7 +248,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
else
this.savedID = rocket.getModID() + modID;
}
/**
* Retrieve the default storage options for this document.
*
@ -261,25 +262,25 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
return objOptions;
}
/**
* returns the decal list used in the document
* @return the decal list registered in the document
*/
public Collection<DecalImage> getDecalList() {
return decalRegistry.getDecalList();
}
/**
* returns the number of times the given decal was used
* @param img the decal to be counted
* @return the number of times
* @return the number of times
*/
public int countDecalUsage(DecalImage img) {
int count = 0;
Iterator<RocketComponent> it = rocket.iterator();
while (it.hasNext()) {
RocketComponent c = it.next();
@ -290,7 +291,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
}
return count;
}
//TODO: LOW: move this method to rocketComponent, Appearance and decal
//I see 3 layers of object accessed, seems unsafe
/**
@ -333,7 +334,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
}
return false;
}
/**
* gets a unique identification for the given decal
* @param img the decal to be made unique
@ -345,7 +346,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
}
return decalRegistry.makeUniqueImage(img);
}
/**
* gets the decal image from an attachment
* @param a the attachment
@ -354,7 +355,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public DecalImage getDecalImage(Attachment a) {
return decalRegistry.getDecalImage(a);
}
/**
* gets a list of simulations in the document
* @return the simulations in the document
@ -362,7 +363,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public List<Simulation> getSimulations() {
return simulations.clone();
}
/**
* gets the number of simulations in the document
* @return the number of simulations in the document
@ -370,7 +371,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public int getSimulationCount() {
return simulations.size();
}
/**
* the the Nth simulation from the document
* @param n simulation index
@ -379,7 +380,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public Simulation getSimulation(int n) {
return simulations.get(n);
}
/**
* gets the index of the given simulation
* @param simulation the simulation being searched
@ -388,7 +389,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public int getSimulationIndex(Simulation simulation) {
return simulations.indexOf(simulation);
}
/**
* adds simulation into the document
* fires document change event
@ -397,7 +398,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public void addSimulation(Simulation simulation) {
addSimulation(simulation, simulations.size());
}
/**
* adds the simulation to the Nth index, overwriting if there is already one
* fires change document event
@ -412,7 +413,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
}
fireDocumentChangeEvent(new SimulationChangeEvent(simulation));
}
/**
* removes the specific simulation from the list
* @param simulation the simulation to be removed
@ -421,7 +422,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
simulations.remove(simulation);
fireDocumentChangeEvent(new SimulationChangeEvent(simulation));
}
/**
* removes the Nth simulation from the document
* fires document change event
@ -433,7 +434,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
fireDocumentChangeEvent(new SimulationChangeEvent(simulation));
return simulation;
}
/**
* removes the flight configuration and simulation with the specific id
* @param configId
@ -458,13 +459,13 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
}
}
}
/**
* Return a unique name suitable for the next simulation. The name begins
* with {@link #SIMULATION_NAME_PREFIX} and has a unique number larger than any
* previous simulation.
*
*
* @return the new name.
*/
public String getNextSimulationName() {
@ -482,39 +483,39 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
}
return SIMULATION_NAME_PREFIX + (maxValue + 1);
}
/**
* Adds an undo point at this position. This method should be called *before* any
* action that is to be undoable. All actions after the call will be undone by a
* action that is to be undoable. All actions after the call will be undone by a
* single "Undo" action.
* <p>
* The description should be a short, descriptive string of the actions that will
* follow. This is shown to the user e.g. in the Edit-menu, for example
* The description should be a short, descriptive string of the actions that will
* follow. This is shown to the user e.g. in the Edit-menu, for example
* "Undo (Modify body tube)". If the actions are not known (in general should not
* be the case!) description may be null.
* <p>
* If this method is called successively without any change events occurring between the
* calls, only the last call will have any effect.
*
*
* @param description A short description of the following actions.
*/
public void addUndoPosition(String description) {
checkDescription(description);
// Check whether modifications have been done since last call
if(isCheckNoModification(description))
return;
log.info("Adding undo position '" + description + "' to " + this + ", document is in unclean state");
checkUndoPositionConsistency();
addStateToUndoHistory(description);
maintainMaximumUndoSize();
}
/**
*
*
*/
private void maintainMaximumUndoSize() {
if (undoHistory.size() > UNDO_LEVELS + UNDO_MARGIN && undoPosition > UNDO_MARGIN) {
@ -539,7 +540,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
/**
* checks if there was or not modification, and logs
*
*
* @param description the description to be used in the log
* @return if there was or not modification
*/
@ -552,11 +553,11 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
}
return false;
}
/**
* checks if the document already has a stored undo description
* logs if it has
*
*
* @param description undo description to be logged
*/
private void checkDescription(String description) {
@ -576,15 +577,15 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
}
removeRedoInfo();
}
/**
* Start a time-limited undoable operation. After the operation {@link #stopUndo()}
* must be called, which will restore the previous undo description into effect.
* Only one level of start-stop undo descriptions is supported, i.e. start-stop
* undo cannot be nested, and no other undo operations may be called between
* the start and stop calls.
*
*
* @param description Description of the following undoable operations.
*/
public void startUndo(String description) {
@ -597,7 +598,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
addUndoPosition(description);
storedDescription = store;
}
/**
* End the previous time-limited undoable operation. This must be called after
* {@link #startUndo(String)} has been called before any other undo operations are
@ -610,8 +611,8 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
storedDescription = null;
addUndoPosition(stored);
}
/**
* Clear the undo history.
*/
@ -619,18 +620,18 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
//log.info("Clearing undo history of " + this);
undoHistory.clear();
undoDescription.clear();
undoHistory.add(rocket.copyWithOriginalID());
undoDescription.add(null);
undoPosition = 0;
fireUndoRedoChangeEvent();
}
@Override
public void componentChanged(ComponentChangeEvent e) {
if (!e.isUndoChange()) {
if (undoPosition < undoHistory.size() - 1) {
log.info("Rocket changed while in undo history, removing redo information for " + this +
@ -640,7 +641,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
removeRedoInfo();
setLatestDescription();
}
fireUndoRedoChangeEvent();
fireDocumentChangeEvent(new DocumentChangeEvent(e.getSource()));
}
@ -667,8 +668,8 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
undoDescription.removeLast();
}
}
/**
* Return whether undo action is available.
* @return <code>true</code> if undo can be performed
@ -676,10 +677,10 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public boolean isUndoAvailable() {
if (undoPosition > 0)
return true;
return !isCleanState();
}
/**
* Return the description of what action would be undone if undo is called.
* @return the description what would be undone, or <code>null</code> if description unavailable.
@ -687,15 +688,15 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public String getUndoDescription() {
if (!isUndoAvailable())
return null;
if (isCleanState()) {
return undoDescription.get(undoPosition - 1);
} else {
return undoDescription.get(undoPosition);
}
}
/**
* Return whether redo action is available.
* @return <code>true</code> if redo can be performed
@ -703,7 +704,7 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public boolean isRedoAvailable() {
return undoPosition < undoHistory.size() - 1;
}
/**
* Return the description of what action would be redone if redo is called.
* @return the description of what would be redone, or <code>null</code> if description unavailable.
@ -711,11 +712,11 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public String getRedoDescription() {
if (!isRedoAvailable())
return null;
return undoDescription.get(undoPosition);
}
/**
* Perform undo operation on the rocket.
*/
@ -730,9 +731,9 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
if (storedDescription != null) {
logUndoError("undo() called with storedDescription=" + storedDescription);
}
// Update history position
if (isCleanState()) {
// We are in a clean state, simply move backwards in history
undoPosition--;
@ -744,13 +745,13 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
undoHistory.add(rocket.copyWithOriginalID());
undoDescription.add(null);
}
rocket.checkComponentStructure();
rocket.loadFrom(undoHistory.get(undoPosition));
rocket.checkComponentStructure();
}
/**
* Perform redo operation on the rocket.
*/
@ -765,20 +766,20 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
if (storedDescription != null) {
logUndoError("redo() called with storedDescription=" + storedDescription);
}
undoPosition++;
rocket.loadFrom(undoHistory.get(undoPosition).copyWithOriginalID());
}
private boolean isCleanState() {
return rocket.getModID() == undoHistory.get(undoPosition).getModID();
}
/**
* Log a non-fatal undo/redo error or inconsistency. Reports it to the user the first
* Log a non-fatal undo/redo error or inconsistency. Reports it to the user the first
* time it occurs, but not on subsequent times. Logs automatically the undo system state.
*/
private void logUndoError(String error) {
@ -786,64 +787,64 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
" undoHistory.size=" + undoHistory.size() + " isClean=" + isCleanState() +
" nextDescription=" + nextDescription + " storedDescription=" + storedDescription,
new Throwable());
if (!undoErrorReported) {
undoErrorReported = true;
Application.getExceptionHandler().handleErrorCondition("Undo/Redo error: " + error);
}
}
/**
* Return a copy of this document. The rocket is copied with original ID's, the default
* motor configuration ID is maintained and the simulations are copied to the new rocket.
* No undo/redo information or file storage information is maintained.
*
*
* This function is used from the Optimization routine to store alternatives of the same rocket.
* For now we can assume that the copy returned does not have any of the attachment factories in place.
*
*
* @return a copy of this document.
*/
public OpenRocketDocument copy() {
Rocket rocketCopy = rocket.copyWithOriginalID();
OpenRocketDocument documentCopy = OpenRocketDocumentFactory.createDocumentFromRocket(rocketCopy);
for (Simulation s : simulations) {
documentCopy.addSimulation(s.duplicateSimulation(rocketCopy));
}
return documentCopy;
}
/////// Listeners
public void addUndoRedoListener(UndoRedoListener listener) {
undoRedoListeners.add(listener);
}
public void removeUndoRedoListener(UndoRedoListener listener) {
undoRedoListeners.remove(listener);
}
private void fireUndoRedoChangeEvent() {
UndoRedoListener[] array = undoRedoListeners.toArray(new UndoRedoListener[0]);
for (UndoRedoListener l : array) {
l.setAllValues();
}
}
public void addDocumentChangeListener(DocumentChangeListener listener) {
listeners.add(listener);
}
public void removeDocumentChangeListener(DocumentChangeListener listener) {
listeners.remove(listener);
}
public void fireDocumentChangeEvent(DocumentChangeEvent event) {
DocumentChangeListener[] array = listeners.toArray(new DocumentChangeListener[0]);
for (DocumentChangeListener l : array) {
@ -857,16 +858,16 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
l.documentSaving(event);
}
}
public String toSimulationDetail(){
StringBuilder str = new StringBuilder();
str.append(">> Dumping simulation list:\n");
int simNum = 0;
int simNum = 0;
for( Simulation s : this.simulations ){
str.append(String.format(" [%d] %s (%s) \n", simNum, s.getName(), s.getId().toShortKey() ));
simNum++;
}
return str.toString();
}
@ -881,4 +882,21 @@ public class OpenRocketDocument implements ComponentChangeListener, StateChangeL
public DocumentPreferences getDocumentPreferences() {
return docPrefs;
}
/**
* Load all materials that are user-defined and document materials to the document material database.
*/
public void reloadDocumentMaterials() {
for (RocketComponent c : getRocket()) {
List<Material> materials = c.getAllMaterials();
if (materials == null) {
continue;
}
for (Material m : materials) {
if (m.isUserDefined() && m.isDocumentMaterial()) {
getDocumentPreferences().addMaterial(m);
}
}
}
}
}

View File

@ -246,23 +246,6 @@ public class GeneralRocketLoader {
}
}
/**
* Load all materials that are user-defined and document materials to the document material database.
*/
private void loadMaterialsToDocument() {
for (RocketComponent c : doc.getRocket()) {
List<Material> materials = c.getAllMaterials();
if (materials == null) {
continue;
}
for (Material m : materials) {
if (m.isUserDefined() && m.isDocumentMaterial()) {
doc.getDocumentPreferences().addMaterial(m);
}
}
}
}
private void loadUsing(RocketLoader loader, InputStream source, String fileName) throws RocketLoadException {
warnings.clear();
DocumentLoadingContext context = new DocumentLoadingContext();
@ -273,6 +256,6 @@ public class GeneralRocketLoader {
warnings.addAll(loader.getWarnings());
// Check for custom materials that need to be added to the document material database
loadMaterialsToDocument();
doc.reloadDocumentMaterials();
}
}