]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/acr/ContentUtils.java
Make DOM ACR more robust when used as repository root.
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / acr / ContentUtils.java
1 package org.argeo.cms.acr;
2
3 import java.io.PrintStream;
4 import java.util.ArrayList;
5 import java.util.List;
6 import java.util.StringJoiner;
7 import java.util.function.BiConsumer;
8
9 import javax.security.auth.login.LoginContext;
10 import javax.security.auth.login.LoginException;
11 import javax.xml.namespace.QName;
12
13 import org.argeo.api.acr.Content;
14 import org.argeo.api.acr.ContentRepository;
15 import org.argeo.api.acr.ContentSession;
16 import org.argeo.api.acr.DName;
17 import org.argeo.api.cms.CmsAuth;
18 import org.argeo.api.cms.directory.CmsDirectory;
19 import org.argeo.api.cms.directory.CmsUserManager;
20 import org.argeo.api.cms.directory.HierarchyUnit;
21 import org.argeo.api.cms.directory.UserDirectory;
22 import org.argeo.cms.util.CurrentSubject;
23 import org.osgi.service.useradmin.Role;
24
25 /** Utilities and routines around {@link Content}. */
26 public class ContentUtils {
27 public static void traverse(Content content, BiConsumer<Content, Integer> doIt) {
28 traverse(content, doIt, (Integer) null);
29 }
30
31 public static void traverse(Content content, BiConsumer<Content, Integer> doIt, Integer maxDepth) {
32 doTraverse(content, doIt, 0, maxDepth);
33 }
34
35 private static void doTraverse(Content content, BiConsumer<Content, Integer> doIt, int currentDepth,
36 Integer maxDepth) {
37 doIt.accept(content, currentDepth);
38 if (maxDepth != null && currentDepth == maxDepth)
39 return;
40 int nextDepth = currentDepth + 1;
41 for (Content child : content) {
42 doTraverse(child, doIt, nextDepth, maxDepth);
43 }
44 }
45
46 public static void print(Content content, PrintStream out, int depth, boolean printText) {
47 StringBuilder sb = new StringBuilder();
48 for (int i = 0; i < depth; i++) {
49 sb.append(" ");
50 }
51 String prefix = sb.toString();
52 out.println(prefix + content.getName());
53 for (QName key : content.keySet()) {
54 out.println(prefix + " " + key + "=" + content.get(key));
55 }
56 if (printText) {
57 if (content.hasText()) {
58 out.println("<![CDATA[" + content.getText().trim() + "]]>");
59 }
60 }
61 }
62
63 // public static <T> boolean isString(T t) {
64 // return t instanceof String;
65 // }
66
67 public static final char SLASH = '/';
68 public static final String SLASH_STRING = Character.toString(SLASH);
69 public static final String ROOT_SLASH = "" + SLASH;
70 public static final String EMPTY = "";
71
72 /**
73 * Split a path (with '/' separator) in an array of length 2, the first part
74 * being the parent path (which could be either absolute or relative), the
75 * second one being the last segment, (guaranteed to be without a '/').
76 */
77 public static String[] getParentPath(String path) {
78 if (path == null)
79 throw new IllegalArgumentException("Path cannot be null");
80 if (path.length() == 0)
81 throw new IllegalArgumentException("Path cannot be empty");
82 checkDoubleSlash(path);
83 int parentIndex = path.lastIndexOf(SLASH);
84 if (parentIndex == path.length() - 1) {// trailing '/'
85 path = path.substring(0, path.length() - 1);
86 parentIndex = path.lastIndexOf(SLASH);
87 }
88
89 if (parentIndex == -1) // no '/'
90 return new String[] { EMPTY, path };
91
92 return new String[] { parentIndex != 0 ? path.substring(0, parentIndex) : "" + SLASH,
93 path.substring(parentIndex + 1) };
94 }
95
96 public static String toPath(List<String> segments) {
97 // TODO checks
98 StringJoiner sj = new StringJoiner("/");
99 segments.forEach((s) -> sj.add(s));
100 return sj.toString();
101 }
102
103 public static List<String> toPathSegments(String path) {
104 List<String> res = new ArrayList<>();
105 if (EMPTY.equals(path) || ROOT_SLASH.equals(path))
106 return res;
107 collectPathSegments(path, res);
108 return res;
109 }
110
111 private static void collectPathSegments(String path, List<String> segments) {
112 String[] parent = getParentPath(path);
113 if (EMPTY.equals(parent[1])) // root
114 return;
115 segments.add(0, parent[1]);
116 if (EMPTY.equals(parent[0])) // end
117 return;
118 collectPathSegments(parent[0], segments);
119 }
120
121 public static void checkDoubleSlash(String path) {
122 if (path.contains(SLASH + "" + SLASH))
123 throw new IllegalArgumentException("Path " + path + " contains //");
124 }
125
126 /*
127 * DIRECTORY
128 */
129
130 public static Content roleToContent(CmsUserManager userManager, ContentSession contentSession, Role role) {
131 UserDirectory userDirectory = userManager.getDirectory(role);
132 String path = directoryPath(userDirectory) + userDirectory.getRolePath(role);
133 Content content = contentSession.get(path);
134 return content;
135 }
136
137 public static Content hierarchyUnitToContent(ContentSession contentSession, HierarchyUnit hierarchyUnit) {
138 CmsDirectory directory = hierarchyUnit.getDirectory();
139 StringJoiner relativePath = new StringJoiner(SLASH_STRING);
140 buildHierarchyUnitPath(hierarchyUnit, relativePath);
141 String path = directoryPath(directory) + relativePath.toString();
142 Content content = contentSession.get(path);
143 return content;
144 }
145
146 /** The path to this {@link CmsDirectory}. Ends with a /. */
147 private static String directoryPath(CmsDirectory directory) {
148 return CmsContentRepository.DIRECTORY_BASE + SLASH + directory.getName() + SLASH;
149 }
150
151 /** Recursively build a relative path of a {@link HierarchyUnit}. */
152 private static void buildHierarchyUnitPath(HierarchyUnit current, StringJoiner relativePath) {
153 if (current.getParent() == null) // directory
154 return;
155 buildHierarchyUnitPath(current.getParent(), relativePath);
156 relativePath.add(current.getHierarchyUnitName());
157 }
158
159 /*
160 * CONSUMER UTILS
161 */
162
163 public static Content createCollections(ContentSession session, String path) {
164 if (session.exists(path)) {
165 Content content = session.get(path);
166 if (!content.isContentClass(DName.collection.qName())) {
167 throw new IllegalStateException("Content " + path + " already exists, but is not a collection");
168 } else {
169 return content;
170 }
171 } else {
172 String[] parentPath = getParentPath(path);
173 Content parent = createCollections(session, parentPath[0]);
174 Content content = parent.add(parentPath[1], DName.collection.qName());
175 return content;
176 }
177 }
178
179 public static ContentSession openDataAdminSession(ContentRepository repository) {
180 LoginContext loginContext;
181 try {
182 loginContext = CmsAuth.DATA_ADMIN.newLoginContext();
183 loginContext.login();
184 } catch (LoginException e1) {
185 throw new RuntimeException("Could not login as data admin", e1);
186 } finally {
187 }
188
189 ClassLoader currentCl = Thread.currentThread().getContextClassLoader();
190 try {
191 Thread.currentThread().setContextClassLoader(ContentUtils.class.getClassLoader());
192 return CurrentSubject.callAs(loginContext.getSubject(), () -> repository.get());
193 } finally {
194 Thread.currentThread().setContextClassLoader(currentCl);
195 }
196 }
197
198 /** Singleton. */
199 private ContentUtils() {
200
201 }
202
203 }