001/*
002 *   AbstractNote  -- Partial implementation of a note that holds a String located at a point in nD space.
003 *
004 *   Copyright (C) 2014-2017, Joseph A. Huwaldt.
005 *   All rights reserved.
006 *   
007 *   This library is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2.1 of the License, or (at your option) any later version.
011 *   
012 *   This library is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015 *   Lesser General Public License for more details.
016 *
017 *   You should have received a copy of the GNU Lesser General Public License
018 *   along with this program; if not, write to the Free Software
019 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
020 *   Or visit:  http://www.gnu.org/licenses/lgpl.html
021 */
022package geomss.geom;
023
024import java.awt.Font;
025import java.awt.FontFormatException;
026import java.awt.GraphicsEnvironment;
027import java.io.IOException;
028import java.io.InputStream;
029import java.net.URL;
030import static java.util.Objects.nonNull;
031import javolution.text.Text;
032import javolution.text.TextBuilder;
033import javolution.text.TypeFormat;
034
035/**
036 * Partial implementation of a textual note located at a point in nD space.
037 *
038 * <p> Modified by: Joseph A. Huwaldt </p>
039 *
040 * @author Joseph A. Huwaldt, Date: February 5, 2014
041 * @version January 30, 2017
042 *
043 * @param <T> The sub-type of this AbstractNote.
044 */
045@SuppressWarnings({"serial", "CloneableImplementsClone"})
046public abstract class AbstractNote<T extends AbstractNote> extends AbstractGeomElement<T> {
047
048    /**
049     * The default font used for displaying note objects.
050     */
051    public static final Font DEFAULT_FONT;
052
053    /**
054     * The String encoding of the default font used for displaying note objects.
055     */
056    protected static final String DEFAULT_FONT_CODE;
057
058    static {
059        //  Load and register the list of fonts that should be loaded at run-time.
060        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
061        String[] availFontNames = ge.getAvailableFontFamilyNames();
062        String[] fontNames = RESOURCES.getString("appFontNames").split(",");
063        String[] fontPaths = RESOURCES.getString("appFontPaths").split(",");
064        int numFonts = fontNames.length;
065        for (int fontIdx = 0; fontIdx < numFonts; ++fontIdx) {
066            String name = fontNames[fontIdx];
067            String path = fontPaths[fontIdx];
068
069            //  Is the font already provided by the system?
070            boolean found = false;
071            for (String availName : availFontNames) {
072                if (availName.equals(name)) {
073                    found = true;
074                    break;
075                }
076            }
077            if (!found) {
078                //  Load the font from the program resources and register it.
079                try {
080                    loadAndRegisterFont(path, Font.TRUETYPE_FONT);
081                } catch (Exception e) {
082                    //  Shouldn't happen after development.
083                    e.printStackTrace();
084                }
085            }
086        }   //  Next fontIdx
087
088        //  Get the default font name, style and size.
089        String defName = RESOURCES.getString("defFontName");
090        int defStyle = Font.BOLD;
091        int defSize = 12;
092        try {
093            defStyle = TypeFormat.parseInt(RESOURCES.getString("defFontStyle"));
094        } catch (Exception ignore) { /* Ignore, keep default */ }
095        try {
096            defSize = TypeFormat.parseInt(RESOURCES.getString("defFontSize"));
097        } catch (Exception ignore) { /* Ignore, keep default */ }
098
099        //  Save the default font for use elsewhere.
100        DEFAULT_FONT = new Font(defName, defStyle, defSize);
101
102        //  Store an encoding of the font name, style & size for use elsewhere.
103        DEFAULT_FONT_CODE = encodeFont(defName, defStyle, defSize);
104    }
105
106    /**
107     * Load the font at the specified path in the program's resources and register it with
108     * the Java font management system.
109     *
110     * @param fontPath The path to the font to be loaded in the program resources.
111     * @param fontType The type of the font: Font.TRUETYPE_FONT or Font.TYPE1_FONT.
112     * @throws FontFormatException if there is a problem with the font's format.
113     * @throws IOException
114     */
115    @SuppressWarnings("null")
116    private static void loadAndRegisterFont(String fontPath, int fontType)
117            throws FontFormatException, IOException {
118
119        //  Get a URL to the font file in the program's resources.
120        URL fontURL = ClassLoader.getSystemResource(fontPath);
121
122        Font baseFnt;
123        try (InputStream is = fontURL.openStream()) {
124
125            //  Create a new font from the file as a 1-point, plain font.
126            baseFnt = Font.createFont(fontType, is);
127
128        }
129
130        //  Register the font with the system.
131        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
132        ge.registerFont(baseFnt);
133    }
134
135    /**
136     * Return the code string for the given font. For example, 12pt Arial with fontStyle
137     * == 1 is returned as: Arial-BOLD-12
138     *
139     * @param fontName  The name of the font family.
140     * @param fontStyle The font style code such as Font.PLAIN.
141     * @param fontSize  The point size of the font.
142     * @return The string encoding of the font.
143     */
144    protected static String encodeFont(String fontName, int fontStyle, int fontSize) {
145
146        //  Convert the font style code into a style String.
147        String styleStr = "PLAIN";
148        switch (fontStyle) {
149            case Font.BOLD:
150                styleStr = "BOLD";
151                break;
152            case Font.BOLD + Font.ITALIC:
153                styleStr = "BOLDITALIC";
154                break;
155            case Font.ITALIC:
156                styleStr = "ITALIC";
157                break;
158            default:
159                break;
160        }
161
162        //  Put it all together.
163        StringBuilder buf = new StringBuilder(fontName);
164        buf.append("-");
165        buf.append(styleStr);
166        buf.append("-");
167        buf.append(fontSize);
168        return buf.toString();
169    }
170
171    /**
172     * Return the text string associated with this note object.
173     *
174     * @return The text string associated with this note.
175     */
176    public abstract String getNote();
177
178    /**
179     * Return the geometric location of this note in space.
180     *
181     * @return The geometric location of this note in space.
182     */
183    public abstract Point getLocation();
184
185    /**
186     * Return the font used to display this note.
187     *
188     * @return The font used to display this note.
189     */
190    public abstract Font getFont();
191
192    /**
193     * Return a new note object identical to this one, but with the specified font.
194     *
195     * @param font The new font to change this note to display in. May not be null.
196     * @return A new note, identical tot his one, but using the specified font.
197     */
198    public abstract T changeFont(Font font);
199
200    /**
201     * Return a new note object identical to this one, but with the specified location in
202     * model space.
203     *
204     * @param location The new location of this note. May not be null.
205     * @return A new note, identical tot his one, but using the specified location.
206     */
207    public abstract T changeLocation(GeomPoint location);
208
209    /**
210     * Return the length of the text string associated with this geometry object.
211     *
212     * @return The length, in characters, of the text string associated with this note.
213     */
214    public int length() {
215        return toString().length();
216    }
217
218    /**
219     * Returns the number of parametric dimensions of the geometry element. This
220     * implementation always returns 0 as a GeneralNote is not parametric.
221     *
222     * @return Always returns 0.
223     */
224    @Override
225    public int getParDimension() {
226        return 0;
227    }
228
229    /**
230     * Returns the text representation of this geometry element that consists of the text
231     * string followed by the coordinate position. For example:
232     * <pre>
233     *   {aNote = {"A text string.",{10 ft, -3 ft, 4.56 ft}}}
234     * </pre>
235     * If there is no name, then the output looks like this:
236     * <pre>
237     *   {"A text string.",{10 ft, -3 ft, 4.56 ft}}
238     * </pre>
239     *
240     * @return the text representation of this geometry element.
241     */
242    @Override
243    public Text toText() {
244        TextBuilder tmp = TextBuilder.newInstance();
245        tmp.append('{');
246        String nameStr = getName();
247        boolean hasName = nonNull(nameStr);
248        if (hasName) {
249            tmp.append(nameStr);
250            tmp.append(" = {");
251        }
252        tmp.append("\"");
253        tmp.append(toString());
254        tmp.append("\",");
255        tmp.append(getLocation().toText());
256        if (hasName)
257            tmp.append('}');
258        tmp.append('}');
259        Text txt = tmp.toText();
260        TextBuilder.recycle(tmp);
261        return txt;
262    }
263
264}