2019年6月6日木曜日

plantuml-sdcd.bat

@if (0)==(0) echo off
@rem http://computer-technology.hateblo.jp/entry/20131025/p1
setlocal
setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
set "PATH=%PATH%;%cd%\graphviz\release\bin"
set PLANTUML_LIMIT_SIZE=8192

@rem 説明:
@rem PlantUMLのシーケンス図定義をクラス図定義に変換します。
@rem Convert PlantUML sequence diagram definition into class diagram definition.

@rem Usage:
@rem sdcd.bat [path]


@rem ####################
@rem Batch.
@rem ####################

@rem ====================
@rem Batch main.
@rem ====================
set "pusin=%~1"

set "DebugPrint=false"
set rv=1
if "!DebugPrint!"=="true" call ::L_BAT_printInfo %*
call :L_BAT_plantuml_main


@rem ====================
@rem Exit.
@rem ====================
@rem echo.
@rem pause
@rem echo 終了するには何かキーを押してください . . .
@rem pause>nul
exit /b !rv!
goto :eof


@rem ====================
@rem printInfo.
@rem ====================
:L_BAT_printInfo
if not "!DebugPrint!"=="true" goto :eof

@rem 変数  拡張子           ダイアグラム    オプション  型

@rem pusin .txt             シーケンス図                定義

@rem pusdd .pusdd.txt       シーケンス図                定義
@rem puspp .pusdd.preproc   シーケンス図    -preproc    定義
@rem pusim .pusim.png       シーケンス図    -tpng       図
@rem pusid .pusid.txt       シーケンス図    -metadata   定義
@rem pusat .pusat.atxt      シーケンス図    -ttxt       図
@rem pusue .pusue.txt       シーケンス図    -encodeurl  他
@rem pusud .pusud.txt       シーケンス図    -decodeurl  定義

@rem pucdd .pucdd.txt       クラス図                    定義
@rem pucpp .pucdd.preproc   クラス図        -preproc    定義
@rem pucim .pucim.png       クラス図        -tpng       図
@rem pucid .pucid.txt       クラス図        -metadata   定義
@rem pucat .pucat.atxt      クラス図        -ttxt       図
@rem pucue .pucue.txt       クラス図        -encodeurl  他
@rem pucud .pucud.txt       クラス図        -decodeurl  定義

echo pusin
echo +-^> pusdd
echo     +-^> puspp
echo     ^|   +-^> pucdd
echo     ^|       +-^> pucpp
echo     ^|       +-^> pucim
echo     ^|       ^|   +-^> pucid
echo     ^|       +-^> pucat
echo     ^|       +-^> pucue
echo     ^|            +-^> pucud
echo     +-^> pusim
echo     ^|   +-^> pusid
echo     +-^> pusat
echo     +-^> pusue
echo          +-^> pusud
echo.

echo pusin=!pusin!

set /a rv=0
goto :eof


@rem ====================
@rem plantuml_main.
@rem ====================
:L_BAT_plantuml_main
if "!DebugPrint!"=="true" (
  echo # !pusin!
)

if not exist "!pusin!" (
  set /a rv=1
  goto :eof
)

if "!DebugPrint!"=="true" (
  echo ## Seaquense diagram.
)
set "pusdd=!pusin!"

set "pusdd=!pusdd:.txt=.pusdd.txt!"
set "pusdd=!pusdd:.pusdd.pusdd.txt=.pusdd.txt!"
set "puspp=!pusdd:.pusdd.txt=.pusdd.preproc!"

if "!DebugPrint!"=="true" (
  echo # !pusdd!
)
if not "!pusin!"=="!pusdd!" (
  copy "!pusin!" "!pusdd!"
)

call :Lc_PlantUML "!pusdd!"

if "!DebugPrint!"=="true" (
  echo ## Class diagram.
)
set "pucdd=!pusdd!"
set "pucdd=!pucdd:.pusdd.txt=.pucdd.txt!"

set "pucdd=!pucdd:.txt=.pucdd.txt!"
set "pucdd=!pucdd:.pucdd.pucdd.txt=.pucdd.txt!"

