1251881Speter/*
2251881Speter * depth_filter_editor.c -- provide a svn_delta_editor_t which wraps
3251881Speter *                          another editor and provides depth-based filtering
4251881Speter *
5251881Speter * ====================================================================
6251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
7251881Speter *    or more contributor license agreements.  See the NOTICE file
8251881Speter *    distributed with this work for additional information
9251881Speter *    regarding copyright ownership.  The ASF licenses this file
10251881Speter *    to you under the Apache License, Version 2.0 (the
11251881Speter *    "License"); you may not use this file except in compliance
12251881Speter *    with the License.  You may obtain a copy of the License at
13251881Speter *
14251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
15251881Speter *
16251881Speter *    Unless required by applicable law or agreed to in writing,
17251881Speter *    software distributed under the License is distributed on an
18251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19251881Speter *    KIND, either express or implied.  See the License for the
20251881Speter *    specific language governing permissions and limitations
21251881Speter *    under the License.
22251881Speter * ====================================================================
23251881Speter */
24251881Speter
25251881Speter#include "svn_delta.h"
26251881Speter
27251881Speter
28251881Speter/*** Batons, and the Toys That Create Them ***/
29251881Speter
30251881Speterstruct edit_baton
31251881Speter{
32251881Speter  /* The editor/baton we're wrapping. */
33251881Speter  const svn_delta_editor_t *wrapped_editor;
34251881Speter  void *wrapped_edit_baton;
35251881Speter
36251881Speter  /* The depth to which we are limiting the drive of the wrapped
37251881Speter     editor/baton. */
38251881Speter  svn_depth_t requested_depth;
39251881Speter
40251881Speter  /* Does the wrapped editor/baton have an explicit target (in the
41251881Speter     anchor/target sense of the word)? */
42251881Speter  svn_boolean_t has_target;
43251881Speter};
44251881Speter
45251881Speterstruct node_baton
46251881Speter{
47251881Speter  /* TRUE iff this node was filtered out -- that is, not allowed to
48251881Speter     pass through to the wrapped editor -- by virtue of not appearing
49251881Speter     at a depth in the tree that was "inside" the requested depth.  Of
50251881Speter     course, any children of this node will be deeper still, and so
51251881Speter     will also be filtered out for the same reason. */
52251881Speter  svn_boolean_t filtered;
53251881Speter
54251881Speter  /* Pointer to the edit_baton. */
55251881Speter  void *edit_baton;
56251881Speter
57251881Speter  /* The real node baton we're wrapping.  May be a directory or file
58251881Speter     baton; we don't care. */
59251881Speter  void *wrapped_baton;
60251881Speter
61251881Speter  /* The calculated depth (in terms of counted, stacked, integral
62251881Speter     deepnesses) of this node.  If the node is a directory, this value
63251881Speter     is 1 greater than the value of the same on its parent directory;
64251881Speter     if a file, it is equal to its parent directory's depth value. */
65251881Speter  int dir_depth;
66251881Speter};
67251881Speter
68251881Speter/* Allocate and return a new node_baton structure, populated via the
69251881Speter   the input to this helper function. */
70251881Speterstatic struct node_baton *
71251881Spetermake_node_baton(void *edit_baton,
72251881Speter                svn_boolean_t filtered,
73251881Speter                int dir_depth,
74251881Speter                apr_pool_t *pool)
75251881Speter{
76251881Speter  struct node_baton *b = apr_palloc(pool, sizeof(*b));
77251881Speter  b->edit_baton = edit_baton;
78251881Speter  b->wrapped_baton = NULL;
79251881Speter  b->filtered = filtered;
80251881Speter  b->dir_depth = dir_depth;
81251881Speter  return b;
82251881Speter}
83251881Speter
84251881Speter/* Return TRUE iff changes to immediate children of the directory
85251881Speter   identified by PB, when those children are of node kind KIND, are
86251881Speter   allowed by the requested depth which this editor is trying to
87251881Speter   preserve.  EB is the edit baton.  */
88251881Speterstatic svn_boolean_t
89251881Speterokay_to_edit(struct edit_baton *eb,
90251881Speter             struct node_baton *pb,
91251881Speter             svn_node_kind_t kind)
92251881Speter{
93251881Speter  int effective_depth;
94251881Speter
95251881Speter  /* If we've already filter out the parent directory, we necessarily
96251881Speter     are filtering out its children, too.  */
97251881Speter  if (pb->filtered)
98251881Speter    return FALSE;
99251881Speter
100251881Speter  /* Calculate the effective depth of the parent directory.
101251881Speter
102251881Speter     NOTE:  "Depth" in this sense is not the same as the Subversion
103251881Speter     magic depth keywords.  Here, we're talking about a literal,
104251881Speter     integral, stacked depth of directories.
105251881Speter
106251881Speter     The root of the edit is generally depth=1, subdirectories thereof
107251881Speter     depth=2, and so on.  But if we have an edit target -- which means
108251881Speter     that the real target of the edit operation isn't the root
109251881Speter     directory, but is instead some immediate child thereof -- we have
110251881Speter     to adjust our calculated effected depth such that the target
111251881Speter     itself is depth=1 (as are its siblings, which we trust aren't
112251881Speter     present in the edit at all), immediate subdirectories thereof are
113251881Speter     depth=2, and so on.
114251881Speter  */
115251881Speter  effective_depth = pb->dir_depth - (eb->has_target ? 1 : 0);
116251881Speter  switch (eb->requested_depth)
117251881Speter    {
118251881Speter    case svn_depth_empty:
119251881Speter      return (effective_depth <= 0);
120251881Speter    case svn_depth_files:
121251881Speter      return ((effective_depth <= 0)
122251881Speter              || (kind == svn_node_file && effective_depth == 1));
123251881Speter    case svn_depth_immediates:
124251881Speter      return (effective_depth <= 1);
125251881Speter    case svn_depth_unknown:
126251881Speter    case svn_depth_exclude:
127251881Speter    case svn_depth_infinity:
128251881Speter      /* Shouldn't reach; see svn_delta_depth_filter_editor() */
129251881Speter    default:
130251881Speter      SVN_ERR_MALFUNCTION_NO_RETURN();
131251881Speter    }
132251881Speter}
133251881Speter
134251881Speter
135251881Speter/*** Editor Functions ***/
136251881Speter
137251881Speterstatic svn_error_t *
138251881Speterset_target_revision(void *edit_baton,
139251881Speter                    svn_revnum_t target_revision,
140251881Speter                    apr_pool_t *pool)
141251881Speter{
142251881Speter  struct edit_baton *eb = edit_baton;
143251881Speter
144251881Speter  /* Nothing depth-y to filter here. */
145251881Speter return eb->wrapped_editor->set_target_revision(eb->wrapped_edit_baton,
146251881Speter                                                target_revision, pool);
147251881Speter}
148251881Speter
149251881Speterstatic svn_error_t *
150251881Speteropen_root(void *edit_baton,
151251881Speter          svn_revnum_t base_revision,
152251881Speter          apr_pool_t *pool,
153251881Speter          void **root_baton)
154251881Speter{
155251881Speter  struct edit_baton *eb = edit_baton;
156251881Speter  struct node_baton *b;
157251881Speter
158251881Speter  /* The root node always gets through cleanly. */
159251881Speter  b = make_node_baton(edit_baton, FALSE, 1, pool);
160251881Speter  SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton, base_revision,
161251881Speter                                        pool, &b->wrapped_baton));
162251881Speter
163251881Speter  *root_baton = b;
164251881Speter  return SVN_NO_ERROR;
165251881Speter}
166251881Speter
167251881Speterstatic svn_error_t *
168251881Speterdelete_entry(const char *path,
169251881Speter             svn_revnum_t base_revision,
170251881Speter             void *parent_baton,
171251881Speter             apr_pool_t *pool)
172251881Speter{
173251881Speter  struct node_baton *pb = parent_baton;
174251881Speter  struct edit_baton *eb = pb->edit_baton;
175251881Speter
176251881Speter  /* ### FIXME: We don't know the type of the entry, which ordinarily
177251881Speter     doesn't matter, but is a key (*the* key, in fact) distinction
178251881Speter     between depth "files" and depths "immediates".  If the server is
179251881Speter     telling us to delete a subdirectory and our requested depth was
180251881Speter     "immediates", that's fine; if our requested depth was "files",
181251881Speter     though, this deletion shouldn't survive filtering.  For now,
182251881Speter     we'll claim to our helper function that the to-be-deleted thing
183251881Speter     is a file because that's the conservative route to take. */
184251881Speter  if (okay_to_edit(eb, pb, svn_node_file))
185251881Speter    SVN_ERR(eb->wrapped_editor->delete_entry(path, base_revision,
186251881Speter                                             pb->wrapped_baton, pool));
187251881Speter
188251881Speter  return SVN_NO_ERROR;
189251881Speter}
190251881Speter
191251881Speterstatic svn_error_t *
192251881Speteradd_directory(const char *path,
193251881Speter              void *parent_baton,
194251881Speter              const char *copyfrom_path,
195251881Speter              svn_revnum_t copyfrom_revision,
196251881Speter              apr_pool_t *pool,
197251881Speter              void **child_baton)
198251881Speter{
199251881Speter  struct node_baton *pb = parent_baton;
200251881Speter  struct edit_baton *eb = pb->edit_baton;
201251881Speter  struct node_baton *b = NULL;
202251881Speter
203251881Speter  /* Check for sufficient depth. */
204251881Speter  if (okay_to_edit(eb, pb, svn_node_dir))
205251881Speter    {
206251881Speter      b = make_node_baton(eb, FALSE, pb->dir_depth + 1, pool);
207251881Speter      SVN_ERR(eb->wrapped_editor->add_directory(path, pb->wrapped_baton,
208251881Speter                                                copyfrom_path,
209251881Speter                                                copyfrom_revision,
210251881Speter                                                pool, &b->wrapped_baton));
211251881Speter    }
212251881Speter  else
213251881Speter    {
214251881Speter      b = make_node_baton(eb, TRUE, pb->dir_depth + 1, pool);
215251881Speter    }
216251881Speter
217251881Speter  *child_baton = b;
218251881Speter  return SVN_NO_ERROR;
219251881Speter}
220251881Speter
221251881Speterstatic svn_error_t *
222251881Speteropen_directory(const char *path,
223251881Speter               void *parent_baton,
224251881Speter               svn_revnum_t base_revision,
225251881Speter               apr_pool_t *pool,
226251881Speter               void **child_baton)
227251881Speter{
228251881Speter  struct node_baton *pb = parent_baton;
229251881Speter  struct edit_baton *eb = pb->edit_baton;
230251881Speter  struct node_baton *b;
231251881Speter
232251881Speter  /* Check for sufficient depth. */
233251881Speter  if (okay_to_edit(eb, pb, svn_node_dir))
234251881Speter    {
235251881Speter      b = make_node_baton(eb, FALSE, pb->dir_depth + 1, pool);
236251881Speter      SVN_ERR(eb->wrapped_editor->open_directory(path, pb->wrapped_baton,
237251881Speter                                                 base_revision, pool,
238251881Speter                                                 &b->wrapped_baton));
239251881Speter    }
240251881Speter  else
241251881Speter    {
242251881Speter      b = make_node_baton(eb, TRUE, pb->dir_depth + 1, pool);
243251881Speter    }
244251881Speter
245251881Speter  *child_baton = b;
246251881Speter  return SVN_NO_ERROR;
247251881Speter}
248251881Speter
249251881Speterstatic svn_error_t *
250251881Speteradd_file(const char *path,
251251881Speter         void *parent_baton,
252251881Speter         const char *copyfrom_path,
253251881Speter         svn_revnum_t copyfrom_revision,
254251881Speter         apr_pool_t *pool,
255251881Speter         void **child_baton)
256251881Speter{
257251881Speter  struct node_baton *pb = parent_baton;
258251881Speter  struct edit_baton *eb = pb->edit_baton;
259251881Speter  struct node_baton *b = NULL;
260251881Speter
261251881Speter  /* Check for sufficient depth. */
262251881Speter  if (okay_to_edit(eb, pb, svn_node_file))
263251881Speter    {
264251881Speter      b = make_node_baton(eb, FALSE, pb->dir_depth, pool);
265251881Speter      SVN_ERR(eb->wrapped_editor->add_file(path, pb->wrapped_baton,
266251881Speter                                           copyfrom_path, copyfrom_revision,
267251881Speter                                           pool, &b->wrapped_baton));
268251881Speter    }
269251881Speter  else
270251881Speter    {
271251881Speter      b = make_node_baton(eb, TRUE, pb->dir_depth, pool);
272251881Speter    }
273251881Speter
274251881Speter  *child_baton = b;
275251881Speter  return SVN_NO_ERROR;
276251881Speter}
277251881Speter
278251881Speterstatic svn_error_t *
279251881Speteropen_file(const char *path,
280251881Speter          void *parent_baton,
281251881Speter          svn_revnum_t base_revision,
282251881Speter          apr_pool_t *pool,
283251881Speter          void **child_baton)
284251881Speter{
285251881Speter  struct node_baton *pb = parent_baton;
286251881Speter  struct edit_baton *eb = pb->edit_baton;
287251881Speter  struct node_baton *b;
288251881Speter
289251881Speter  /* Check for sufficient depth. */
290251881Speter  if (okay_to_edit(eb, pb, svn_node_file))
291251881Speter    {
292251881Speter      b = make_node_baton(eb, FALSE, pb->dir_depth, pool);
293251881Speter      SVN_ERR(eb->wrapped_editor->open_file(path, pb->wrapped_baton,
294251881Speter                                            base_revision, pool,
295251881Speter                                            &b->wrapped_baton));
296251881Speter    }
297251881Speter  else
298251881Speter    {
299251881Speter      b = make_node_baton(eb, TRUE, pb->dir_depth, pool);
300251881Speter    }
301251881Speter
302251881Speter  *child_baton = b;
303251881Speter  return SVN_NO_ERROR;
304251881Speter}
305251881Speter
306251881Speterstatic svn_error_t *
307251881Speterapply_textdelta(void *file_baton,
308251881Speter                const char *base_checksum,
309251881Speter                apr_pool_t *pool,
310251881Speter                svn_txdelta_window_handler_t *handler,
311251881Speter                void **handler_baton)
312251881Speter{
313251881Speter  struct node_baton *fb = file_baton;
314251881Speter  struct edit_baton *eb = fb->edit_baton;
315251881Speter
316251881Speter  /* For filtered files, we just consume the textdelta. */
317251881Speter  if (fb->filtered)
318251881Speter    {
319251881Speter      *handler = svn_delta_noop_window_handler;
320251881Speter      *handler_baton = NULL;
321251881Speter    }
322251881Speter  else
323251881Speter    {
324251881Speter      SVN_ERR(eb->wrapped_editor->apply_textdelta(fb->wrapped_baton,
325251881Speter                                                  base_checksum, pool,
326251881Speter                                                  handler, handler_baton));
327251881Speter    }
328251881Speter  return SVN_NO_ERROR;
329251881Speter}
330251881Speter
331251881Speterstatic svn_error_t *
332251881Speterclose_file(void *file_baton,
333251881Speter           const char *text_checksum,
334251881Speter           apr_pool_t *pool)
335251881Speter{
336251881Speter  struct node_baton *fb = file_baton;
337251881Speter  struct edit_baton *eb = fb->edit_baton;
338251881Speter
339251881Speter  /* Don't close filtered files. */
340251881Speter  if (! fb->filtered)
341251881Speter    SVN_ERR(eb->wrapped_editor->close_file(fb->wrapped_baton,
342251881Speter                                           text_checksum, pool));
343251881Speter
344251881Speter  return SVN_NO_ERROR;
345251881Speter}
346251881Speter
347251881Speterstatic svn_error_t *
348251881Speterabsent_file(const char *path,
349251881Speter            void *parent_baton,
350251881Speter            apr_pool_t *pool)
351251881Speter{
352251881Speter  struct node_baton *pb = parent_baton;
353251881Speter  struct edit_baton *eb = pb->edit_baton;
354251881Speter
355251881Speter  /* Don't report absent items in filtered directories. */
356251881Speter  if (! pb->filtered)
357251881Speter    SVN_ERR(eb->wrapped_editor->absent_file(path, pb->wrapped_baton, pool));
358251881Speter
359251881Speter  return SVN_NO_ERROR;
360251881Speter}
361251881Speter
362251881Speterstatic svn_error_t *
363251881Speterclose_directory(void *dir_baton,
364251881Speter                apr_pool_t *pool)
365251881Speter{
366251881Speter  struct node_baton *db = dir_baton;
367251881Speter  struct edit_baton *eb = db->edit_baton;
368251881Speter
369251881Speter  /* Don't close filtered directories. */
370251881Speter  if (! db->filtered)
371251881Speter    SVN_ERR(eb->wrapped_editor->close_directory(db->wrapped_baton, pool));
372251881Speter
373251881Speter  return SVN_NO_ERROR;
374251881Speter}
375251881Speter
376251881Speterstatic svn_error_t *
377251881Speterabsent_directory(const char *path,
378251881Speter                 void *parent_baton,
379251881Speter                 apr_pool_t *pool)
380251881Speter{
381251881Speter  struct node_baton *pb = parent_baton;
382251881Speter  struct edit_baton *eb = pb->edit_baton;
383251881Speter
384251881Speter  /* Don't report absent items in filtered directories. */
385251881Speter  if (! pb->filtered)
386251881Speter    SVN_ERR(eb->wrapped_editor->absent_directory(path, pb->wrapped_baton,
387251881Speter                                                 pool));
388251881Speter
389251881Speter  return SVN_NO_ERROR;
390251881Speter}
391251881Speter
392251881Speterstatic svn_error_t *
393251881Speterchange_file_prop(void *file_baton,
394251881Speter                 const char *name,
395251881Speter                 const svn_string_t *value,
396251881Speter                 apr_pool_t *pool)
397251881Speter{
398251881Speter  struct node_baton *fb = file_baton;
399251881Speter  struct edit_baton *eb = fb->edit_baton;
400251881Speter
401251881Speter  /* No propchanges on filtered files. */
402251881Speter  if (! fb->filtered)
403251881Speter    SVN_ERR(eb->wrapped_editor->change_file_prop(fb->wrapped_baton,
404251881Speter                                                 name, value, pool));
405251881Speter
406251881Speter  return SVN_NO_ERROR;
407251881Speter}
408251881Speter
409251881Speterstatic svn_error_t *
410251881Speterchange_dir_prop(void *dir_baton,
411251881Speter                const char *name,
412251881Speter                const svn_string_t *value,
413251881Speter                apr_pool_t *pool)
414251881Speter{
415251881Speter  struct node_baton *db = dir_baton;
416251881Speter  struct edit_baton *eb = db->edit_baton;
417251881Speter
418251881Speter  /* No propchanges on filtered nodes. */
419251881Speter  if (! db->filtered)
420251881Speter    SVN_ERR(eb->wrapped_editor->change_dir_prop(db->wrapped_baton,
421251881Speter                                                name, value, pool));
422251881Speter
423251881Speter  return SVN_NO_ERROR;
424251881Speter}
425251881Speter
426251881Speterstatic svn_error_t *
427251881Speterclose_edit(void *edit_baton,
428251881Speter           apr_pool_t *pool)
429251881Speter{
430251881Speter  struct edit_baton *eb = edit_baton;
431251881Speter  return eb->wrapped_editor->close_edit(eb->wrapped_edit_baton, pool);
432251881Speter}
433251881Speter
434251881Spetersvn_error_t *
435251881Spetersvn_delta_depth_filter_editor(const svn_delta_editor_t **editor,
436251881Speter                              void **edit_baton,
437251881Speter                              const svn_delta_editor_t *wrapped_editor,
438251881Speter                              void *wrapped_edit_baton,
439251881Speter                              svn_depth_t requested_depth,
440251881Speter                              svn_boolean_t has_target,
441251881Speter                              apr_pool_t *pool)
442251881Speter{
443251881Speter  svn_delta_editor_t *depth_filter_editor;
444251881Speter  struct edit_baton *eb;
445251881Speter
446251881Speter  /* Easy out: if the caller wants infinite depth, there's nothing to
447251881Speter     filter, so just return the editor we were supposed to wrap.  And
448251881Speter     if they've asked for an unknown depth, we can't possibly know
449251881Speter     what that means, so why bother?  */
450251881Speter  if ((requested_depth == svn_depth_unknown)
451251881Speter      || (requested_depth == svn_depth_infinity))
452251881Speter    {
453251881Speter      *editor = wrapped_editor;
454251881Speter      *edit_baton = wrapped_edit_baton;
455251881Speter      return SVN_NO_ERROR;
456251881Speter    }
457251881Speter
458251881Speter  depth_filter_editor = svn_delta_default_editor(pool);
459251881Speter  depth_filter_editor->set_target_revision = set_target_revision;
460251881Speter  depth_filter_editor->open_root = open_root;
461251881Speter  depth_filter_editor->delete_entry = delete_entry;
462251881Speter  depth_filter_editor->add_directory = add_directory;
463251881Speter  depth_filter_editor->open_directory = open_directory;
464251881Speter  depth_filter_editor->change_dir_prop = change_dir_prop;
465251881Speter  depth_filter_editor->close_directory = close_directory;
466251881Speter  depth_filter_editor->absent_directory = absent_directory;
467251881Speter  depth_filter_editor->add_file = add_file;
468251881Speter  depth_filter_editor->open_file = open_file;
469251881Speter  depth_filter_editor->apply_textdelta = apply_textdelta;
470251881Speter  depth_filter_editor->change_file_prop = change_file_prop;
471251881Speter  depth_filter_editor->close_file = close_file;
472251881Speter  depth_filter_editor->absent_file = absent_file;
473251881Speter  depth_filter_editor->close_edit = close_edit;
474251881Speter
475251881Speter  eb = apr_palloc(pool, sizeof(*eb));
476251881Speter  eb->wrapped_editor = wrapped_editor;
477251881Speter  eb->wrapped_edit_baton = wrapped_edit_baton;
478251881Speter  eb->has_target = has_target;
479251881Speter  eb->requested_depth = requested_depth;
480251881Speter
481251881Speter  *editor = depth_filter_editor;
482251881Speter  *edit_baton = eb;
483251881Speter
484251881Speter  return SVN_NO_ERROR;
485251881Speter}
486