1 /*
2  * TextStyle.java 27 nov. 2008
3  *
4  * Sweet Home 3D, Copyright (c) 2008 Emmanuel PUYBARET / eTeks <info@eteks.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 package com.eteks.sweethome3d.model;
21 
22 import java.io.IOException;
23 import java.io.ObjectInputStream;
24 import java.io.Serializable;
25 import java.lang.ref.WeakReference;
26 import java.util.ArrayList;
27 import java.util.List;
28 
29 /**
30  * The different attributes that define a text style.
31  * @author Emmanuel Puybaret
32  */
33 public class TextStyle implements Serializable {
34   private static final long serialVersionUID = 1L;
35 
36   public enum Alignment {LEFT, CENTER, RIGHT}
37 
38   private final String    fontName;
39   private final float     fontSize;
40   private final boolean   bold;
41   private final boolean   italic;
42   private       Alignment alignment;
43 
44   private static final List<WeakReference<TextStyle>> textStylesCache = new ArrayList<WeakReference<TextStyle>>();
45 
TextStyle(float fontSize)46   public TextStyle(float fontSize) {
47     this(fontSize, false, false);
48   }
49 
TextStyle(float fontSize, boolean bold, boolean italic)50   public TextStyle(float fontSize, boolean bold, boolean italic) {
51     this(null, fontSize, bold, italic);
52   }
53 
54   /**
55    * Creates a text style from its font's name, its size and style.
56    * @since 5.0
57    */
TextStyle(String fontName, float fontSize, boolean bold, boolean italic)58   public TextStyle(String fontName, float fontSize, boolean bold, boolean italic) {
59     this(fontName, fontSize, bold, italic, Alignment.CENTER);
60   }
61 
62   /**
63    * Creates a text style from its font's name, its size, style and alignment.
64    * @since 6.0
65    */
TextStyle(String fontName, float fontSize, boolean bold, boolean italic, Alignment alignment)66   public TextStyle(String fontName, float fontSize, boolean bold, boolean italic, Alignment alignment) {
67     this(fontName, fontSize, bold, italic, alignment, true);
68   }
69 
TextStyle(String fontName, float fontSize, boolean bold, boolean italic, Alignment alignment, boolean cached)70   private TextStyle(String fontName, float fontSize, boolean bold, boolean italic,
71                     Alignment alignment, boolean cached) {
72     this.fontName = fontName;
73     this.fontSize = fontSize;
74     this.bold = bold;
75     this.italic = italic;
76     this.alignment = alignment;
77 
78     if (cached) {
79       textStylesCache.add(new WeakReference<TextStyle>(this));
80     }
81   }
82 
83   /**
84    * Reads style and updates cache.
85    */
readObject(ObjectInputStream in)86   private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
87     in.defaultReadObject();
88     if (this.alignment == null) {
89       this.alignment = Alignment.CENTER;
90     }
91     textStylesCache.add(new WeakReference<TextStyle>(this));
92   }
93 
94   /**
95    * Returns the text style instance matching the given parameters.
96    */
getInstance(String fontName, float fontSize, boolean bold, boolean italic, Alignment alignment)97   private TextStyle getInstance(String fontName, float fontSize, boolean bold, boolean italic, Alignment alignment) {
98     TextStyle textStyle = new TextStyle(fontName, fontSize, bold, italic, alignment, false);
99     for (int i = textStylesCache.size() - 1; i >= 0; i--) {
100       TextStyle cachedTextStyle = textStylesCache.get(i).get();
101       if (cachedTextStyle == null) {
102         textStylesCache.remove(i);
103       } else if (cachedTextStyle.equals(textStyle)) {
104         return textStyle;
105       }
106     }
107     textStylesCache.add(new WeakReference<TextStyle>(textStyle));
108     return textStyle;
109   }
110 
111   /**
112    * Returns the font name of this text style.
113    */
getFontName()114   public String getFontName() {
115     return this.fontName;
116   }
117 
118   /**
119    * Returns the font size of this text style.
120    */
getFontSize()121   public float getFontSize() {
122     return this.fontSize;
123   }
124 
125   /**
126    * Returns whether this text style is bold or not.
127    */
isBold()128   public boolean isBold() {
129     return this.bold;
130   }
131 
132   /**
133    * Returns whether this text style is italic or not.
134    */
isItalic()135   public boolean isItalic() {
136     return this.italic;
137   }
138 
139   /**
140    * Returns the alignment applied on text using this style.
141    * @since 6.0
142    */
getAlignment()143   public Alignment getAlignment() {
144     return this.alignment;
145   }
146 
147   /**
148    * Returns a derived style of this text style with a given font name.
149    * @since 5.0
150    */
deriveStyle(String fontName)151   public TextStyle deriveStyle(String fontName) {
152     if (getFontName() == fontName
153         || (fontName != null && fontName.equals(getFontName()))) {
154       return this;
155     } else {
156       return getInstance(fontName, getFontSize(), isBold(), isItalic(), getAlignment());
157     }
158   }
159 
160   /**
161    * Returns a derived style of this text style with a given font size.
162    */
deriveStyle(float fontSize)163   public TextStyle deriveStyle(float fontSize) {
164     if (getFontSize() == fontSize) {
165       return this;
166     } else {
167       return getInstance(getFontName(), fontSize, isBold(), isItalic(), getAlignment());
168     }
169   }
170 
171   /**
172    * Returns a derived style of this text style with a given alignment.
173    */
deriveStyle(Alignment alignment)174   public TextStyle deriveStyle(Alignment alignment) {
175     if (getAlignment() == alignment) {
176       return this;
177     } else {
178       return getInstance(getFontName(), getFontSize(), isBold(), isItalic(), alignment);
179     }
180   }
181 
182   /**
183    * Returns a derived style of this text style with a given bold style.
184    */
deriveBoldStyle(boolean bold)185   public TextStyle deriveBoldStyle(boolean bold) {
186     if (isBold() == bold) {
187       return this;
188     } else {
189       return getInstance(getFontName(), getFontSize(), bold, isItalic(), getAlignment());
190     }
191   }
192 
193   /**
194    * Returns a derived style of this text style with a given italic style.
195    */
deriveItalicStyle(boolean italic)196   public TextStyle deriveItalicStyle(boolean italic) {
197     if (isItalic() == italic) {
198       return this;
199     } else {
200       return getInstance(getFontName(), getFontSize(), isBold(), italic, getAlignment());
201     }
202   }
203 
204   /**
205    * Returns <code>true</code> if this text style is equal to <code>object</code>.
206    */
207   @Override
equals(Object object)208   public boolean equals(Object object) {
209     if (object instanceof TextStyle) {
210       TextStyle textStyle = (TextStyle)object;
211       return (textStyle.fontName == this.fontName
212               || (textStyle.fontName != null && textStyle.fontName.equals(this.fontName)))
213           && textStyle.fontSize == this.fontSize
214           && textStyle.bold == this.bold
215           && textStyle.italic == this.italic
216           && textStyle.alignment == this.alignment;
217     }
218     return false;
219   }
220 
221   /**
222    * Returns a hash code for this text style.
223    */
224   @Override
hashCode()225   public int hashCode() {
226     int hashCode = Float.floatToIntBits(this.fontSize);
227     if (this.fontName != null) {
228       hashCode += this.fontName.hashCode();
229     }
230     if (this.bold) {
231       hashCode++;
232     }
233     if (this.italic) {
234       hashCode++;
235     }
236     hashCode += this.alignment.hashCode();
237     return hashCode;
238   }
239 }
240