2 * depth_filter_editor.c -- provide a svn_delta_editor_t which wraps
3 * another editor and provides depth-based filtering
5 * ====================================================================
6 * Licensed to the Apache Software Foundation (ASF) under one
7 * or more contributor license agreements. See the NOTICE file
8 * distributed with this work for additional information
9 * regarding copyright ownership. The ASF licenses this file
10 * to you under the Apache License, Version 2.0 (the
11 * "License"); you may not use this file except in compliance
12 * with the License. You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * Unless required by applicable law or agreed to in writing,
17 * software distributed under the License is distributed on an
18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19 * KIND, either express or implied. See the License for the
20 * specific language governing permissions and limitations
22 * ====================================================================
25 #include "svn_delta.h"
28 /*** Batons, and the Toys That Create Them ***/
32 /* The editor/baton we're wrapping. */
33 const svn_delta_editor_t *wrapped_editor;
34 void *wrapped_edit_baton;
36 /* The depth to which we are limiting the drive of the wrapped
38 svn_depth_t requested_depth;
40 /* Does the wrapped editor/baton have an explicit target (in the
41 anchor/target sense of the word)? */
42 svn_boolean_t has_target;
47 /* TRUE iff this node was filtered out -- that is, not allowed to
48 pass through to the wrapped editor -- by virtue of not appearing
49 at a depth in the tree that was "inside" the requested depth. Of
50 course, any children of this node will be deeper still, and so
51 will also be filtered out for the same reason. */
52 svn_boolean_t filtered;
54 /* Pointer to the edit_baton. */
57 /* The real node baton we're wrapping. May be a directory or file
58 baton; we don't care. */
61 /* The calculated depth (in terms of counted, stacked, integral
62 deepnesses) of this node. If the node is a directory, this value
63 is 1 greater than the value of the same on its parent directory;
64 if a file, it is equal to its parent directory's depth value. */
68 /* Allocate and return a new node_baton structure, populated via the
69 the input to this helper function. */
70 static struct node_baton *
71 make_node_baton(void *edit_baton,
72 svn_boolean_t filtered,
76 struct node_baton *b = apr_palloc(pool, sizeof(*b));
77 b->edit_baton = edit_baton;
78 b->wrapped_baton = NULL;
79 b->filtered = filtered;
80 b->dir_depth = dir_depth;
84 /* Return TRUE iff changes to immediate children of the directory
85 identified by PB, when those children are of node kind KIND, are
86 allowed by the requested depth which this editor is trying to
87 preserve. EB is the edit baton. */
89 okay_to_edit(struct edit_baton *eb,
90 struct node_baton *pb,
95 /* If we've already filter out the parent directory, we necessarily
96 are filtering out its children, too. */
100 /* Calculate the effective depth of the parent directory.
102 NOTE: "Depth" in this sense is not the same as the Subversion
103 magic depth keywords. Here, we're talking about a literal,
104 integral, stacked depth of directories.
106 The root of the edit is generally depth=1, subdirectories thereof
107 depth=2, and so on. But if we have an edit target -- which means
108 that the real target of the edit operation isn't the root
109 directory, but is instead some immediate child thereof -- we have
110 to adjust our calculated effected depth such that the target
111 itself is depth=1 (as are its siblings, which we trust aren't
112 present in the edit at all), immediate subdirectories thereof are
115 effective_depth = pb->dir_depth - (eb->has_target ? 1 : 0);
116 switch (eb->requested_depth)
118 case svn_depth_empty:
119 return (effective_depth <= 0);
120 case svn_depth_files:
121 return ((effective_depth <= 0)
122 || (kind == svn_node_file && effective_depth == 1));
123 case svn_depth_immediates:
124 return (effective_depth <= 1);
125 case svn_depth_unknown:
126 case svn_depth_exclude:
127 case svn_depth_infinity:
128 /* Shouldn't reach; see svn_delta_depth_filter_editor() */
130 SVN_ERR_MALFUNCTION_NO_RETURN();
135 /*** Editor Functions ***/
138 set_target_revision(void *edit_baton,
139 svn_revnum_t target_revision,
142 struct edit_baton *eb = edit_baton;
144 /* Nothing depth-y to filter here. */
145 return eb->wrapped_editor->set_target_revision(eb->wrapped_edit_baton,
146 target_revision, pool);
150 open_root(void *edit_baton,
151 svn_revnum_t base_revision,
155 struct edit_baton *eb = edit_baton;
156 struct node_baton *b;
158 /* The root node always gets through cleanly. */
159 b = make_node_baton(edit_baton, FALSE, 1, pool);
160 SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton, base_revision,
161 pool, &b->wrapped_baton));
168 delete_entry(const char *path,
169 svn_revnum_t base_revision,
173 struct node_baton *pb = parent_baton;
174 struct edit_baton *eb = pb->edit_baton;
176 /* ### FIXME: We don't know the type of the entry, which ordinarily
177 doesn't matter, but is a key (*the* key, in fact) distinction
178 between depth "files" and depths "immediates". If the server is
179 telling us to delete a subdirectory and our requested depth was
180 "immediates", that's fine; if our requested depth was "files",
181 though, this deletion shouldn't survive filtering. For now,
182 we'll claim to our helper function that the to-be-deleted thing
183 is a file because that's the conservative route to take. */
184 if (okay_to_edit(eb, pb, svn_node_file))
185 SVN_ERR(eb->wrapped_editor->delete_entry(path, base_revision,
186 pb->wrapped_baton, pool));
192 add_directory(const char *path,
194 const char *copyfrom_path,
195 svn_revnum_t copyfrom_revision,
199 struct node_baton *pb = parent_baton;
200 struct edit_baton *eb = pb->edit_baton;
201 struct node_baton *b = NULL;
203 /* Check for sufficient depth. */
204 if (okay_to_edit(eb, pb, svn_node_dir))
206 b = make_node_baton(eb, FALSE, pb->dir_depth + 1, pool);
207 SVN_ERR(eb->wrapped_editor->add_directory(path, pb->wrapped_baton,
210 pool, &b->wrapped_baton));
214 b = make_node_baton(eb, TRUE, pb->dir_depth + 1, pool);
222 open_directory(const char *path,
224 svn_revnum_t base_revision,
228 struct node_baton *pb = parent_baton;
229 struct edit_baton *eb = pb->edit_baton;
230 struct node_baton *b;
232 /* Check for sufficient depth. */
233 if (okay_to_edit(eb, pb, svn_node_dir))
235 b = make_node_baton(eb, FALSE, pb->dir_depth + 1, pool);
236 SVN_ERR(eb->wrapped_editor->open_directory(path, pb->wrapped_baton,
242 b = make_node_baton(eb, TRUE, pb->dir_depth + 1, pool);
250 add_file(const char *path,
252 const char *copyfrom_path,
253 svn_revnum_t copyfrom_revision,
257 struct node_baton *pb = parent_baton;
258 struct edit_baton *eb = pb->edit_baton;
259 struct node_baton *b = NULL;
261 /* Check for sufficient depth. */
262 if (okay_to_edit(eb, pb, svn_node_file))
264 b = make_node_baton(eb, FALSE, pb->dir_depth, pool);
265 SVN_ERR(eb->wrapped_editor->add_file(path, pb->wrapped_baton,
266 copyfrom_path, copyfrom_revision,
267 pool, &b->wrapped_baton));
271 b = make_node_baton(eb, TRUE, pb->dir_depth, pool);
279 open_file(const char *path,
281 svn_revnum_t base_revision,
285 struct node_baton *pb = parent_baton;
286 struct edit_baton *eb = pb->edit_baton;
287 struct node_baton *b;
289 /* Check for sufficient depth. */
290 if (okay_to_edit(eb, pb, svn_node_file))
292 b = make_node_baton(eb, FALSE, pb->dir_depth, pool);
293 SVN_ERR(eb->wrapped_editor->open_file(path, pb->wrapped_baton,
299 b = make_node_baton(eb, TRUE, pb->dir_depth, pool);
307 apply_textdelta(void *file_baton,
308 const char *base_checksum,
310 svn_txdelta_window_handler_t *handler,
311 void **handler_baton)
313 struct node_baton *fb = file_baton;
314 struct edit_baton *eb = fb->edit_baton;
316 /* For filtered files, we just consume the textdelta. */
319 *handler = svn_delta_noop_window_handler;
320 *handler_baton = NULL;
324 SVN_ERR(eb->wrapped_editor->apply_textdelta(fb->wrapped_baton,
326 handler, handler_baton));
332 close_file(void *file_baton,
333 const char *text_checksum,
336 struct node_baton *fb = file_baton;
337 struct edit_baton *eb = fb->edit_baton;
339 /* Don't close filtered files. */
341 SVN_ERR(eb->wrapped_editor->close_file(fb->wrapped_baton,
342 text_checksum, pool));
348 absent_file(const char *path,
352 struct node_baton *pb = parent_baton;
353 struct edit_baton *eb = pb->edit_baton;
355 /* Don't report absent items in filtered directories. */
357 SVN_ERR(eb->wrapped_editor->absent_file(path, pb->wrapped_baton, pool));
363 close_directory(void *dir_baton,
366 struct node_baton *db = dir_baton;
367 struct edit_baton *eb = db->edit_baton;
369 /* Don't close filtered directories. */
371 SVN_ERR(eb->wrapped_editor->close_directory(db->wrapped_baton, pool));
377 absent_directory(const char *path,
381 struct node_baton *pb = parent_baton;
382 struct edit_baton *eb = pb->edit_baton;
384 /* Don't report absent items in filtered directories. */
386 SVN_ERR(eb->wrapped_editor->absent_directory(path, pb->wrapped_baton,
393 change_file_prop(void *file_baton,
395 const svn_string_t *value,
398 struct node_baton *fb = file_baton;
399 struct edit_baton *eb = fb->edit_baton;
401 /* No propchanges on filtered files. */
403 SVN_ERR(eb->wrapped_editor->change_file_prop(fb->wrapped_baton,
410 change_dir_prop(void *dir_baton,
412 const svn_string_t *value,
415 struct node_baton *db = dir_baton;
416 struct edit_baton *eb = db->edit_baton;
418 /* No propchanges on filtered nodes. */
420 SVN_ERR(eb->wrapped_editor->change_dir_prop(db->wrapped_baton,
427 close_edit(void *edit_baton,
430 struct edit_baton *eb = edit_baton;
431 return eb->wrapped_editor->close_edit(eb->wrapped_edit_baton, pool);
435 svn_delta_depth_filter_editor(const svn_delta_editor_t **editor,
437 const svn_delta_editor_t *wrapped_editor,
438 void *wrapped_edit_baton,
439 svn_depth_t requested_depth,
440 svn_boolean_t has_target,
443 svn_delta_editor_t *depth_filter_editor;
444 struct edit_baton *eb;
446 /* Easy out: if the caller wants infinite depth, there's nothing to
447 filter, so just return the editor we were supposed to wrap. And
448 if they've asked for an unknown depth, we can't possibly know
449 what that means, so why bother? */
450 if ((requested_depth == svn_depth_unknown)
451 || (requested_depth == svn_depth_infinity))
453 *editor = wrapped_editor;
454 *edit_baton = wrapped_edit_baton;
458 depth_filter_editor = svn_delta_default_editor(pool);
459 depth_filter_editor->set_target_revision = set_target_revision;
460 depth_filter_editor->open_root = open_root;
461 depth_filter_editor->delete_entry = delete_entry;
462 depth_filter_editor->add_directory = add_directory;
463 depth_filter_editor->open_directory = open_directory;
464 depth_filter_editor->change_dir_prop = change_dir_prop;
465 depth_filter_editor->close_directory = close_directory;
466 depth_filter_editor->absent_directory = absent_directory;
467 depth_filter_editor->add_file = add_file;
468 depth_filter_editor->open_file = open_file;
469 depth_filter_editor->apply_textdelta = apply_textdelta;
470 depth_filter_editor->change_file_prop = change_file_prop;
471 depth_filter_editor->close_file = close_file;
472 depth_filter_editor->absent_file = absent_file;
473 depth_filter_editor->close_edit = close_edit;
475 eb = apr_palloc(pool, sizeof(*eb));
476 eb->wrapped_editor = wrapped_editor;
477 eb->wrapped_edit_baton = wrapped_edit_baton;
478 eb->has_target = has_target;
479 eb->requested_depth = requested_depth;
481 *editor = depth_filter_editor;