2 * PartsDetailVisitorStrategy.java
4 package net.sf.openrocket.gui.print.visitor;
6 import com.itextpdf.text.Chunk;
7 import com.itextpdf.text.Document;
8 import com.itextpdf.text.DocumentException;
9 import com.itextpdf.text.Element;
10 import com.itextpdf.text.Font;
11 import com.itextpdf.text.Image;
12 import com.itextpdf.text.Paragraph;
13 import com.itextpdf.text.Phrase;
14 import com.itextpdf.text.Rectangle;
15 import com.itextpdf.text.pdf.PdfPCell;
16 import com.itextpdf.text.pdf.PdfPTable;
17 import com.itextpdf.text.pdf.PdfWriter;
18 import com.itextpdf.text.pdf.draw.VerticalPositionMark;
19 import net.sf.openrocket.gui.main.ComponentIcons;
20 import net.sf.openrocket.gui.print.ITextHelper;
21 import net.sf.openrocket.gui.print.PrintUtilities;
22 import net.sf.openrocket.gui.print.PrintableFinSet;
23 import net.sf.openrocket.logging.LogHelper;
24 import net.sf.openrocket.material.Material;
25 import net.sf.openrocket.preset.ComponentPreset;
26 import net.sf.openrocket.rocketcomponent.BodyComponent;
27 import net.sf.openrocket.rocketcomponent.BodyTube;
28 import net.sf.openrocket.rocketcomponent.Bulkhead;
29 import net.sf.openrocket.rocketcomponent.Coaxial;
30 import net.sf.openrocket.rocketcomponent.ExternalComponent;
31 import net.sf.openrocket.rocketcomponent.FinSet;
32 import net.sf.openrocket.rocketcomponent.InnerTube;
33 import net.sf.openrocket.rocketcomponent.LaunchLug;
34 import net.sf.openrocket.rocketcomponent.MassObject;
35 import net.sf.openrocket.rocketcomponent.NoseCone;
36 import net.sf.openrocket.rocketcomponent.Parachute;
37 import net.sf.openrocket.rocketcomponent.RadiusRingComponent;
38 import net.sf.openrocket.rocketcomponent.RecoveryDevice;
39 import net.sf.openrocket.rocketcomponent.RingComponent;
40 import net.sf.openrocket.rocketcomponent.RocketComponent;
41 import net.sf.openrocket.rocketcomponent.ShockCord;
42 import net.sf.openrocket.rocketcomponent.Stage;
43 import net.sf.openrocket.rocketcomponent.Streamer;
44 import net.sf.openrocket.rocketcomponent.Transition;
45 import net.sf.openrocket.startup.Application;
46 import net.sf.openrocket.unit.Unit;
47 import net.sf.openrocket.unit.UnitGroup;
48 import net.sf.openrocket.util.Coordinate;
51 import java.text.NumberFormat;
52 import java.util.Collection;
53 import java.util.List;
57 * A visitor strategy for creating documentation about parts details.
59 public class PartsDetailVisitorStrategy {
64 private static final LogHelper log = Application.getLogger();
67 * The number of columns in the table.
69 private static final int TABLE_COLUMNS = 7;
72 * The parts detail is represented as an iText table.
79 protected Document document;
82 * The direct iText writer.
84 protected PdfWriter writer;
87 * The stages selected.
89 protected Set<Integer> stages;
92 * State variable to track the level of hierarchy.
94 protected int level = 0;
96 private static final String LINES = "Lines: ";
97 private static final String MASS = "Mass: ";
98 private static final String LEN = "Len: ";
99 private static final String THICK = "Thick: ";
100 private static final String INNER = "in ";
101 private static final String DIAMETER = "Dia";
102 private static final String OUTER = "out";
103 private static final String WIDTH = "Width";
104 private static final String LENGTH = "Length";
105 private static final String SHROUD_LINES = "Shroud Lines";
106 private static final String AFT_DIAMETER = "Aft Dia: ";
107 private static final String FORE_DIAMETER = "Fore Dia: ";
108 private static final String PARTS_DETAIL = "Parts Detail";
111 * Construct a strategy for visiting a parts hierarchy for the purposes of collecting details on those parts.
113 * @param doc The iText document
114 * @param theWriter The direct iText writer
115 * @param theStagesToVisit The stages to be visited by this strategy
117 public PartsDetailVisitorStrategy (Document doc, PdfWriter theWriter, Set<Integer> theStagesToVisit) {
120 stages = theStagesToVisit;
121 PrintUtilities.addText(doc, PrintUtilities.BIG_BOLD, PARTS_DETAIL);
125 * Print the parts detail.
127 * @param root the root component
129 public void writeToDocument (final RocketComponent root) {
130 goDeep(root.getChildren());
134 * Recurse through the given rocket component.
136 * @param theRc an array of rocket components; all children will be visited recursively
138 protected void goDeep (final List<RocketComponent> theRc) {
140 for (RocketComponent rocketComponent : theRc) {
141 handle(rocketComponent);
147 * Add a line to the detail report based upon the type of the component.
149 * @param component the component to print the detail for
151 private void handle (RocketComponent component) {
152 //This ugly if-then-else construct is not object oriented. Originally it was an elegant, and very OO savy, design
153 //using the Visitor pattern. Unfortunately, it was misunderstood and was removed.
154 if (component instanceof Stage) {
159 document.add(ITextHelper.createPhrase(component.getName()));
160 grid = new PdfPTable(TABLE_COLUMNS);
161 grid.setWidthPercentage(100);
162 grid.setHorizontalAlignment(Element.ALIGN_LEFT);
164 catch (DocumentException e) {
167 List<RocketComponent> rc = component.getChildren();
170 else if (component instanceof LaunchLug) {
171 LaunchLug ll = (LaunchLug) component;
172 grid.addCell(iconToImage(component));
173 grid.addCell(createNameCell(component.getName(), true, component.getPresetComponent()));
175 grid.addCell(createMaterialCell(ll.getMaterial()));
176 grid.addCell(createOuterInnerDiaCell(ll));
177 grid.addCell(createLengthCell(component.getLength()));
178 grid.addCell(createMassCell(component.getMass()));
180 else if (component instanceof NoseCone) {
181 NoseCone nc = (NoseCone) component;
182 grid.addCell(iconToImage(component));
183 grid.addCell(createNameCell(component.getName(), true, component.getPresetComponent()));
184 grid.addCell(createMaterialCell(nc.getMaterial()));
185 grid.addCell(ITextHelper.createCell(nc.getType().getName(), PdfPCell.BOTTOM));
186 grid.addCell(createLengthCell(component.getLength()));
187 grid.addCell(createMassCell(component.getMass()));
188 List<RocketComponent> rc = component.getChildren();
191 else if (component instanceof Transition) {
192 Transition tran = (Transition) component;
193 grid.addCell(iconToImage(component));
194 grid.addCell(createNameCell(component.getName(), true, component.getPresetComponent()));
195 grid.addCell(createMaterialCell(tran.getMaterial()));
197 Chunk fore = new Chunk(FORE_DIAMETER + toLength(tran.getForeRadius() * 2));
198 fore.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
199 Chunk aft = new Chunk(AFT_DIAMETER + toLength(tran.getAftRadius() * 2));
200 aft.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
201 final PdfPCell cell = ITextHelper.createCell();
202 cell.addElement(fore);
203 cell.addElement(aft);
205 grid.addCell(createLengthCell(component.getLength()));
206 grid.addCell(createMassCell(component.getMass()));
208 List<RocketComponent> rc = component.getChildren();
211 else if (component instanceof BodyTube) {
212 BodyTube bt = (BodyTube) component;
213 grid.addCell(iconToImage(component));
214 grid.addCell(createNameCell(component.getName(), true, component.getPresetComponent()));
215 grid.addCell(createMaterialCell(bt.getMaterial()));
216 grid.addCell(createOuterInnerDiaCell(bt));
217 grid.addCell(createLengthCell(component.getLength()));
218 grid.addCell(createMassCell(component.getMass()));
219 List<RocketComponent> rc = component.getChildren();
222 else if (component instanceof FinSet) {
223 handleFins((FinSet) component);
225 else if (component instanceof BodyComponent) {
226 grid.addCell(component.getName());
228 List<RocketComponent> rc = component.getChildren();
231 else if (component instanceof ExternalComponent) {
232 ExternalComponent ext = (ExternalComponent) component;
233 grid.addCell(iconToImage(component));
234 grid.addCell(createNameCell(component.getName(), true, component.getPresetComponent()));
236 grid.addCell(createMaterialCell(ext.getMaterial()));
237 grid.addCell(ITextHelper.createCell());
238 grid.addCell(createLengthCell(component.getLength()));
239 grid.addCell(createMassCell(component.getMass()));
241 List<RocketComponent> rc = component.getChildren();
244 else if (component instanceof InnerTube) {
245 InnerTube it = (InnerTube) component;
246 grid.addCell(iconToImage(component));
247 final PdfPCell pCell = createNameCell(component.getName(), true, component.getPresetComponent());
249 grid.addCell(createMaterialCell(it.getMaterial()));
250 grid.addCell(createOuterInnerDiaCell(it));
251 grid.addCell(createLengthCell(component.getLength()));
252 grid.addCell(createMassCell(component.getMass()));
254 List<RocketComponent> rc = component.getChildren();
257 else if (component instanceof RadiusRingComponent) {
258 RadiusRingComponent rrc = (RadiusRingComponent) component;
259 grid.addCell(iconToImage(component));
260 grid.addCell(createNameCell(component.getName(), true, component.getPresetComponent()));
261 grid.addCell(createMaterialCell(rrc.getMaterial()));
262 if (component instanceof Bulkhead) {
263 grid.addCell(createDiaCell(rrc.getOuterRadius()*2));
266 grid.addCell(createOuterInnerDiaCell(rrc));
268 grid.addCell(createLengthCell(component.getLength()));
269 grid.addCell(createMassCell(component.getMass()));
270 List<RocketComponent> rc = component.getChildren();
273 else if (component instanceof RingComponent) {
274 RingComponent ring = (RingComponent) component;
275 grid.addCell(iconToImage(component));
276 grid.addCell(createNameCell(component.getName(), true, component.getPresetComponent()));
277 grid.addCell(createMaterialCell(ring.getMaterial()));
278 grid.addCell(createOuterInnerDiaCell(ring));
279 grid.addCell(createLengthCell(component.getLength()));
280 grid.addCell(createMassCell(component.getMass()));
282 List<RocketComponent> rc = component.getChildren();
285 else if (component instanceof ShockCord) {
286 ShockCord ring = (ShockCord) component;
287 PdfPCell cell = ITextHelper.createCell();
288 cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
289 cell.setPaddingBottom(12f);
290 grid.addCell(iconToImage(component));
291 grid.addCell(createNameCell(component.getName(), true, component.getPresetComponent()));
292 grid.addCell(createMaterialCell(ring.getMaterial()));
294 grid.addCell(createLengthCell(ring.getCordLength()));
295 grid.addCell(createMassCell(component.getMass()));
297 else if (component instanceof Parachute) {
298 Parachute chute = (Parachute) component;
299 PdfPCell cell = ITextHelper.createCell();
300 cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
301 cell.setPaddingBottom(12f);
302 grid.addCell(iconToImage(component));
303 grid.addCell(createNameCell(component.getName(), true, component.getPresetComponent()));
304 grid.addCell(createMaterialCell(chute.getMaterial()));
305 // if (chute.hasSpillHole()) {
306 // grid.addCell(createOuterInnerDiaCell(chute.getDiameter()/2, chute.getSpillHoleDiameter()/2));
309 grid.addCell(createDiaCell(chute.getDiameter()));
311 grid.addCell(createLengthCell(component.getLength()));
312 grid.addCell(createMassCell(component.getMass()));
314 grid.addCell(iconToImage(null));
315 grid.addCell(createNameCell(SHROUD_LINES, true, component.getPresetComponent()));
316 grid.addCell(createMaterialCell(chute.getLineMaterial()));
317 grid.addCell(createLinesCell(chute.getLineCount()));
318 grid.addCell(createLengthCell(chute.getLineLength()));
321 else if (component instanceof Streamer) {
322 Streamer ring = (Streamer) component;
323 PdfPCell cell = ITextHelper.createCell();
324 cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
325 cell.setPaddingBottom(12f);
326 grid.addCell(iconToImage(component));
327 grid.addCell(createNameCell(component.getName(), true, component.getPresetComponent()));
328 grid.addCell(createMaterialCell(ring.getMaterial()));
329 grid.addCell(createStrip(ring));
330 grid.addCell(createLengthCell(component.getLength()));
331 grid.addCell(createMassCell(component.getMass()));
333 else if (component instanceof RecoveryDevice) {
334 RecoveryDevice device = (RecoveryDevice) component;
335 PdfPCell cell = ITextHelper.createCell();
336 cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
337 cell.setPaddingBottom(12f);
338 grid.addCell(iconToImage(component));
339 grid.addCell(createNameCell(component.getName(), true, component.getPresetComponent()));
340 grid.addCell(createMaterialCell(device.getMaterial()));
342 grid.addCell(createLengthCell(component.getLength()));
343 grid.addCell(createMassCell(component.getMass()));
345 else if (component instanceof MassObject) {
346 PdfPCell cell = ITextHelper.createCell();
347 cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
348 cell.setPaddingBottom(12f);
350 grid.addCell(iconToImage(component));
351 final PdfPCell nameCell = createNameCell(component.getName(), true, component.getPresetComponent());
352 nameCell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
353 nameCell.setPaddingBottom(12f);
354 grid.addCell(nameCell);
356 grid.addCell(createDiaCell(((MassObject) component).getRadius() * 2));
358 grid.addCell(createMassCell(component.getMass()));
363 * Close the strategy by adding the last grid to the document.
365 public void close () {
371 catch (DocumentException e) {
372 log.error("Could not write last cell to document.", e);
377 * Create a cell to document an outer 'diameter'. This is used for components that have no inner diameter, such as
378 * a solid parachute or bulkhead.
380 * @param diameter the diameter in default length units
382 * @return a formatted cell containing the diameter
384 private PdfPCell createDiaCell (final double diameter) {
385 PdfPCell result = new PdfPCell();
386 Phrase p = new Phrase();
388 result.setVerticalAlignment(Element.ALIGN_TOP);
389 result.setBorder(Rectangle.BOTTOM);
390 Chunk c = new Chunk();
391 c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
396 c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE));
401 c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
402 c.append(" " + toLength(diameter));
404 result.addElement(p);
409 * Create a PDF cell for a streamer.
411 * @param component a component that is a Coaxial
412 * @return the PDF cell that has the streamer documented
414 private PdfPCell createStrip (final Streamer component) {
415 PdfPCell result = new PdfPCell();
416 Phrase p = new Phrase();
418 result.setVerticalAlignment(Element.ALIGN_TOP);
419 result.setBorder(Rectangle.BOTTOM);
420 Chunk c = new Chunk();
421 c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
426 c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
427 c.append(" " + toLength(component.getStripLength()));
429 result.addElement(p);
431 Phrase pw = new Phrase();
434 c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
439 c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
440 c.append(" " + toLength(component.getStripWidth()));
442 result.addElement(pw);
448 * Create a PDF cell that documents both an outer and an inner diameter of a component.
450 * @param component a component that is a Coaxial
452 * @return the PDF cell that has the outer and inner diameters documented
454 private PdfPCell createOuterInnerDiaCell (final Coaxial component) {
455 return createOuterInnerDiaCell(component, INNER);
459 * Create a PDF cell that documents both an outer and an inner diameter of a component.
461 * @param component a component that is a Coaxial
462 * @param innerLabel the label to use for the inner label subscript
464 * @return the PDF cell that has the outer and inner diameters documented
466 private PdfPCell createOuterInnerDiaCell (final Coaxial component, final String innerLabel) {
467 return createOuterInnerDiaCell(component.getOuterRadius(), component.getInnerRadius(), innerLabel);
471 * Create a PDF cell that documents both an outer and an inner diameter of a component.
473 * @param outerRadius the outer radius
474 * @param innerRadius the inner radius
475 * @param innerLabel the label to use for the inner label subscript
477 * @return the PDF cell that has the outer and inner diameters documented
479 private PdfPCell createOuterInnerDiaCell (final double outerRadius, final double innerRadius, final String innerLabel) {
481 PdfPCell result = new PdfPCell();
482 Phrase p = new Phrase();
484 result.setVerticalAlignment(Element.ALIGN_TOP);
485 result.setBorder(Rectangle.BOTTOM);
486 Chunk c = new Chunk();
487 c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
492 c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE));
497 c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
498 c.append(" " + toLength(outerRadius * 2));
500 createInnerDiaCell(innerRadius, result, innerLabel);
501 result.addElement(p);
506 * Add inner diameter data to a cell.
508 * @param innerRadius the inner radius
509 * @param cell the PDF cell to add the inner diameter data to
510 * @param innerLabel the label to use for the inner label subscript
512 private void createInnerDiaCell (final double innerRadius, PdfPCell cell, final String innerLabel) {
513 Phrase p = new Phrase();
515 Chunk c = new Chunk();
516 c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
521 c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE));
522 c.append(innerLabel);
526 c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
527 c.append(" " + toLength(innerRadius * 2));
533 * Add PDF cells for a fin set.
535 * @param theFinSet the fin set
537 private void handleFins (FinSet theFinSet) {
540 java.awt.Image awtImage = new PrintableFinSet(theFinSet).createImage();
542 Collection<Coordinate> x = theFinSet.getComponentBounds();
545 img = Image.getInstance(writer, awtImage, 0.25f);
547 catch (Exception e) {
548 log.error("Could not write image to document.", e);
551 grid.addCell(iconToImage(theFinSet));
552 grid.addCell(createNameCell(theFinSet.getName() + " (" + theFinSet.getFinCount() + ")", true));
553 grid.addCell(createMaterialCell(theFinSet.getMaterial()));
554 grid.addCell(ITextHelper.createCell(THICK + toLength(theFinSet.getThickness()), PdfPCell.BOTTOM));
555 final PdfPCell pCell = new PdfPCell();
556 pCell.setBorder(Rectangle.BOTTOM);
557 pCell.addElement(img);
559 grid.addCell(ITextHelper.createCell());
560 grid.addCell(createMassCell(theFinSet.getMass()));
562 List<RocketComponent> rc = theFinSet.getChildren();
567 * Create a length formatted cell.
569 * @param length the length, in default length units
571 * @return a PdfPCell that is formatted with the length
573 protected PdfPCell createLengthCell (double length) {
574 return ITextHelper.createCell(LEN + toLength(length), PdfPCell.BOTTOM);
578 * Create a mass formatted cell.
580 * @param mass the mass, in default mass units
582 * @return a PdfPCell that is formatted with the mass
584 protected PdfPCell createMassCell (double mass) {
585 return ITextHelper.createCell(MASS + toMass(mass), PdfPCell.BOTTOM);
589 * Create a (shroud) line count formatted cell.
591 * @param count the number of shroud lines
593 * @return a PdfPCell that is formatted with the line count
595 protected PdfPCell createLinesCell (int count) {
596 return ITextHelper.createCell(LINES + count, PdfPCell.BOTTOM);
600 * Create a cell formatted for a name (or any string for that matter).
602 * @param v the string to format into a PDF cell
603 * @param withIndent if true, then an indention is made scaled to the level of the part in the parent hierarchy
605 * @return a PdfPCell that is formatted with the string <code>v</code>
607 protected PdfPCell createNameCell (String v, boolean withIndent) {
608 return createNameCell(v, withIndent, null);
612 * Create a cell formatted for a name (or any string for that matter).
614 * @param v the string to format into a PDF cell
615 * @param withIndent if true, then an indention is made scaled to the level of the part in the parent hierarchy
616 * @param preset the component's preset, if it has one
618 * @return a PdfPCell that is formatted with the string <code>v</code>
620 protected PdfPCell createNameCell (String v, boolean withIndent, ComponentPreset preset) {
621 PdfPCell result = new PdfPCell();
622 result.setColspan(2);
623 result.setBorder(Rectangle.BOTTOM);
624 Paragraph para = new Paragraph();
625 para.setLeading(12f, 0);
626 Chunk c = new Chunk();
627 c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
629 new Chunk(new VerticalPositionMark(), (level - 2) * 10, true);
632 para.add(new Chunk(tab1));
637 //Add the preset's manufacturer and part no in a subscript font.
638 if (preset != null) {
639 para.add(Chunk.NEWLINE);
642 para.add(new Chunk(tab1));
644 c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE));
645 c.append(preset.toString());
648 result.addElement(para);
653 * Create a cell that describes a material.
655 * @param material the material
657 * @return a PdfPCell that is formatted with a description of the material
659 protected PdfPCell createMaterialCell (Material material) {
660 PdfPCell cell = ITextHelper.createCell();
661 cell.setLeading(13f, 0);
663 Chunk c = new Chunk();
664 c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
665 c.append(toMaterialName(material));
667 Chunk density = new Chunk();
668 density.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE));
669 density.append(toMaterialDensity(material));
670 cell.addElement(density);
675 * Get the icon of the particular type of rocket component and conver it to an image in a PDF cell.
677 * @param visitable the rocket component to create a cell with it's image
679 * @return a PdfPCell that is just an image that can be put into a PDF
681 protected PdfPCell iconToImage (final RocketComponent visitable) {
682 if (visitable != null) {
683 final ImageIcon icon = (ImageIcon) ComponentIcons.getLargeIcon(visitable.getClass());
686 Image im = Image.getInstance(icon.getImage(), null);
688 im.scaleToFit(icon.getIconWidth() * 0.6f, icon.getIconHeight() * 0.6f);
689 PdfPCell cell = new PdfPCell(im);
690 cell.setFixedHeight(icon.getIconHeight() * 0.6f);
691 cell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER);
692 cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
693 cell.setBorder(PdfPCell.NO_BORDER);
698 catch (Exception e) {
701 PdfPCell cell = new PdfPCell();
702 cell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER);
703 cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
704 cell.setBorder(PdfPCell.NO_BORDER);
709 * Format the length as a displayable string.
711 * @param length the length (assumed to be in default length units)
713 * @return a string representation of the length with unit abbreviation
715 protected String toLength (double length) {
716 final Unit defaultUnit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
717 return NumberFormat.getNumberInstance().format(defaultUnit.toUnit(length)) + defaultUnit.toString();
721 * Format the mass as a displayable string.
723 * @param mass the mass (assumed to be in default mass units)
725 * @return a string representation of the mass with mass abbreviation
727 protected String toMass (double mass) {
728 final Unit defaultUnit = UnitGroup.UNITS_MASS.getDefaultUnit();
729 return NumberFormat.getNumberInstance().format(defaultUnit.toUnit(mass)) + defaultUnit.toString();
733 * Get a displayable string of the material's name.
735 * @param material the material to output
737 * @return the material name
739 protected String toMaterialName (Material material) {
740 return material.getName();
744 * Format the material density as a displayable string.
746 * @param material the material to output
748 * @return a string representation of the material density
750 protected String toMaterialDensity (Material material) {
751 return " (" + material.getType().getUnitGroup().getDefaultUnit().toStringUnit(material.getDensity()) + ")";