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