Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix a parsing error and turns it into a singleton using enum #6600

Merged
merged 3 commits into from
Feb 24, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 34 additions & 77 deletions megamek/src/megamek/client/generator/RandomCallsignGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,126 +18,83 @@
*/
package megamek.client.generator;

import megamek.MMConstants;
import megamek.common.util.weightedMaps.WeightedIntMap;
import megamek.logging.MMLogger;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

import megamek.MMConstants;
import megamek.common.util.weightedMaps.WeightedIntMap;
import megamek.logging.MMLogger;

/**
* Save File Formatting:
* callsign, weight
* Callsign is a String that does not include a ','
* Weight is an integer weight that is used during generation
*/
public class RandomCallsignGenerator implements Serializable {
public class RandomCallsignGenerator {
private final static RandomCallsignGenerator INSTANCE = new RandomCallsignGenerator();
private final static MMLogger logger = MMLogger.create(RandomCallsignGenerator.class);

// region Variable Declarations
private static final long serialVersionUID = 4721410214327210288L;

private static WeightedIntMap<String> weightedCallsigns;

private static RandomCallsignGenerator rcg;
private final WeightedIntMap<String> weightedCallsigns = new WeightedIntMap<>();

private static volatile boolean initialized = false; // volatile to ensure readers get the current version
// endregion Variable Declarations
RandomCallsignGenerator() {
final Map<String, Integer> callsigns = new HashMap<>();
loadCallsignsFromFile(new File(MMConstants.CALLSIGN_FILE_PATH), callsigns);
loadCallsignsFromFile(new File(MMConstants.USER_CALLSIGN_FILE_PATH), callsigns);

// region Constructors
protected RandomCallsignGenerator() {
for (final Map.Entry<String, Integer> entry : callsigns.entrySet()) {
getWeightedCallsigns().add(entry.getValue(), entry.getKey());
}
}

public static RandomCallsignGenerator getInstance() {
return INSTANCE;
}
// endregion Constructors

// region Getters/Setters
public static WeightedIntMap<String> getWeightedCallsigns() {
public WeightedIntMap<String> getWeightedCallsigns() {
return weightedCallsigns;
}

public static void setWeightedCallsigns(final WeightedIntMap<String> weightedCallsigns) {
RandomCallsignGenerator.weightedCallsigns = weightedCallsigns;
}
// endregion Getters/Setters

// region Synchronization
/**
* @return the instance of the RandomCallsignGenerator to use
*/
public static synchronized RandomCallsignGenerator getInstance() {
// only this code reads and writes 'rcg'
if (rcg == null) {
// synchronized ensures this will only be entered exactly once
rcg = new RandomCallsignGenerator();
rcg.runThreadLoader();
}
// when getInstance returns, rcg will always be non-null
return rcg;
}
// endregion Synchronization

// region Generation
public String generate() {
String callsign = "";

if (initialized) {
callsign = getWeightedCallsigns().randomItem();
} else {
logger.warn("Attempted to generate a callsign before the list was initialized.");
}

return callsign;
return getWeightedCallsigns().randomItem();
}
// endregion Generation

// region Initialization
private void runThreadLoader() {
Thread loader = new Thread(() -> rcg.populateCallsigns(), "Random Callsign Generator initializer");
loader.setPriority(Thread.NORM_PRIORITY - 1);
loader.start();
}

private void populateCallsigns() {
setWeightedCallsigns(new WeightedIntMap<>());
final Map<String, Integer> callsigns = new HashMap<>();
loadCallsignsFromFile(new File(MMConstants.CALLSIGN_FILE_PATH), callsigns);
loadCallsignsFromFile(new File(MMConstants.USER_CALLSIGN_FILE_PATH), callsigns);

for (final Map.Entry<String, Integer> entry : callsigns.entrySet()) {
getWeightedCallsigns().add(entry.getValue(), entry.getKey());
}

initialized = true;
}

private void loadCallsignsFromFile(final File file, final Map<String, Integer> callsigns) {
private void loadCallsignsFromFile(File file, Map<String, Integer> callsigns) {
if (!file.exists()) {
return;
}

int lineNumber = 0;

try (InputStream is = new FileInputStream(file);
Scanner input = new Scanner(is, StandardCharsets.UTF_8.name())) {
Scanner input = new Scanner(is, StandardCharsets.UTF_8)) {
// skip the first line, as that's the header
lineNumber++;
input.nextLine();

while (input.hasNextLine()) {
lineNumber++;
String[] values = input.nextLine().split(",");
if (values.length == 2) {
callsigns.put(values[0], Integer.parseInt(values[1]));
} else if (values.length < 2) {
logger.error("Not enough fields in " + file + " on " + lineNumber);
} else {
logger.error("Too many fields in " + file + " on " + lineNumber);
String line = input.nextLine();
int lastCommaIndex = line.lastIndexOf(",");
if (lastCommaIndex == -1 || line.length() == lastCommaIndex + 1) {
logger.debug("Not enough fields in {} on {}",file, lineNumber);
continue;
}

String[] values = {line.substring(0, lastCommaIndex), line.substring(lastCommaIndex + 1)};

try {
callsigns.put(values[0], Integer.parseInt(values[1].trim()));
} catch (NumberFormatException e) {
logger.warn("Invalid weight in {} on {}", file, lineNumber);
}
}
} catch (Exception e) {
Expand Down