ThemisDSMReport.java
/*
* Themis: Java Project Framework
* Copyright 2012-2026. Tony Washer
*
* 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 io.github.tonywasher.joceanus.themis.lethe.dsm;
import java.util.Iterator;
import java.util.List;
import javax.swing.text.html.HTML.Tag;
/**
* DSM report.
*/
public final class ThemisDSMReport {
/**
* The reference separator.
*/
public static final String SEP_REF = "-";
/**
* Private constructor.
*/
private ThemisDSMReport() {
}
/**
* report on a module.
*
* @param pModule the module to report on
* @return the report
*/
public static String reportOnModule(final ThemisDSMModule pModule) {
/* Create a stringBuilder */
final StringBuilder myBuilder = new StringBuilder();
/* Start document */
addStartElement(myBuilder, Tag.HTML);
addStartElement(myBuilder, Tag.BODY);
/* Build table */
addStartElement(myBuilder, Tag.TABLE);
buildTableHeader(myBuilder, pModule);
/* Loop through the packages */
int myKey = 0;
int myRowNo = 0;
final Iterator<ThemisDSMPackage> myIterator = pModule.packageIterator();
while (myIterator.hasNext()) {
final ThemisDSMPackage myPackage = myIterator.next();
buildTableRow(myBuilder, pModule, myPackage, myRowNo++, myKey++);
}
/* Finish the table */
addEndElement(myBuilder, Tag.TABLE);
/* Finish document */
addEndElement(myBuilder, Tag.BODY);
addEndElement(myBuilder, Tag.HTML);
return myBuilder.toString();
}
/**
* build the table header.
*
* @param pBuilder the builder
* @param pModule the module to report on
*/
private static void buildTableHeader(final StringBuilder pBuilder,
final ThemisDSMModule pModule) {
/* Start the row */
addStartElementWithClass(pBuilder, Tag.TR, "dsm-row-header");
/* Build the initial headers */
addTextElementWithClass(pBuilder, Tag.TH, "dsm-package", "Package");
addTextElementWithClass(pBuilder, Tag.TH, "dsm-key", "Key");
/* Loop through the packages */
int myKey = 0;
final int myCount = pModule.getPackageCount();
for (int i = 0; i < myCount; i++) {
/* Add the key */
addTextElementWithClass(pBuilder, Tag.TH, "dsm-col-count", getKeyForIndex(myKey++));
}
/* Complete the row */
addEndElement(pBuilder, Tag.TR);
}
/**
* build a row for the table.
*
* @param pBuilder the builder
* @param pModule the module to report on
* @param pPackage the package to report on
* @param pRowNo the row number
* @param pKey the package key
*/
private static void buildTableRow(final StringBuilder pBuilder,
final ThemisDSMModule pModule,
final ThemisDSMPackage pPackage,
final int pRowNo,
final int pKey) {
/* Start the row */
final String myClass = (pRowNo % 2 == 0) ? "dsm-row-even" : "dsm-row-odd";
addStartElementWithClass(pBuilder, Tag.TR, myClass);
/* Build the initial cells */
addTextElementWithClass(pBuilder, Tag.TD, "dsm-cell-name-left", pPackage.getPackageName());
final String myKeyTo = getKeyForIndex(pKey);
addTextElement(pBuilder, Tag.TD, myKeyTo);
/* Loop through the packages */
int myKey = 0;
final Iterator<ThemisDSMPackage> myIterator = pModule.packageIterator();
while (myIterator.hasNext()) {
final ThemisDSMPackage myPackage = myIterator.next();
/* Add the key */
final boolean isSelf = pPackage.equals(myPackage);
final boolean isCircular = myPackage.isCircular();
final int myCount = myPackage.getReferencesTo(pPackage).size();
if (isSelf) {
final String myCellClass = isCircular ? "dsm-cell-circular" : "dsm-cell-self";
addTextElementWithClass(pBuilder, Tag.TD, myCellClass, "");
} else if (myCount == 0) {
addTextElement(pBuilder, Tag.TD, "");
} else {
final String myKeyFrom = getKeyForIndex(myKey);
addTextElementWithLink(pBuilder, Tag.TD, Integer.toString(myCount),
myKeyFrom + SEP_REF + myKeyTo);
}
myKey++;
}
/* Complete the row */
addEndElement(pBuilder, Tag.TR);
}
/**
* Obtain key for index.
*
* @param pIndex the index
* @return the key
*/
static String getKeyForIndex(final int pIndex) {
/* Handle simple index */
final int myRadix = 'Z' - 'A' + 1;
if (pIndex < myRadix) {
return Character.toString('A' + pIndex);
}
/* Handle double index */
final int myFirst = pIndex / myRadix - 1;
final int mySecond = pIndex % myRadix;
return new String(new char[]{(char) ('A' + myFirst), (char) ('A' + mySecond)});
}
/**
* Obtain index for key.
*
* @param pKey the key
* @return the index
*/
public static int getIndexForKey(final String pKey) {
/* Handle simple key */
if (pKey.length() == 1) {
return pKey.charAt(0) - 'A';
}
/* Handle double index */
final int myRadix = 'Z' - 'A' + 1;
final int myFirst = pKey.charAt(0) - 'A' + 1;
final int mySecond = pKey.charAt(1) - 'A';
return myFirst * myRadix + mySecond;
}
/**
* report on package links.
*
* @param pSource the source package to report on
* @param pTarget the target package to report on
* @return the report
*/
public static String reportOnPackageLinks(final ThemisDSMPackage pSource,
final ThemisDSMPackage pTarget) {
/* Create a stringBuilder */
final StringBuilder myBuilder = new StringBuilder();
/* Build table */
addStartElement(myBuilder, Tag.TABLE);
/* Table header */
addStartElementWithClass(myBuilder, Tag.TR, "dsm-row-header");
addTextElement(myBuilder, Tag.TH, "Class");
addTextElement(myBuilder, Tag.TH, "References");
addEndElement(myBuilder, Tag.TR);
/* Loop through the packages */
int myRowNo = 0;
for (ThemisDSMClass myClass : pSource.getReferencesTo(pTarget)) {
/* Access list of references */
final List<ThemisDSMClass> myClasses = myClass.getReferencesTo(pTarget);
/* Table header */
final String myRowClass = (myRowNo++ % 2 == 0) ? "dsm-row-even" : "dsm-row-odd";
addStartElementWithClass(myBuilder, Tag.TR, myRowClass);
addTextElementWithSpan(myBuilder, Tag.TD, myClass.getClassName(), myClasses.size());
boolean bFirst = true;
for (ThemisDSMClass myRef : myClasses) {
if (!bFirst) {
addStartElementWithClass(myBuilder, Tag.TR, myRowClass);
}
bFirst = false;
addTextElement(myBuilder, Tag.TD, myRef.getClassName());
addEndElement(myBuilder, Tag.TR);
}
}
/* Finish the table */
addEndElement(myBuilder, Tag.TABLE);
/* Finish document */
addEndElement(myBuilder, Tag.BODY);
addEndElement(myBuilder, Tag.HTML);
return myBuilder.toString();
}
/**
* Add text element.
*
* @param pBuilder the builder
* @param pElement the element
* @param pText the text
*/
private static void addTextElement(final StringBuilder pBuilder,
final Tag pElement,
final String pText) {
/* Add the text element */
addStartElement(pBuilder, pElement);
pBuilder.append(pText);
addEndElement(pBuilder, pElement);
}
/**
* Add text element.
*
* @param pBuilder the builder
* @param pElement the element
* @param pText the text
* @param pLink the link
*/
private static void addTextElementWithLink(final StringBuilder pBuilder,
final Tag pElement,
final String pText,
final String pLink) {
/* Add the text element */
addStartElement(pBuilder, pElement);
addStartElementWithLink(pBuilder, Tag.A, pLink);
pBuilder.append(pText);
addEndElement(pBuilder, Tag.A);
addEndElement(pBuilder, pElement);
}
/**
* Add text element with Clas.
*
* @param pBuilder the builder
* @param pElement the element
* @param pClass the class of the element
* @param pText the text
*/
private static void addTextElementWithClass(final StringBuilder pBuilder,
final Tag pElement,
final String pClass,
final String pText) {
/* Add the text element */
addStartElementWithClass(pBuilder, pElement, pClass);
pBuilder.append(pText);
addEndElement(pBuilder, pElement);
}
/**
* Add text element with span.
*
* @param pBuilder the builder
* @param pElement the element
* @param pText the text
* @param pSpan the span count
*/
private static void addTextElementWithSpan(final StringBuilder pBuilder,
final Tag pElement,
final String pText,
final int pSpan) {
/* Add the text element */
addStartElementWithSpan(pBuilder, pElement, pSpan);
pBuilder.append(pText);
addEndElement(pBuilder, pElement);
}
/**
* Add start element with class.
*
* @param pBuilder the builder
* @param pElement the element
* @param pClass the class of the element
*/
private static void addStartElementWithClass(final StringBuilder pBuilder,
final Tag pElement,
final String pClass) {
/* Start the element */
pBuilder.append("<");
pBuilder.append(pElement);
pBuilder.append(" class=\"");
pBuilder.append(pClass);
pBuilder.append("\">");
}
/**
* Add start element with link.
*
* @param pBuilder the builder
* @param pElement the element
* @param pLink the link
*/
private static void addStartElementWithLink(final StringBuilder pBuilder,
final Tag pElement,
final String pLink) {
/* Start the element */
pBuilder.append("<");
pBuilder.append(pElement);
pBuilder.append(" href=\"");
pBuilder.append(pLink);
pBuilder.append("\">");
}
/**
* Add start element with span.
*
* @param pBuilder the builder
* @param pElement the element
* @param pSpan the span count
*/
private static void addStartElementWithSpan(final StringBuilder pBuilder,
final Tag pElement,
final int pSpan) {
/* Start the element */
pBuilder.append("<");
pBuilder.append(pElement);
if (pSpan > 1) {
pBuilder.append(" rowspan=\"");
pBuilder.append(pSpan);
pBuilder.append("\"");
}
pBuilder.append(">");
}
/**
* Add start element.
*
* @param pBuilder the builder
* @param pElement the element
*/
private static void addStartElement(final StringBuilder pBuilder,
final Tag pElement) {
/* Start the element */
pBuilder.append("<");
pBuilder.append(pElement);
pBuilder.append(">");
}
/**
* Add end element.
*
* @param pBuilder the builder
* @param pElement the element
*/
private static void addEndElement(final StringBuilder pBuilder,
final Tag pElement) {
/* Start the element */
pBuilder.append("</");
pBuilder.append(pElement);
pBuilder.append(">");
}
}