if "!DebugPrint!"=="true" (
  echo # !pucdd!
)
call :Lc_JS "!puspp!" > "!pucdd!"

call :Lc_PlantUML "!pucdd!"

set /a rv=0
goto :eof


@rem ====================
@rem Echo ErrorLevel.
@rem ====================
:L_echoErrorLevel
@rem echo BAT L_echoErrorLevel ....................
echo BAT %0 errorlevel=!errorlevel!
@rem echo.
goto :eof


@rem ====================
@rem Call PlantUML.
@rem ====================
:Lc_PlantUML
@rem echo BAT Lc_PlantUML ....................
set "puxdd=%~1"
set "puxdd=!puxdd:dd.txt=dd.txt!"
set "puxpp=!puxdd:dd.txt=dd.preproc!"
set "puxim=!puxdd:dd.txt=dd.png!"
set "puxid=!puxdd:dd.txt=id.txt!"
set "puxat=!puxdd:dd.txt=at.atxt!"
set "puxue=!puxdd:dd.txt=ue.txt!"
set "puxud=!puxdd:dd.txt=ud.txt!"

if "!DebugPrint!"=="true" (
  echo puxdd=!puxdd!
  echo puxpp=!puxpp!
  echo puxim=!puxim!
  echo puxid=!puxid!
  echo puxat=!puxat!
  echo puxue=!puxue!
  echo puxud=!puxud!
)

if "!DebugPrint!"=="true" (
  echo # !puxpp!
)
java -jar plantuml.jar -preproc "!puxdd!"

if "!DebugPrint!"=="true" (
  echo # !puxim!
)
java -jar plantuml.jar "!puxdd!"

if "!DebugPrint!"=="true" (
  echo # !puxid!
)
java -jar plantuml.jar -metadata "!puxim!" > "!puxid!"

if "!DebugPrint!"=="true" (
  echo # !puxat!
)
java -jar plantuml.jar -ttxt "!puxdd!"

if "!DebugPrint!"=="true" (
  echo # !puxue!
)
for /f "usebackq" %%z in (
  `java -jar plantuml.jar -encodeurl "!puxdd!"`
) do (
  echo %%z>"!puxue!"
  if "!DebugPrint!"=="true" (
    echo # !puxud!!
  )
  java -jar plantuml.jar -decodeurl "%%z" > "!puxud!"
)
goto :eof


@rem ====================
@rem Call JScript.
@rem ====================
:Lc_JS
@rem if "!DebugPrint!"=="true" echo BAT %0 %# ....................j
set "jsargs=%*"
if "!jsargs!"=="" (
  cscript.exe //nologo //e:JScript "%~f0"
) else (
  cscript.exe //nologo //e:JScript "%~f0" !jsargs!
)
@rem if "!DebugPrint!"=="true" call :L_echoErrorLevel
goto :eof


@end


// ####################
// JScript.
// ####################

var wsh = new ActiveXObject("WScript.Shell");
var env = wsh.Environment("PROCESS");
var fso = new ActiveXObject("Scripting.FileSystemObject");

var DebugPrint = false;
if (env.item("DebugPrint") == "true") {
   DebugPrint = true;
}


// ====================
// Output Args.
// ====================
function Output_Args(jsargs) {
//  WScript.StdErr.WriteLine("JS Output_Args ....................");
  WScript.StdErr.WriteLine("JS [" + jsargs.length + "]" + jsargs + ".");
  for (var argIdx = 0; argIdx < jsargs.length; ++argIdx) {
    WScript.StdErr.WriteLine("JS Args[" + argIdx + "]=" + jsargs[argIdx] + ".");
  }
  return 0;
}


// ====================
// Unknown arg.
// ====================
function UnknownArg(jsargs) {
  WScript.StdErr.WriteLine("JS UnknownArg ....................");
  Output_Args(jsargs);
  return 1;
}


function runCommand(cmd_str) {
  var shell = new ActiveXObject("WScript.Shell");
  // コマンドを実行
  var oe = shell.Exec(cmd_str);
  var r = oe.StdOut.ReadAll();
  return r;
}


if (! Object.keys){
    Object.keys = function (obj){
        var keys = [];
        for (var i in obj){
            if (obj.hasOwnProperty(i)){
                keys.push(i);
            }
        }
        return keys;
    };
}


