001package com.hfg.xml.msofficexml.docx.drawingml.line;
002
003
004import java.awt.*;
005import java.io.Writer;
006import java.util.*;
007import java.util.List;
008
009import com.hfg.graphics.units.GfxSize;
010import com.hfg.graphics.units.GfxUnits;
011import com.hfg.util.CompareUtil;
012import com.hfg.xml.XMLNamespace;
013import com.hfg.xml.XMLNamespaceSet;
014import com.hfg.xml.XMLNode;
015import com.hfg.xml.XMLTag;
016import com.hfg.xml.msofficexml.OfficeOpenXMLTag;
017import com.hfg.xml.msofficexml.OfficeOpenXmlDocument;
018import com.hfg.xml.msofficexml.docx.drawingml.DmlXML;
019import com.hfg.xml.msofficexml.docx.drawingml.fill.DmlFill;
020import com.hfg.xml.msofficexml.docx.drawingml.fill.DmlSolidFill;
021import com.hfg.xml.msofficexml.docx.drawingml.line.dash.DmlLineDash;
022import com.hfg.xml.msofficexml.docx.drawingml.line.dash.DmlPresetLineDash;
023import com.hfg.xml.msofficexml.docx.drawingml.line.dash.DmlPresetLineDashType;
024import com.hfg.xml.msofficexml.docx.drawingml.line.join.DmlLineJoin;
025
026//------------------------------------------------------------------------------
027/**
028 Represents a line (<a:ln>) tag in drawing markup language (DML) from Office Open XML.
029
030 @author J. Alex Taylor, hairyfatguy.com
031 */
032//------------------------------------------------------------------------------
033// com.hfg XML/HTML Coding Library
034//
035// This library is free software; you can redistribute it and/or
036// modify it under the terms of the GNU Lesser General Public
037// License as published by the Free Software Foundation; either
038// version 2.1 of the License, or (at your option) any later version.
039//
040// This library is distributed in the hope that it will be useful,
041// but WITHOUT ANY WARRANTY; without even the implied warranty of
042// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
043// Lesser General Public License for more details.
044//
045// You should have received a copy of the GNU Lesser General Public
046// License along with this library; if not, write to the Free Software
047// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
048//
049// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
050// jataylor@hairyfatguy.com
051//------------------------------------------------------------------------------
052/*
053Sequence [1..1]
054
055    1. Choice [0..1]
056        a:noFill    No Fill
057        a:solidFill    Solid Fill
058        a:gradFill    Gradient Fill
059        a:pattFill
060    2. Choice [0..1]
061        a:prstDash    Preset Dash
062        a:custDash    Custom Dash
063    3. Choice [0..1]
064        a:round    Round Line Join
065        a:bevel    Line Join Bevel
066        a:miter    Miter Line Join
067    4. a:headEnd [0..1]    Line Head/End Style
068    5. a:tailEnd [0..1]    Tail line end style
069    6. a:extLst [0..1]
070 */
071
072public class DmlLine extends OfficeOpenXMLTag
073{
074   private XMLTag         mFillTag;
075   private XMLTag         mLineJoinTag;
076   private DmlLineHeadEnd mLineHeadEnd;
077   private DmlLineTailEnd mLineTailEnd;
078   private DmlLineDash    mLineDash;
079
080   private static final SubtagComparator SUBTAG_SEQUENCE_COMPARATOR = new DmlLine(null).new SubtagComparator();
081
082   // TODO: Is subtag sequencing still necessary now that we use the xsd?
083   private static List<Set<String>> sSubtagSequence = new ArrayList<>(6);
084
085   static
086   {
087      Set<String> hashSet = new HashSet<>(4);
088      hashSet.add(DmlXML.NO_FILL.getLocalName());
089      hashSet.add(DmlXML.SOLID_FILL.getLocalName());
090      hashSet.add(DmlXML.GRADIENT_FILL.getLocalName());
091      hashSet.add(DmlXML.PATTERN_FILL.getLocalName());
092      sSubtagSequence.add(hashSet);
093
094      hashSet = new HashSet<>(2);
095      hashSet.add(DmlXML.PRESET_DASH.getLocalName());
096      hashSet.add(DmlXML.CUSTOM_DASH.getLocalName());
097      sSubtagSequence.add(hashSet);
098
099      hashSet = new HashSet<>(3);
100      hashSet.add(DmlXML.ROUND.getLocalName());
101      hashSet.add(DmlXML.BEVEL.getLocalName());
102      hashSet.add(DmlXML.MITER.getLocalName());
103      sSubtagSequence.add(hashSet);
104
105      hashSet = new HashSet<>(1);
106      hashSet.add(DmlXML.HEAD_END.getLocalName());
107      sSubtagSequence.add(hashSet);
108
109      hashSet = new HashSet<>(1);
110      hashSet.add(DmlXML.TAIL_END.getLocalName());
111      sSubtagSequence.add(hashSet);
112
113      hashSet = new HashSet<>(1);
114      hashSet.add(DmlXML.EXT_LIST.getLocalName());
115      sSubtagSequence.add(hashSet);
116   }
117
118   //###########################################################################
119   // CONSTRUCTORS
120   //###########################################################################
121
122   //---------------------------------------------------------------------------
123   public DmlLine(OfficeOpenXmlDocument inParentDoc)
124   {
125      super(DmlXML.LINE, inParentDoc);
126      init();
127   }
128
129   //---------------------------------------------------------------------------
130   private void init()
131   {
132      getHeadEnd();
133      getTailEnd();
134   }
135
136   //###########################################################################
137   // PUBLIC METHODS
138   //###########################################################################
139
140   //---------------------------------------------------------------------------
141   /**
142    * Specifies the fill type that should be used for the shape.
143    */
144   public DmlLine setWidth(GfxSize inValue)
145   {
146      setAttribute(DmlXML.WIDTH_ATT, inValue.toInt(GfxUnits.emus));
147      return this;
148   }
149
150   //---------------------------------------------------------------------------
151   /**
152    * Specifies the fill type that should be used for the shape.
153    */
154   public DmlLine setFill(DmlFill inValue)
155   {
156      if (mFillTag != null)
157      {
158         removeSubtag(mFillTag);
159      }
160
161      mFillTag = inValue;
162
163      addSubtag(mFillTag);
164
165      return this;
166   }
167
168   //---------------------------------------------------------------------------
169   /**
170    * Specifies the color for a solid fill to be used for the shape.
171    */
172   public DmlLine setFill(Color inColor)
173   {
174      if (mFillTag != null)
175      {
176         removeSubtag(mFillTag);
177      }
178
179      mFillTag = new DmlSolidFill(inColor);
180
181      addSubtag(mFillTag);
182
183      return this;
184   }
185
186   //---------------------------------------------------------------------------
187   /**
188    * Specifies the line end cap type.
189    */
190   public DmlLine setCapType(DmlLineEndCapType inValue)
191   {
192      setAttribute(DmlXML.CAP_ATT, inValue);
193
194      return this;
195   }
196
197   //---------------------------------------------------------------------------
198   /**
199    * Specifies the compound line type.
200    */
201   public DmlLine setCompoundLineType(DmlCompoundLineType inValue)
202   {
203      setAttribute(DmlXML.COMPOUND_ATT, inValue);
204
205      return this;
206   }
207
208   //---------------------------------------------------------------------------
209   /**
210    * Specifies the pen alignment type.
211    */
212   public DmlLine setPenAlignmentType(DmlPenAlignmentType inValue)
213   {
214      setAttribute(DmlXML.ALIGN_ATT, inValue);
215
216      return this;
217   }
218
219   //---------------------------------------------------------------------------
220   /**
221    * Specifies the dash type.
222    */
223   public DmlLine setDash(DmlLineDash inValue)
224   {
225      if (mLineDash != null)
226      {
227         removeSubtag(mLineDash);
228      }
229
230      addSubtag(inValue);
231
232      return this;
233   }
234
235   //---------------------------------------------------------------------------
236   /**
237    * Specifies the dash type.
238    */
239   public DmlLine setDash(DmlPresetLineDashType inValue)
240   {
241      return setDash(new DmlPresetLineDash(getParentDoc(), inValue));
242   }
243
244   //---------------------------------------------------------------------------
245   /**
246    * Specifies the line join that should be used for the shape.
247    */
248   public DmlLine setLineJoin(DmlLineJoin inValue)
249   {
250      if (mLineJoinTag != null)
251      {
252         removeSubtag(mLineJoinTag);
253      }
254
255      mLineJoinTag = inValue;
256
257      addSubtag(mLineJoinTag);
258
259      return this;
260   }
261
262   //---------------------------------------------------------------------------
263   /**
264    * Returns the headEnd tag if one exists or else instantiates a new one.
265    * @return the headEnd tag for this line
266    */
267   public DmlLineHeadEnd getHeadEnd()
268   {
269      if (null == mLineHeadEnd)
270      {
271         // Check if it has been added via addSubtag()...
272         mLineHeadEnd = getOptionalSubtagByName(DmlXML.LINE_HEAD_END);
273         if (null == mLineHeadEnd)
274         {
275            mLineHeadEnd = new DmlLineHeadEnd();
276            addSubtag(mLineHeadEnd);
277         }
278      }
279
280      return mLineHeadEnd;
281   }
282
283   //---------------------------------------------------------------------------
284   /**
285    * Returns the tailEnd tag if one exists or else instantiates a new one.
286    * @return the tailEnd tag for this line
287    */
288   public DmlLineTailEnd getTailEnd()
289   {
290      if (null == mLineTailEnd)
291      {
292         // Check if it has been added via addSubtag()...
293         mLineTailEnd = getOptionalSubtagByName(DmlXML.LINE_TAIL_END);
294         if (null == mLineTailEnd)
295         {
296            mLineTailEnd = new DmlLineTailEnd();
297            addSubtag(mLineTailEnd);
298         }
299      }
300
301      return mLineTailEnd;
302   }
303
304   //---------------------------------------------------------------------------
305   @Override
306   protected void toXML(Writer inWriter, XMLNamespaceSet inDeclaredNamespaces)
307   {
308      // Before writing to XML, be sure the subtags are in the proper sequence.
309      Collections.sort(mContentAndSubtagList, SUBTAG_SEQUENCE_COMPARATOR);
310
311
312      super.toXML(inWriter, inDeclaredNamespaces);
313   }
314
315   private class SubtagComparator implements Comparator<XMLNode>
316   {
317      //------------------------------------------------------------------------
318      public int compare(XMLNode inNode1, XMLNode inNode2)
319      {
320         return CompareUtil.compare(getSequenceIndex(inNode1.getTagName()), getSequenceIndex(inNode2.getTagName()));
321      }
322
323      //------------------------------------------------------------------------
324      public int getSequenceIndex(String inTagName)
325      {
326         int index = -1;
327         for (int i = 0; i < sSubtagSequence.size(); i++)
328         {
329            if (sSubtagSequence.get(i).contains(inTagName))
330            {
331               index = i;
332               break;
333            }
334         }
335
336         return index;
337      }
338   }
339}