]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/acr/ContentUtils.java
Improve nested OSGi runtimes
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / acr / ContentUtils.java
1 package org.argeo.cms.acr;
2
3 import static org.argeo.api.acr.Content.PATH_SEPARATOR;
4
5 import java.io.PrintStream;
6 import java.net.URLEncoder;
7 import java.nio.charset.StandardCharsets;
8 import java.util.ArrayList;
9 import java.util.List;
10 import java.util.StringJoiner;
11 import java.util.StringTokenizer;
12 import java.util.function.BiConsumer;
13
14 import javax.security.auth.login.LoginContext;
15 import javax.security.auth.login.LoginException;
16 import javax.xml.namespace.QName;
17
18 import org.argeo.api.acr.Content;
19 import org.argeo.api.acr.ContentRepository;
20 import org.argeo.api.acr.ContentSession;
21 import org.argeo.api.acr.DName;
22 import org.argeo.api.cms.CmsAuth;
23 import org.argeo.api.cms.CmsConstants;
24 import org.argeo.api.cms.CmsSession;
25 import org.argeo.api.cms.directory.CmsDirectory;
26 import org.argeo.api.cms.directory.CmsRole;
27 import org.argeo.api.cms.directory.CmsUserManager;
28 import org.argeo.api.cms.directory.HierarchyUnit;
29 import org.argeo.api.cms.directory.UserDirectory;
30 import org.argeo.cms.util.CurrentSubject;
31
32 /** Utilities and routines around {@link Content}. */
33 public class ContentUtils {
34 // Optimisations
35 static final String PATH_SEPARATOR_STRING = Character.toString(PATH_SEPARATOR);
36 private static final String DOUBLE_PATH_SEPARATOR = PATH_SEPARATOR_STRING + PATH_SEPARATOR_STRING;
37
38 public static void traverse(Content content, BiConsumer<Content, Integer> doIt) {
39 traverse(content, doIt, (Integer) null);
40 }
41
42 public static void traverse(Content content, BiConsumer<Content, Integer> doIt, Integer maxDepth) {
43 doTraverse(content, doIt, 0, maxDepth);
44 }
45
46 private static void doTraverse(Content content, BiConsumer<Content, Integer> doIt, int currentDepth,
47 Integer maxDepth) {
48 doIt.accept(content, currentDepth);
49 if (maxDepth != null && currentDepth == maxDepth)
50 return;
51 int nextDepth = currentDepth + 1;
52 for (Content child : content) {
53 doTraverse(child, doIt, nextDepth, maxDepth);
54 }
55 }
56
57 public static void print(Content content, PrintStream out, int depth, boolean printText) {
58 StringBuilder sb = new StringBuilder();
59 for (int i = 0; i < depth; i++) {
60 sb.append(" ");
61 }
62 String prefix = sb.toString();
63 String txt = "";
64 if (printText) {
65 if (content.hasText()) {
66 final int MAX_LENGTH = 64;
67 txt = content.getText().trim();
68 if (txt.length() > MAX_LENGTH)
69 txt = txt.substring(0, 64) + " ...";
70 txt = " : " + txt;
71 }
72 }
73 out.println(prefix + content.getName() + txt);
74 for (QName key : content.keySet()) {
75 out.println(prefix + " " + key + "=" + content.get(key));
76 }
77 }
78
79 // public static <T> boolean isString(T t) {
80 // return t instanceof String;
81 // }
82
83 public static String toPath(List<String> segments) {
84 // TODO checks
85 StringJoiner sj = new StringJoiner(PATH_SEPARATOR_STRING);
86 segments.forEach((s) -> sj.add(s));
87 return sj.toString();
88 }
89
90 static List<String> toPathSegments(String path) {
91 List<String> res = new ArrayList<>();
92 if ("".equals(path) || Content.ROOT_PATH.equals(path))
93 return res;
94 collectPathSegments(path, res);
95 return res;
96 }
97
98 static void collectPathSegments(String path, List<String> segments) {
99 String[] parent = CmsContent.getParentPath(path);
100 if ("".equals(parent[1])) // root
101 return;
102 segments.add(0, parent[1]);
103 if ("".equals(parent[0])) // end
104 return;
105 collectPathSegments(parent[0], segments);
106 }
107
108 static void checkDoubleSlash(String path) {
109 if (path.contains(DOUBLE_PATH_SEPARATOR))
110 throw new IllegalArgumentException("Path " + path + " contains //");
111 }
112
113 /** The last element of a path. */
114 public static String lastPathElement(String path) {
115 if (path.charAt(path.length() - 1) == '/')
116 throw new IllegalArgumentException("Path " + path + " cannot end with '/'");
117 int index = path.lastIndexOf('/');
118 if (index < 0)
119 return path;
120 return path.substring(index + 1);
121 }
122
123 /*
124 * DIRECTORY
125 */
126
127 public static Content roleToContent(CmsUserManager userManager, ContentSession contentSession, CmsRole role) {
128 UserDirectory userDirectory = userManager.getDirectory(role);
129 String path = directoryPath(userDirectory) + userDirectory.getRolePath(role);
130 Content content = contentSession.get(path);
131 return content;
132 }
133
134 public static Content hierarchyUnitToContent(ContentSession contentSession, HierarchyUnit hierarchyUnit) {
135 CmsDirectory directory = hierarchyUnit.getDirectory();
136 StringJoiner relativePath = new StringJoiner(PATH_SEPARATOR_STRING);
137 buildHierarchyUnitPath(hierarchyUnit, relativePath);
138 String path = directoryPath(directory) + relativePath.toString();
139 Content content = contentSession.get(path);
140 return content;
141 }
142
143 /** The path to this {@link CmsDirectory}. Ends with a /. */
144 private static String directoryPath(CmsDirectory directory) {
145 return CmsContentRepository.DIRECTORY_BASE + PATH_SEPARATOR + directory.getName() + PATH_SEPARATOR;
146 }
147
148 /** Recursively build a relative path of a {@link HierarchyUnit}. */
149 private static void buildHierarchyUnitPath(HierarchyUnit current, StringJoiner relativePath) {
150 if (current.getParent() == null) // directory
151 return;
152 buildHierarchyUnitPath(current.getParent(), relativePath);
153 relativePath.add(current.getHierarchyUnitName());
154 }
155
156 /*
157 * CONSUMER UTILS
158 */
159
160 public static Content createCollections(ContentSession session, String path) {
161 if (session.exists(path)) {
162 Content content = session.get(path);
163 if (!content.isContentClass(DName.collection.qName())) {
164 throw new IllegalStateException("Content " + path + " already exists, but is not a collection");
165 } else {
166 return content;
167 }
168 } else {
169 String[] parentPath = CmsContent.getParentPath(path);
170 Content parent = createCollections(session, parentPath[0]);
171 Content content = parent.add(parentPath[1], DName.collection.qName());
172 return content;
173 }
174 }
175
176 public static ContentSession openDataAdminSession(ContentRepository contentRepository) {
177 LoginContext loginContext;
178 try {
179 loginContext = CmsAuth.DATA_ADMIN.newLoginContext();
180 loginContext.login();
181 } catch (LoginException e1) {
182 throw new RuntimeException("Could not login as data admin", e1);
183 } finally {
184 }
185
186 ClassLoader currentCl = Thread.currentThread().getContextClassLoader();
187 try {
188 Thread.currentThread().setContextClassLoader(ContentUtils.class.getClassLoader());
189 return CurrentSubject.callAs(loginContext.getSubject(), () -> contentRepository.get());
190 } finally {
191 Thread.currentThread().setContextClassLoader(currentCl);
192 }
193 }
194
195 public static ContentSession openSession(ContentRepository contentRepository, CmsSession cmsSession) {
196 return CurrentSubject.callAs(cmsSession.getSubject(), () -> contentRepository.get());
197 }
198
199 /** A path in the node repository */
200 public static String getDataPath(Content node) {
201 // TODO make it more configurable?
202 StringBuilder buf = new StringBuilder(CmsConstants.PATH_API_ACR);
203 buf.append(node.getPath());
204 return buf.toString();
205 }
206
207 /** A path in the node repository */
208 public static String getDataPathForUrl(Content node) {
209 return cleanPathForUrl(getDataPath(node));
210 }
211
212 /** Clean reserved URL characters for use in HTTP links. */
213 public static String cleanPathForUrl(String path) {
214 StringTokenizer st = new StringTokenizer(path, "/");
215 StringBuilder sb = new StringBuilder();
216 while (st.hasMoreElements()) {
217 sb.append('/');
218 String encoded = URLEncoder.encode(st.nextToken(), StandardCharsets.UTF_8);
219 encoded = encoded.replace("+", "%20");
220 sb.append(encoded);
221
222 }
223 return sb.toString();
224 }
225
226 /** Singleton. */
227 private ContentUtils() {
228
229 }
230
231 }