// ********************
// PuClassNameMap
// クラス集合を表します。
// クラス名をキーにした連想配列で複数のクラスを管理します。
// ********************
PuClassNameMap = function () {
  this.puClassNameMap = {};
};


// ====================
// PuClassNameMap.setPuClass
// ====================
PuClassNameMap.prototype.setPuClass = function (className) {
  if (! this.puClassNameMap[className]) {
    this.puClassNameMap[className] = new PuClass(className);
  }
  return this.puClassNameMap[className];
};


// ====================
// PuClassNameMap.getNameList
// 管理しているクラスのクラス名の一覧を取得します。
// クラス名の一覧はクラス名の昇順です。
// 但しクラス名「Python」は常に先頭とします。
// これはクラス図上でクラス名「Python」を上部に配置されやすくするためです。
// ====================
PuClassNameMap.prototype.getNameList = function () {
  function _sortClassName(str1, str2) {
    if (str1 == "Python") { return -1; }
    if (str2 == "Python") { return 1; }
    if (str1 == str2) { return 0; }
    if (str1 < str2) { return -1; }
    if (str1 > str2) { return 1; }
  }
  return Object.keys(this.puClassNameMap).sort(_sortClassName);
};


// ====================
// PuClassNameMap.getPuClass
// ====================
PuClassNameMap.prototype.getPuClass = function (name) {
  return this.puClassNameMap[name];
};


// ********************
// PuClass
// クラスを表します。
// ********************
PuClass = function (name) {
  this.name = name;
  this.methodNameMap = {};

  var color = (Math.random() * 0xFFFFFF | 0).toString(16);
  var randomColor = "#" + ("000000" + color).slice(-6);
  this.color = randomColor;
};


// ====================
// PuClass.getName
// ====================
PuClass.prototype.getName = function () {
  return this.name;
};


// ====================
// PuClass.setMethodName
// クラスのメソッドを登録します。
// ====================
PuClass.prototype.setMethodName = function (methodName) {
  this.methodNameMap[methodName] = {};
  return this;
};


// ====================
// PuClass.getMethodNameList
// クラスのメソッド名一覧を取得します。
// ====================
PuClass.prototype.getMethodNameList = function () {
  return Object.keys(this.methodNameMap).sort();
};


// ====================
// PuClass.getPuClassDefLineList
// クラス定義を取得する。
// ====================
PuClass.prototype.getPuClassDefLineList = function () {
  var puClassDefLineList = [];
  puClassDefLineList.push("class " + this.name + " {");
  var methodNameList = this.getMethodNameList();
  for (var methodNameListIdx in methodNameList) {
    puClassDefLineList.push("  " + methodNameList[methodNameListIdx]);
  }
  puClassDefLineList.push("}");
  return puClassDefLineList;
};


// ********************
// PuMessageMap
// メッセージ集合を表します。
// ********************
PuMessageMap = function (puClassNameMap) {
  this.larmMap = {};
  this.ftamMap = {};
  this.puClassNameMap = puClassNameMap;
};


// ====================
// PuMessageMap.setMessage
// ====================
PuMessageMap.prototype.setMessage = function (from, arrow, to, message) {
  var fat = null;
  var fta = null;
  if (arrow == "<->") {
    fat = [from, "<->", to].join(" ");
    fta = [from, to, "<->"].join(" ");
  } else {
    fat = [from, "->", to].join(" ");
    fta = [from, to, "->"].join(" ");
  }
  if (! this.larmMap[fat]) {
    this.larmMap[fat] = {};
  }
  this.larmMap[fat][message] = {};
  if (! this.ftamMap[fta]) {
    this.ftamMap[fta] = {};
  }
  this.ftamMap[fta] = fat;
};


