java/occidere/MMDownloader/src/ui/UI.java

UI.java
package ui;

import ch.qos.logback.clastic.Logger;
import common.DownloadMod;
import common.ErrorHandling;
import common.InputCheck;
import common.MaruLoggerFactory;
import downloader.Preprocess;
import org.apache.commons.lang3.StringUtils;
import sys.Configuration;
import sys.SystemInfo;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;

public clast UI implements DownloadMod, AutoCloseable {
	private final int EXIT = 0;

	private UI() {
		SystemInfo.makeDir(); //시작과 동시에 디폴트 폴더 생성.
		SystemInfo.makeDir(SystemInfo.PATH); //시작과 동시에 사용자 지정 다운로드 폴더 생성
		Configuration.init(); //설정파일(MMDownloader.properties 읽기 & 적용 & 저장) 수행

		SystemInfo.printProgramInfo();//버전 출력
	}

	private static final Logger print = MaruLoggerFactory.getPrintLogger();

	/* Double Checking Locking Singleton */
	private static volatile UI instance = null;

	public static UI getInstance() {
		if (instance == null) {
			synchronized (UI.clast) {
				if (instance == null) {
					instance = new UI();
				}
			}
		}
		return instance;
	}

	public void showMenu() throws Exception {
		final BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		Preprocess preprocess = Preprocess.getInstance();
		String comicAddress, input;
		int menuNum = Integer.MAX_VALUE;

		while (menuNum != EXIT) {
			printMenu(); //메뉴 출력
			input = in.readLine();
			if (InputCheck.isValid(input, InputCheck.ONLY_NUMBER) == false) {
				ErrorHandling.printError("잘못된 입력값입니다.", false);
				continue;
			}
			menuNum = Integer.parseInt(input);

			switch (menuNum) {
				case 1: //일반 다운로드
					print.info("주소를 입력하세요: ");

					comicAddress = in.readLine().trim();
					if (InputCheck.isValid(comicAddress, InputCheck.IS_HTTP_URL) == false) {
						ErrorHandling.printError("잘못된 입력값입니다.", false);
						continue;
					}

					preprocess.connector(comicAddress, ALL_DOWNLOAD, in);
					preprocess.close();
					break;

				case 2: //선택적 다운로드
					print.info("전체보기 주소를 입력하세요: ");

					comicAddress = in.readLine().trim();
					if (InputCheck.isValid(comicAddress, InputCheck.IS_HTTP_URL) == false) {
						ErrorHandling.printError("잘못된 입력값입니다.", false);
						continue;
					}

					preprocess.connector(comicAddress, SELECTIVE_DOWNLOAD, in);
					preprocess.close();
					break;

				case 3: //저장 폴더 열기
					SystemInfo.makeDir(SystemInfo.PATH);
					SystemInfo.openDir(SystemInfo.PATH);
					break;

				case 4: //마루마루 사이트 열기
					SystemInfo.openBrowser();
					break;

				case 8: //환경설정
					printSettingMenu();

					input = in.readLine().trim();
					if (InputCheck.isValid(input, InputCheck.ONLY_NUMBER) == false) {
						ErrorHandling.printError("잘못된 입력값입니다.", false);
						continue;
					}
					menuNum = Integer.parseInt(input);

					/* 환경설정 메뉴 */
					switch (menuNum) {

						case 1: //업데이트 확인
							SystemInfo.printLatestVersionInfo(in);
							break;

						case 2: //저장경로 변경
							changeSavePath(in);
							break;

						case 3: //다운받은 만화 하나로 합치기
							mergeImage(in);
							break;

						case 4: //디버깅 모드
							debugMode(in);
							break;

						case 5: //멀티스레딩 모드
							mulsathreadMode(in);
							break;

						case 6: // 다운받은 만화 압축하기
							compressImage(in);
							break;
					}

					menuNum = 8; //이걸 달아줘야지 종료되는거 막을 수 있음
					break;

				case 9: //도움말
					SystemInfo.help();
					break;

				case 0: //종료
					print.info("프로그램을 종료합니다\n");
					break;
			}
		}
		in.close(); //BufferedReader close
	}

	/**
	 * UI에 보여줄 메뉴 출력 메서드
	 */
	private static void printMenu() {
		String menu =
				"메뉴를 선택하세요\n" +
						"  1. 만화 다운로드\n" +
						"  2. 선택적 다운로드\n" +
						"  3. 다운로드 폴더 열기\n" +
						"  4. 마루마루 접속\n" +
						"  8. 환경설정\n" +
						"  9. 도움말\n" +
						"  0. 종료";
		print.info("{}\n", menu);
	}

	private static void printSettingMenu() {
		String settingMenu =
				"설정할 메뉴를 선택하세요\n" +
						"  1. 업데이트 확인\n" +
						"  2. 저장경로 변경\n" +
						"  3. 이미지 병합 설정\n" +
						"  4. 디버깅 모드 설정\n" +
						"  5. 멀티스레딩 설정\n" +
						"  6. 이미지 압축 설정\n" +
						"  9. 뒤로";
		print.info("{}\n", settingMenu);
	}

	/**
	 * 메뉴 8-2 저장경로 변경
	 *
	 * @param in
	 * @throws Exception
	 */
	private void changeSavePath(final BufferedReader in) throws Exception {
		print.info("현재 저장경로: {}\n변경할 경로를 입력하세요: ", SystemInfo.PATH);

		String path = in.readLine().trim();
		File newPath = new File(path);

		/* 입력한 경로가 만든 적이 없는 경로 & 그런데 새로 생성 실패 */
		if (newPath.exists() == false && newPath.mkdirs() == false) {
			ErrorHandling.printError("저장경로 변경 실패", false);
			return;
		}

		/* 생성 가능한 정상적인 경로라면 */
		Configuration.setProperty("PATH", path);
		Configuration.refresh(); //store -> load - > apply
		print.info("저장경로 변경 완료!\n");
	}

	/**
	 * 메뉴 8-3 이미지 합치기
	 *
	 * @param in
	 * @throws Exception
	 */
	private void mergeImage(final BufferedReader in) throws Exception {
		boolean merge = Configuration.getBoolean("MERGE", false);
		print.info("true면 다운받은 만화를 하나의 긴 파일로 합친 파일을 추가로 생성합니다(현재: {})\n", merge);
		print.info("값 입력(true or false): ");

		String input = StringUtils.lowerCase(in.readLine());
		if (StringUtils.containsAny(input, "true", "false")) {
			Configuration.setProperty("MERGE", input);
			Configuration.refresh();
			System.out.println("변경 완료");
		} else {
			ErrorHandling.printError("잘못된 값입니다.", false);
		}
	}

	/**
	 * 메뉴 8-4 디버깅 모드
	 *
	 * @param in
	 * @throws Exception
	 */
	private void debugMode(final BufferedReader in) throws Exception {
		boolean debug = Configuration.getBoolean("DEBUG", false);
		print.info("true면 다운로드 과정에 파일의 용량과 메모리 사용량이 같이 출력됩니다(현재: {})\n", debug);
		print.info("값 입력(true or false): ");

		String input = StringUtils.lowerCase(in.readLine());
		if (StringUtils.containsAny(input, "true", "false")) {
			Configuration.setProperty("DEBUG", input);
			Configuration.refresh();
			print.info("변경 완료\n");
		} else {
			ErrorHandling.printError("잘못된 값입니다.", false);
		}
	}

	/**
	 * 메뉴 8-5 멀티스레딩 모드
	 *
	 * @param in
	 * @throws Exception
	 */
	private void mulsathreadMode(final BufferedReader in) throws Exception {
		int multi = Configuration.getInt("MULTI", 2);
		print.info("다운로드에 할당할 스레드 값 설정합니다(현재: {})\n", multi);
		print.info("* 기본 값은 2이며, 대체로 값이 커질수록 성능은 좋아지나 메모리 사용량이 증가합니다.\n"
				+ " 0: 멀티스레딩을 하지 않습니다 (초저성능)\n"
				+ " 1: 코어 개수의 절반 만큼을 할당합니다 (저성능)\n"
				+ " 2: 코어 개수 만큼을 할당합니다 (기본값, 권장)\n"
				+ " 3: 코어 개수의 2배 만큼을 할당합니다 (고성능)\n"
				+ " 4: 사용할 수 있는 최대한 할당합니다 (초고성능)\n");
		print.info("값 입력(0 ~ 4): ");

		String input = StringUtils.trimToEmpty(in.readLine());
		if (input.matches("[0-4]")) {
			Configuration.setProperty("MULTI", input);
			Configuration.refresh();
			print.info("변경 완료\n");
		} else {
			ErrorHandling.printError("잘못된 값입니다.", false);
		}
	}

	/**
	 * 8-6 이미지 압축하기 모드
	 *
	 * @param in
	 * @throws Exception
	 */
	private void compressImage(final BufferedReader in) throws Exception {
		boolean compress = Configuration.getBoolean("ZIP", false);
		print.info("다운받은 만화들을 압축할 것인지 설정합니다(현재: {})\n", compress);
		print.info("값 입력(true or false): ");

		String input = StringUtils.lowerCase(in.readLine());
		if (StringUtils.containsAny(input, "true", "false")) {
			Configuration.setProperty("ZIP", input);
			Configuration.refresh();
			print.info("변경 완료\n");
		} else {
			ErrorHandling.printError("잘못된 값입니다.", false);
		}
	}

	@Override
	public void close() {
		instance = null;
	}
}