Skip to content

Commit 96fd096

Browse files
committed
#85: disambiguate model labels for dialog and document panes
Generate human-readable model labels with observation count and creation time, use them as stable per-model keys, and feed the unique label into phased keys so same-description models coexist and appear distinctly in the Models dialog. Made-with: Cursor
1 parent 0275e8f commit 96fd096

2 files changed

Lines changed: 67 additions & 7 deletions

File tree

src/org/aavso/tools/vstar/ui/dialog/model/ModelDialog.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,8 @@ public Listener<ModelCreationMessage> createModelCreationListener() {
210210
return new Listener<ModelCreationMessage>() {
211211
@Override
212212
public void update(ModelCreationMessage info) {
213-
String desc = info.getModel().getDescription();
213+
String desc = Mediator.getInstance().getDocumentManager()
214+
.getModelDisplayLabel(info.getModel());
214215

215216
if (!modelMap.containsKey(desc)) {
216217
modelMap.put(desc, info.getModel());

src/org/aavso/tools/vstar/ui/mediator/DocumentManager.java

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
package org.aavso.tools.vstar.ui.mediator;
1919

2020
import java.awt.Window;
21+
import java.time.LocalTime;
22+
import java.time.format.DateTimeFormatter;
2123
import java.util.HashMap;
24+
import java.util.IdentityHashMap;
2225
import java.util.Map;
2326
import java.util.TreeMap;
2427

@@ -46,6 +49,7 @@
4649
*/
4750
@SuppressWarnings("serial")
4851
public class DocumentManager {
52+
private static final DateTimeFormatter MODEL_LABEL_TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
4953

5054
private Mediator mediator;
5155

@@ -55,6 +59,7 @@ public class DocumentManager {
5559

5660
private Map<String, SyntheticObservationListPane<AbstractModelObservationTableModel>> rawDataResidualComponents;
5761
private Map<String, SyntheticObservationListPane<AbstractModelObservationTableModel>> phasedResidualComponents;
62+
private Map<IModel, String> uniqueModelKeys;
5863

5964
private boolean phasePlotExists;
6065
private double epoch;
@@ -163,7 +168,8 @@ private void togglePlotControlState(Map<AnalysisType, Boolean> map) {
163168
public SyntheticObservationListPane<AbstractModelObservationTableModel> getModelListPane(AnalysisType type,
164169
IModel model) {
165170
SyntheticObservationListPane<AbstractModelObservationTableModel> pane = null;
166-
String key = model.getDescription();
171+
String modelKey = getOrCreateUniqueModelKey(model);
172+
String key = modelKey;
167173

168174
switch (type) {
169175
case RAW_DATA:
@@ -183,7 +189,7 @@ public SyntheticObservationListPane<AbstractModelObservationTableModel> getModel
183189
break;
184190

185191
case PHASE_PLOT:
186-
key = getPhasedModelKey(model);
192+
key = getPhasedModelKey(modelKey);
187193

188194
if (!phasedModelComponents.containsKey(key)) {
189195
// Set the fit list's phases according to the last phase change.
@@ -211,7 +217,8 @@ public SyntheticObservationListPane<AbstractModelObservationTableModel> getModel
211217
public SyntheticObservationListPane<AbstractModelObservationTableModel> getResidualsListPane(AnalysisType type,
212218
IModel model) {
213219
SyntheticObservationListPane<AbstractModelObservationTableModel> pane = null;
214-
String key = model.getDescription();
220+
String modelKey = getOrCreateUniqueModelKey(model);
221+
String key = modelKey;
215222

216223
switch (type) {
217224
case RAW_DATA:
@@ -229,7 +236,7 @@ public SyntheticObservationListPane<AbstractModelObservationTableModel> getResid
229236
break;
230237

231238
case PHASE_PLOT:
232-
key = getPhasedModelKey(model);
239+
key = getPhasedModelKey(modelKey);
233240

234241
if (!phasedResidualComponents.containsKey(key)) {
235242
// Set the residual list's phases according to the last phase
@@ -324,8 +331,55 @@ public String getNextUntitledFilterName() {
324331
* @param model The model whose description we will use as part of the key.
325332
* @return The unique key from the tuple: <description, epoch, period>.
326333
*/
327-
private String getPhasedModelKey(IModel model) {
328-
return String.format("%s:e=%f,p=%f", model.getDescription(), epoch, period);
334+
private String getPhasedModelKey(String modelKey) {
335+
return String.format("%s:e=%f,p=%f", modelKey, epoch, period);
336+
}
337+
338+
/**
339+
* Return a stable unique key for this model instance for the life of the active
340+
* document. Keys are derived from model description, but disambiguated on
341+
* conflicts via " #N" suffixes to preserve models with identical descriptions.
342+
*/
343+
private String getOrCreateUniqueModelKey(IModel model) {
344+
if (uniqueModelKeys.containsKey(model)) {
345+
return uniqueModelKeys.get(model);
346+
}
347+
348+
String baseKey = buildModelLabel(model);
349+
String key = baseKey;
350+
int suffix = 2;
351+
352+
while (isModelKeyInUse(key)) {
353+
key = String.format("%s #%d", baseKey, suffix++);
354+
}
355+
356+
uniqueModelKeys.put(model, key);
357+
return key;
358+
}
359+
360+
private boolean isModelKeyInUse(String key) {
361+
return uniqueModelKeys.containsValue(key) || rawDataModelComponents.containsKey(key)
362+
|| rawDataResidualComponents.containsKey(key)
363+
|| phasedModelComponents.containsKey(getPhasedModelKey(key))
364+
|| phasedResidualComponents.containsKey(getPhasedModelKey(key));
365+
}
366+
367+
/**
368+
* Build a human-readable label for model and residual document components.
369+
* Includes model description, observation count, and creation time to reduce
370+
* collisions while preserving user context.
371+
*/
372+
private String buildModelLabel(IModel model) {
373+
int obsCount = model.getFit() == null ? 0 : model.getFit().size();
374+
String time = LocalTime.now().format(MODEL_LABEL_TIME_FORMATTER);
375+
return String.format("%s (%d obs, %s)", model.getDescription(), obsCount, time);
376+
}
377+
378+
/**
379+
* Return the display label/key for this model instance.
380+
*/
381+
public String getModelDisplayLabel(IModel model) {
382+
return getOrCreateUniqueModelKey(model);
329383
}
330384

331385
/**
@@ -357,6 +411,11 @@ public void init() {
357411
}
358412
phasedResidualComponents.clear();
359413

414+
if (uniqueModelKeys == null) {
415+
uniqueModelKeys = new IdentityHashMap<IModel, String>();
416+
}
417+
uniqueModelKeys.clear();
418+
360419
// Boolean maps
361420
if (showErrorBars == null) {
362421
showErrorBars = new HashMap<AnalysisType, Boolean>();

0 commit comments

Comments
 (0)