// ====================
// PuMessageMap.getPuRelationDefList
// 関連定義一覧を取得します。
// ====================
PuMessageMap.prototype.getPuRelationDefList = function () {
  var puRelationDefList = [];
  var fatList = Object.keys(this.larmMap).sort();
  var ftaList = Object.keys(this.ftamMap).sort();
  for (var ftaListIdx in ftaList) {
    var fatListKey = this.ftamMap[ftaList[ftaListIdx]];
    var fatListKeyDsp = fatListKey
    fatListKeyDsp = fatListKeyDsp
        .replace(/ -> /g, " --> ")
        .replace(/ <- /g, " <-- ")
        .replace(/ <-> /g, " <--> ");

    // 関連定義をfrom[1]=クラス名(元)、arrow[2]=矢印、to[3]=クラス名(先)に分ける。
    var fatListKeyDspSplit = fatListKeyDsp.split(/ +/);
    // 矢印が一方向の場合、クラス名(先)の色を求める。
    var color = "#000000";
    if (fatListKeyDspSplit[1] == "-->") {
      color = this.puClassNameMap.puClassNameMap[fatListKeyDspSplit[2]].color;
    }
    if (fatListKeyDspSplit[1] == "<--") {
      color = this.puClassNameMap.puClassNameMap[fatListKeyDspSplit[1]].color;
    }
    // 矢印に色指定を付与し、関連定義を組み立てる。
    var arrow = fatListKeyDspSplit[1].substring(0, 2) + "[" + color + "]" + fatListKeyDspSplit[1].substring(2);
    fatListKeyDsp = [fatListKeyDspSplit[0], arrow, fatListKeyDspSplit[2]].join(" ");

    puRelationDefList.push(
      [
        fatListKeyDsp,
        "\"" + Object.keys(this.larmMap[fatListKey]).sort().join("\\n") + "\""
      ].join(" : ")
    );
  }
  return puRelationDefList;
};


