ghidra-master/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/translate/ManualStringTranslationService.java
/* ###
 * IP: GHIDRA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ghidra.app.plugin.core.string.translate;

import static ghidra.program.model.data.TranslationSettingsDefinition.*;

import java.util.List;

import org.apache.commons.lang3.StringUtils;

import docking.widgets.OptionDialog;
import ghidra.app.services.StringTranslationService;
import ghidra.program.model.data.DataUtilities;
import ghidra.program.model.data.StringDataInstance;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.util.HelpLocation;
import ghidra.util.task.TaskLauncher;

/**
 * This class allows users to manually translate strings.
 */
public class ManualStringTranslationService implements StringTranslationService {

	private static final int MAX_STR_PROMPT = 30;

	@Override
	public String getTranslationServiceName() {
		return "Manual";
	}

	@Override
	public HelpLocation getHelpLocation() {
		return StringTranslationService.createStringTranslationServiceHelpLocation(
			TranslateStringsPlugin.class, this);
	}

	@Override
	public void translate(Program program, List<ProgramLocation> stringLocations) {
		TaskLauncher.launchModal("Manually translate strings", monitor -> {

			int id = program.startTransaction("Translate strings");
			try {
				for (int instanceNum = 0; instanceNum < stringLocations.size(); instanceNum++) {
					ProgramLocation progLoc = stringLocations.get(instanceNum);
					Data data = DataUtilities.getDataAtLocation(progLoc);
					StringDataInstance stringData = StringDataInstance.getStringDataInstance(data);
					String strValue = stringData.getStringValue();
					if (strValue != null) {
						strValue = strValue.length() > MAX_STR_PROMPT
								? strValue.substring(0, MAX_STR_PROMPT) + "..."
								: strValue;
						String previous =
							StringUtils.defaultString(stringData.getTranslatedValue());
						String translatedValue = OptionDialog.showInputSingleLineDialog(null,
							"Translate " + (instanceNum + 1) + " of " + stringLocations.size(),
							"Translate \"" + strValue + "\"", previous);
						if (translatedValue == null) {
							break;
						}
						if (!translatedValue.trim().isEmpty()) {
							TRANSLATION.setTranslatedValue(data, translatedValue);
							TRANSLATION.setShowTranslated(data, true);
						}
					}
				}
			}
			finally {
				program.endTransaction(id, true);
			}
		});
	}

	/**
	 * Helper method called by Defined String table model to set the value for a single item.
	 * <p>
	 * This method is here to keep it adjacent to the manual string translation logic.
	 *
	 * @param program current {@link Program}
	 * @param stringLocation {@link ProgramLocation} of the string to set new translation
	 * @param newValue String manual translated value
	 */
	public static void setTranslatedValue(Program program, ProgramLocation stringLocation,
			String newValue) {

		Data data = DataUtilities.getDataAtLocation(stringLocation);
		StringDataInstance sdi = StringDataInstance.getStringDataInstance(data);

		int id = program.startTransaction("Set string translated value");
		try {
			// remove the translation settings if the new value is empty or exactly equal to the
			// actual string data instance value.
			if (newValue.isEmpty() || newValue.equals(sdi.getStringValue())) {
				TRANSLATION.clear(data);
				TRANSLATION.setTranslatedValue(data, null);
			}
			else {
				TRANSLATION.setTranslatedValue(data, newValue);
				TRANSLATION.setShowTranslated(data, true);
			}
		}
		finally {
			program.endTransaction(id, true);
		}

	}

}
