1 /* libobby - Network text editing library
2  * Copyright (C) 2005, 2006 0x539 dev group
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (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 GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public
15  * License along with this program; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 
19 #ifndef _OBBY_SPLIT_OPERATION_HPP_
20 #define _OBBY_SPLIT_OPERATION_HPP_
21 
22 #include "operation.hpp"
23 
24 namespace obby
25 {
26 
27 /** split_operation is a wrapper around two other operations. If an
28  * insert_operation occurs in the range of a delete_operation, the
29  * delete_operation has to be splitted into two delete_operations.
30  */
31 template<typename Document>
32 class split_operation: public operation<Document>
33 {
34 public:
35 	typedef operation<Document> operation_type;
36 	typedef typename operation_type::document_type document_type;
37 
38 	/** Constructor taking a copy of both given operations.
39 	 */
40 	split_operation(const operation_type& first,
41 	                const operation_type& second);
42 
43 	/** Constructor taking the ownership of both operations instead of
44 	 * making a copy.
45 	 */
46 	split_operation(std::auto_ptr<operation_type> first,
47 	                std::auto_ptr<operation_type> second);
48 
49 	/** Reads a split_operation from the given network packet.
50 	 */
51 	split_operation(const net6::packet& pack,
52 	                unsigned int& index,
53 	                const user_table& user_table);
54 
55 	/** Creates a copy of this operation.
56 	 */
57 	virtual operation_type* clone() const;
58 
59 	/** Creates the reverse operation of this one.
60 	 * @param doc Document to receive additional information from.
61 	 */
62 	virtual operation_type* reverse(const document_type& doc) const;
63 
64 	/** Applies this operation to a document. The split_operation just
65 	 * forwards this request to both wrapped operations.
66 	 */
67 	virtual void apply(document_type& doc, const user* author) const;
68 
69 	/** Transforms <em>base_op</em> against this operation. This function
70 	 * transforms base_op against both wrapped operations.
71 	 */
72 	virtual operation_type* transform(const operation_type& base_op) const;
73 
74 	/** Includes the effect of the given insertion into this operation.
75 	 * Both wrapped operations will be transformed.
76 	 */
77 	virtual operation_type* transform_insert(position pos,
78 	                                         const std::string& text) const;
79 
80 	/** Includes the effect of the given deletion into this operation.
81 	 * Both wrapped operations will be transformed.
82 	 */
83 	virtual operation_type* transform_delete(position pos,
84 	                                         position len) const;
85 
86 	/** Appends the operation to the given packet.
87 	 */
88 	virtual void append_packet(net6::packet& pack) const;
89 protected:
90 	std::auto_ptr<operation_type> m_first;
91 	std::auto_ptr<operation_type> m_second;
92 };
93 
94 template<typename Document>
split_operation(const operation_type & first,const operation_type & second)95 split_operation<Document>::split_operation(const operation_type& first,
96                                            const operation_type& second):
97 	operation<Document>(), m_first(first.clone() ),
98 	m_second(second.clone() )
99 {
100 }
101 
102 template<typename Document>
103 split_operation<Document>::
split_operation(std::auto_ptr<operation_type> first,std::auto_ptr<operation_type> second)104 	split_operation(std::auto_ptr<operation_type> first,
105                         std::auto_ptr<operation_type> second):
106 	operation<Document>(), m_first(first), m_second(second)
107 {
108 }
109 
110 template<typename Document>
split_operation(const net6::packet & pack,unsigned int & index,const user_table & user_table)111 split_operation<Document>::split_operation(const net6::packet& pack,
112                                            unsigned int& index,
113                                            const user_table& user_table):
114 	operation<Document>(),
115 	m_first(
116 		operation<Document>::from_packet(
117 			pack,
118 			index,
119 			user_table
120 		).release()
121 	),
122 	m_second(operation<Document>::from_packet(
123 			pack,
124 			index,
125 			user_table
126 		).release()
127 	)
128 {
129 }
130 
131 template<typename Document>
132 typename split_operation<Document>::operation_type*
clone() const133 split_operation<Document>::clone() const
134 {
135 	return new split_operation<Document>(*m_first, *m_second);
136 }
137 
138 template<typename Document>
139 typename split_operation<Document>::operation_type*
reverse(const document_type & doc) const140 split_operation<Document>::reverse(const document_type& doc) const
141 {
142 	return new split_operation<Document>(
143 		std::auto_ptr<operation_type>(m_first->reverse(doc) ),
144 		std::auto_ptr<operation_type>(m_second->reverse(doc) )
145 	);
146 }
147 
148 template<typename Document>
apply(document_type & doc,const user * author) const149 void split_operation<Document>::apply(document_type& doc,
150                                       const user* author) const
151 {
152 	m_first->apply(doc, author);
153 
154 	// Transform second operation because first has just been applied
155 	std::auto_ptr<operation_type> second(m_first->transform(*m_second) );
156 	second->apply(doc, author);
157 }
158 
159 template<typename Document>
160 typename split_operation<Document>::operation_type*
transform(const operation_type & base_op) const161 split_operation<Document>::transform(const operation_type& base_op) const
162 {
163 	std::auto_ptr<operation_type> op1(m_second->transform(base_op) );
164 	return m_first->transform(*op1);
165 }
166 
167 template<typename Document>
168 typename split_operation<Document>::operation_type*
transform_insert(position pos,const std::string & text) const169 split_operation<Document>::transform_insert(position pos,
170                                             const std::string& text) const
171 {
172 	return new split_operation<Document>(
173 		std::auto_ptr<operation_type>(
174 			m_first->transform_insert(pos, text)
175 		),
176 		std::auto_ptr<operation_type>(
177 			m_second->transform_insert(pos, text)
178 		)
179 	);
180 }
181 
182 template<typename Document>
183 typename split_operation<Document>::operation_type*
transform_delete(position pos,position len) const184 split_operation<Document>::transform_delete(position pos,
185                                             position len) const
186 {
187 	return new split_operation<Document>(
188 		std::auto_ptr<operation_type>(
189 			m_first->transform_delete(pos, len)
190 		),
191 		std::auto_ptr<operation_type>(
192 			m_second->transform_delete(pos, len)
193 		)
194 	);
195 }
196 
197 template<typename Document>
append_packet(net6::packet & pack) const198 void split_operation<Document>::append_packet(net6::packet& pack) const
199 {
200 	pack << "split";
201 	m_first->append_packet(pack);
202 	m_second->append_packet(pack);
203 }
204 
205 } // namespace obby
206 
207 #endif // _OBBY_SPLIT_OPERATION_HPP_
208