Rewriter Sone text parser to separate the parser from the generated HTML.
[Sone.git] / src / main / java / net / pterodactylus / sone / text / PartContainer.java
1 /*
2  * Sone - PartContainer.java - Copyright © 2010 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sone.text;
19
20 import java.io.IOException;
21 import java.io.StringWriter;
22 import java.io.Writer;
23 import java.util.ArrayDeque;
24 import java.util.ArrayList;
25 import java.util.Deque;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.NoSuchElementException;
29
30 /**
31  * Part implementation that can contain an arbitrary amount of other parts.
32  * Parts are added using the {@link #add(Part)} method and will be rendered in
33  * the order they are added.
34  *
35  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
36  */
37 public class PartContainer implements Iterable<Part> {
38
39         /** The parts to render. */
40         private final List<Part> parts = new ArrayList<Part>();
41
42         //
43         // ACCESSORS
44         //
45
46         /**
47          * Adds a part to render.
48          *
49          * @param part
50          *            The part to add
51          */
52         public void add(Part part) {
53                 parts.add(part);
54         }
55
56         /**
57          * Returns the part at the given index.
58          *
59          * @param index
60          *            The index of the part
61          * @return The part
62          */
63         public Part getPart(int index) {
64                 return parts.get(index);
65         }
66
67         /**
68          * Removes the part at the given index.
69          *
70          * @param index
71          *            The index of the part to remove
72          */
73         public void removePart(int index) {
74                 parts.remove(index);
75         }
76
77         /**
78          * Returns the number of parts.
79          *
80          * @return The number of parts
81          */
82         public int size() {
83                 return parts.size();
84         }
85
86         //
87         // ITERABLE METHODS
88         //
89
90         /**
91          * {@inheritDoc}
92          */
93         @Override
94         @SuppressWarnings("synthetic-access")
95         public Iterator<Part> iterator() {
96                 return new Iterator<Part>() {
97
98                         private Deque<Iterator<Part>> partStack = new ArrayDeque<Iterator<Part>>();
99                         private Part nextPart;
100                         private boolean foundNextPart;
101                         private boolean noNextPart;
102
103                         {
104                                 partStack.push(parts.iterator());
105                         }
106
107                         private void findNext() {
108                                 if (foundNextPart) {
109                                         return;
110                                 }
111                                 noNextPart = true;
112                                 while (!partStack.isEmpty()) {
113                                         Iterator<Part> parts = partStack.pop();
114                                         if (parts.hasNext()) {
115                                                 nextPart = parts.next();
116                                                 partStack.push(parts);
117                                                 if (nextPart instanceof PartContainer) {
118                                                         partStack.push(((PartContainer) nextPart).iterator());
119                                                 } else {
120                                                         noNextPart = false;
121                                                         break;
122                                                 }
123                                         }
124                                 }
125                                 foundNextPart = true;
126                         }
127
128                         @Override
129                         public boolean hasNext() {
130                                 findNext();
131                                 return !noNextPart;
132                         }
133
134                         @Override
135                         public Part next() {
136                                 findNext();
137                                 if (noNextPart) {
138                                         throw new NoSuchElementException();
139                                 }
140                                 foundNextPart = false;
141                                 return nextPart;
142                         }
143
144                         @Override
145                         public void remove() {
146                                 /* ignore. */
147                         }
148
149                 };
150         }
151
152 }