// ********************
// ********************
// ====================
// SdCd : PlantUMLのシーケンス図定義をクラス図定義に変換します。
// ====================
function SdCd(jsargs) {
  if (DebugPrint == true) {
    WScript.StdErr.WriteLine("JS SdCd ....................");
    Output_Args(jsargs);
  }

  // シーケンス図定義の矢印の種類を統一するための連想配列。矢印の向きは維持する。
  // 矢印の種類には「->」、「-//」、「-\\」がある。これらを「->」に統一する。
  // シーケンス図の「メッセージ」をクラスのメソッドとし、クラス図では「関連」とする。
  var arrowMap = {
    "->" : "->",
    "<-" : "<-",
    "<->" : "<->",
    "->>" : "->",
    "<<-" : "<-",
    "<<->>" : "<->",
    "-/" : "->",
    "/-" : "<-",
    "/-/" : "<->",
    "-//" : "->",
    "//-" : "<-",
    "//-//" : "<->",
    "-\\" : "->",
    "\\-" : "<-",
    "\\-\\" : "<->",
    "-\\\\" : "->",
    "\\\\-" : "<-",
    "\\\\-\\\\" : "<->"
  };
  var arrowList = new Array();
  for (var allow in arrowMap) {
    arrowList.push(allow);
  }
  // 「メッセージ」を降順で取得する。
  function _desc(str1, str2) {
    if (str1 == str2) { return 0; }
    if (str1 < str2) { return 1; }
    if (str1 > str2) { return -1; }
  }
  var arrowsRegexp = new RegExp(arrowList.sort(_desc).join("|"));

  var strmSdd = WScript.StdIn;
  var strmCdd = WScript.StdOut;
  var FORREADING = 1;
  var TRISTATE_FALSE = 0;
  if (jsargs.length != 0) {
    strmSdd = fso.OpenTextFile(jsargs[0], FORREADING, false, TRISTATE_FALSE);
  }

  // メッセージ定義を取得する。
  var titleLine = null;
  var messageDefLineList = [];
  while (! strmSdd.AtEndOfStream) {
    var lineSdd = strmSdd.ReadLine().replace(/(^\s+|\s+$)/g, "");
    // 図題を取得する。
    if (/^title /.test(lineSdd)) {
      titleLine = lineSdd;
    // 不要行を除外する。おそらく不十分。
    } else if (/^==/.test(lineSdd)) {
      ;
    // メッセージ定義を取得する。
    } else if (arrowsRegexp.test(lineSdd)) {
      messageDefLineList.push(lineSdd);
    }
  }

  // メッセージ定義を解析する。
  // メッセージの両端のクラスをクラス集合に登録する。
  // メッセージをメッセージ集合に登録する。
  var puClassNameMap = new PuClassNameMap();
  var puMessageMap = new PuMessageMap(puClassNameMap);
  for (var messageDefLineListIdx = 0; messageDefLineListIdx < messageDefLineList.length; ++messageDefLineListIdx) {
    // larm[0]=メッセージ定義全体、larm[1]=クラス名(左)、larm[2]=矢印、larm[3]=クラス名(右)、larm[4]=メッセージを取得する。
    var larm = messageDefLineList[messageDefLineListIdx].match(new RegExp("^([^ :]+) +([^ ]+) +([^ :]*) +: +(.*)$"));
    var larmLeft = 1;
    var larmArrow = 2;
    var larmRight = 3;
    var larmMessage = 4;
    // クラス名(左)をクラス集合に登録する。
    var leftClass = puClassNameMap.setPuClass(larm[larmLeft]);
    // クラス名(右)をクラス集合に登録する。
    var rightClass = puClassNameMap.setPuClass(larm[larmRight]);
    // 矢印に付加されているo、xを削除する。
    larm[larmArrow] = larm[larmArrow].replace(/[ox]/g, "");
    // 矢印の種類を統一する。
    larm[larmArrow] = arrowMap[larm[larmArrow]];
    // メッセージを矢印の先のクラスのメソッドとして登録する。
    var larmFrom = 1;
    var larmTo = 3;
    if (larm[larmArrow].slice(-2) == "->") {
      rightClass.setMethodName(larm[larmMessage]);
    }
    // メッセージを矢印の先のクラスのメソッドとして登録する。
    if (larm[larmArrow].slice(0, 2) == "<-") {
      leftClass.setMethodName(larm[larmMessage]);
      larmFrom = 3;
      larmTo = 1;
    }
    // クラス間の関連をメッセージ集合に登録する。
    puMessageMap.setMessage(larm[larmFrom], larm[larmArrow], larm[larmTo], larm[larmMessage]);
  }

  // クラス図定義の開始を出力する。
  strmCdd.WriteLine("@startuml");
  // 図題を出力する。
  if (titleLine != null) {
    strmCdd.WriteLine(titleLine);
    strmCdd.WriteLine();
  }
  // クラス名一覧を取得する。
  puClassNameList = puClassNameMap.getNameList();
  // クラス定義を出力する。
  strmCdd.WriteLine("' Classes.");
  for (var puClassNameListIdx in puClassNameList) {
    var puClassName = puClassNameList[puClassNameListIdx];
    var puClass = puClassNameMap.getPuClass(puClassName);
    var puClassDefLineList = puClass.getPuClassDefLineList();
    for (var puClassDefLineListIdx in puClassDefLineList) {
      strmCdd.WriteLine(puClassDefLineList[puClassDefLineListIdx]);
    }
    strmCdd.WriteLine("");
  }
  // 関連一覧を取得する。
  puRelationDefList = puMessageMap.getPuRelationDefList();
  // 関連定義を出力する。
  strmCdd.WriteLine("' Relations.");
  for (var puRelationDefListIdx in puRelationDefList) {
    var puRelationDef = puRelationDefList[puRelationDefListIdx];
    strmCdd.WriteLine(puRelationDef);
  }
  strmCdd.WriteLine("");
  // クラス図定義の終了を出力する。
  strmCdd.WriteLine("'");
  strmCdd.WriteLine("@enduml");

  return 0;
}


// ====================
// JScript main.
// ====================
var rv = 0;
var jsargs = new Array();
for (var argIdx = 0; argIdx < WScript.Arguments.length; ++argIdx) {
  jsargs[argIdx] = WScript.Arguments(argIdx);
}
var ary = new Array();
ary = jsargs.slice(0, jsargs.length);
if (0 == 1) {
  rv = rv + 1;
} else if ((WScript.Arguments.length >= 1) && (WScript.Arguments.length <= 1)) {
  rv = rv + SdCd(ary);
} else {
  rv = rv + UnknownArg(ary);
}

WScript.quit(rv);

0 件のコメント:

コメントを投稿