aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files/js/fileactionsmenu.js
diff options
context:
space:
mode:
authorJan-Christoph Borchardt <hey@jancborchardt.net>2018-10-01 20:38:10 +0200
committerJan-Christoph Borchardt <hey@jancborchardt.net>2018-10-01 20:38:10 +0200
commitc2a956f55e74fde2efde9e807d37bd8ff1c76b5b (patch)
tree92332f65c7b9bb0787986bf0fcf2cfe9788b5ea7 /apps/files/js/fileactionsmenu.js
parent86d5bfd81d0d15203b3dbab3d8a6dcc0d1fc6a99 (diff)
downloadnextcloud-server-c2a956f55e74fde2efde9e807d37bd8ff1c76b5b.tar.gz
nextcloud-server-c2a956f55e74fde2efde9e807d37bd8ff1c76b5b.zip
Fix buttons wrapping on long folder names
Signed-off-by: Jan-Christoph Borchardt <hey@jancborchardt.net>
Diffstat (limited to 'apps/files/js/fileactionsmenu.js')
0 files changed, 0 insertions, 0 deletions
cation architecture is in three layers. </para> <itemizedlist> <listitem> <para> The basic objects provide basic functionality to simulate customers, calls and connections (regular calls have one connection, conference calls have more than one). </para> </listitem> <listitem> <para> The timing feature is concerned with timing the connections and keeping the total connection time per customer. Aspects are used to add a timer to each connection and to manage the total time per customer. </para> </listitem> <listitem> <para> The billing feature is concerned with charging customers for the calls they make. Aspects are used to calculate a charge per connection and, upon termination of a connection, to add the charge to the appropriate customer's bill. The billing aspect builds upon the timing aspect: it uses a pointcut defined in Timing and it uses the timers that are associated with connections. </para> </listitem> </itemizedlist> <para> The simulation of system has three configurations: basic, timing and billing. Programs for the three configurations are in classes <classname>BasicSimulation</classname>, <classname>TimingSimulation</classname> and <classname>BillingSimulation</classname>. These share a common superclass <classname>AbstractSimulation</classname>, which defines the method run with the simulation itself and the method wait used to simulate elapsed time. </para> </sect3> <sect3> <title>The Basic Objects</title> <para> The telecom simulation comprises the classes <classname>Customer</classname>, <classname>Call</classname> and the abstract class <classname>Connection</classname> with its two concrete subclasses <classname>Local</classname> and <classname>LongDistance</classname>. Customers have a name and a numeric area code. They also have methods for managing calls. Simple calls are made between one customer (the caller) and another (the receiver), a <classname>Connection</classname> object is used to connect them. Conference calls between more than two customers will involve more than one connection. A customer may be involved in many calls at one time. <inlinemediaobject> <imageobject> <imagedata fileref="telecom.gif"/> </imageobject> </inlinemediaobject> </para> </sect3> <sect3> <title>The <classname>Customer</classname> class</title> <para> <classname>Customer</classname> has methods <literal>call</literal>, <literal>pickup</literal>, <literal>hangup</literal> and <literal>merge</literal> for managing calls. </para> <programlisting><![CDATA[ public class Customer { private String name; private int areacode; private Vector calls = new Vector(); protected void removeCall(Call c){ calls.removeElement(c); } protected void addCall(Call c){ calls.addElement(c); } public Customer(String name, int areacode) { this.name = name; this.areacode = areacode; } public String toString() { return name + "(" + areacode + ")"; } public int getAreacode(){ return areacode; } public boolean localTo(Customer other){ return areacode == other.areacode; } public Call call(Customer receiver) { Call call = new Call(this, receiver); addCall(call); return call; } public void pickup(Call call) { call.pickup(); addCall(call); } public void hangup(Call call) { call.hangup(this); removeCall(call); } public void merge(Call call1, Call call2){ call1.merge(call2); removeCall(call2); } } ]]></programlisting> </sect3> <sect3> <title>The <classname>Call</classname> class</title> <para> Calls are created with a caller and receiver who are customers. If the caller and receiver have the same area code then the call can be established with a <classname>Local</classname> connection (see below), otherwise a <classname>LongDistance</classname> connection is required. A call comprises a number of connections between customers. Initially there is only the connection between the caller and receiver but additional connections can be added if calls are merged to form conference calls. </para> </sect3> <sect3> <title>The <classname>Connection</classname> class</title> <para> The class <classname>Connection</classname> models the physical details of establishing a connection between customers. It does this with a simple state machine (connections are initially <literal>PENDING</literal>, then <literal>COMPLETED</literal> and finally <literal>DROPPED</literal>). Messages are printed to the console so that the state of connections can be observed. Connection is an abstract class with two concrete subclasses: <classname>Local</classname> and <classname>LongDistance</classname>. </para> <programlisting><![CDATA[ abstract class Connection { public static final int PENDING = 0; public static final int COMPLETE = 1; public static final int DROPPED = 2; Customer caller, receiver; private int state = PENDING; Connection(Customer a, Customer b) { this.caller = a; this.receiver = b; } public int getState(){ return state; } public Customer getCaller() { return caller; } public Customer getReceiver() { return receiver; } void complete() { state = COMPLETE; System.out.println("connection completed"); } void drop() { state = DROPPED; System.out.println("connection dropped"); } public boolean connects(Customer c){ return (caller == c || receiver == c); } } ]]></programlisting> </sect3> <sect3> <title>The <literal>Local</literal> and <literal>LongDistance</literal> classes</title> <para> The two kinds of connections supported by our simulation are <literal>Local</literal> and <literal>LongDistance</literal> connections. </para> <programlisting><![CDATA[ class Local extends Connection { Local(Customer a, Customer b) { super(a, b); System.out.println("[new local connection from " + a + " to " + b + "]"); } } ]]></programlisting> <programlisting><![CDATA[ class LongDistance extends Connection { LongDistance(Customer a, Customer b) { super(a, b); System.out.println("[new long distance connection from " + a + " to " + b + "]"); } } ]]></programlisting> </sect3> <sect3> <title>Compiling and Running the Basic Simulation</title> <para> The source files for the basic system are listed in the file <filename>basic.lst</filename>. To build and run the basic system, in a shell window, type these commands: </para> <programlisting><![CDATA[ ajc -argfile telecom/basic.lst java telecom.BasicSimulation ]]></programlisting> </sect3> <sect3> <title>The Timing aspect</title> <para> The <classname>Timing</classname> aspect keeps track of total connection time for each <classname>Customer</classname> by starting and stopping a timer associated with each connection. It uses some helper classes: </para> <sect4> <title>The <classname>Timer</classname> class</title> <para> A <classname>Timer</classname> object simply records the current time when it is started and stopped, and returns their difference when asked for the elapsed time. The aspect <classname>TimerLog</classname> (below) can be used to cause the start and stop times to be printed to standard output. </para> <programlisting><![CDATA[ class Timer { long startTime, stopTime; public void start() { startTime = System.currentTimeMillis(); stopTime = startTime; } public void stop() { stopTime = System.currentTimeMillis(); } public long getTime() { return stopTime - startTime; } } ]]></programlisting> </sect4> </sect3> <sect3> <title>The <classname>TimerLog</classname> aspect</title> <para> The <classname>TimerLog</classname> aspect can be included in a build to get the timer to announce when it is started and stopped. </para> <programlisting><![CDATA[ public aspect TimerLog { after(Timer t): target(t) && call(* Timer.start()) { System.err.println("Timer started: " + t.startTime); } after(Timer t): target(t) && call(* Timer.stop()) { System.err.println("Timer stopped: " + t.stopTime); } } ]]></programlisting> </sect3> <sect3> <title>The <classname>Timing</classname> aspect</title> <para> The <classname>Timing</classname> aspect is declares an inter-type field <literal>totalConnectTime</literal> for <classname>Customer</classname> to store the accumulated connection time per <classname>Customer</classname>. It also declares that each <classname>Connection</classname> object has a timer. <programlisting><![CDATA[ public long Customer.totalConnectTime = 0; private Timer Connection.timer = new Timer(); ]]></programlisting> Two pieces of after advice ensure that the timer is started when a connection is completed and and stopped when it is dropped. The pointcut <literal>endTiming</literal> is defined so that it can be used by the <classname>Billing</classname> aspect. </para> <programlisting><![CDATA[ public aspect Timing { public long Customer.totalConnectTime = 0; public long getTotalConnectTime(Customer cust) { return cust.totalConnectTime; } private Timer Connection.timer = new Timer(); public Timer getTimer(Connection conn) { return conn.timer; } after (Connection c): target(c) && call(void Connection.complete()) { getTimer(c).start(); } pointcut endTiming(Connection c): target(c) && call(void Connection.drop()); after(Connection c): endTiming(c) { getTimer(c).stop(); c.getCaller().totalConnectTime += getTimer(c).getTime(); c.getReceiver().totalConnectTime += getTimer(c).getTime(); } }]]></programlisting> </sect3> <sect3> <title>The <literal>Billing</literal> aspect</title> <para> The Billing system adds billing functionality to the telecom application on top of timing. </para> <para> The <classname>Billing</classname> aspect declares that each <classname>Connection</classname> has a <literal>payer</literal> inter-type field to indicate who initiated the call and therefore who is responsible to pay for it. It also declares the inter-type method <literal>callRate</literal> of <classname>Connection</classname> so that local and long distance calls can be charged differently. The call charge must be calculated after the timer is stopped; the after advice on pointcut <literal>Timing.endTiming</literal> does this, and <classname>Billing</classname> is declared to be more precedent than <classname>Timing</classname> to make sure that this advice runs after <classname>Timing</classname>'s advice on the same join point. Finally, it declares inter-type methods and fields for <classname>Customer</classname> to handle the <literal>totalCharge</literal>. </para> <programlisting><![CDATA[ public aspect Billing { // precedence required to get advice on endtiming in the right order declare precedence: Billing, Timing; public static final long LOCAL_RATE = 3; public static final long LONG_DISTANCE_RATE = 10; public Customer Connection.payer; public Customer getPayer(Connection conn) { return conn.payer; } after(Customer cust) returning (Connection conn): args(cust, ..) && call(Connection+.new(..)) { conn.payer = cust; } public abstract long Connection.callRate(); public long LongDistance.callRate() { return LONG_DISTANCE_RATE; } public long Local.callRate() { return LOCAL_RATE; } after(Connection conn): Timing.endTiming(conn) { long time = Timing.aspectOf().getTimer(conn).getTime(); long rate = conn.callRate(); long cost = rate * time; getPayer(conn).addCharge(cost); } public long Customer.totalCharge = 0; public long getTotalCharge(Customer cust) { return cust.totalCharge; } public void Customer.addCharge(long charge){ totalCharge += charge; } } ]]></programlisting> </sect3> <sect3> <title>Accessing the inter-type state</title> <para> Both the aspects <classname>Timing</classname> and <classname>Billing</classname> contain the definition of operations that the rest of the system may want to access. For example, when running the simulation with one or both aspects, we want to find out how much time each customer spent on the telephone and how big their bill is. That information is also stored in the classes, but they are accessed through static methods of the aspects, since the state they refer to is private to the aspect. </para> <para> Take a look at the file <filename>TimingSimulation.java</filename>. The most important method of this class is the method <filename>report(Customer)</filename>, which is used in the method run of the superclass <classname>AbstractSimulation</classname>. This method is intended to print out the status of the customer, with respect to the <classname>Timing</classname> feature. </para> <programlisting><![CDATA[ protected void report(Customer c){ Timing t = Timing.aspectOf(); System.out.println(c + " spent " + t.getTotalConnectTime(c)); } ]]></programlisting> </sect3> <sect3> <title>Compiling and Running</title> <para> The files timing.lst and billing.lst contain file lists for the timing and billing configurations. To build and run the application with only the timing feature, go to the directory examples and type: </para> <programlisting><![CDATA[ ajc -argfile telecom/timing.lst java telecom.TimingSimulation ]]></programlisting> <para> To build and run the application with the timing and billing features, go to the directory examples and type: </para> <programlisting><![CDATA[ ajc -argfile telecom/billing.lst java telecom.BillingSimulation ]]></programlisting> </sect3> <sect3> <title>Discussion</title> <para> There are some explicit dependencies between the aspects Billing and Timing: <itemizedlist> <listitem> <para> Billing is declared more precedent than Timing so that Billing's after advice runs after that of Timing when they are on the same join point. </para> </listitem> <listitem> <para> Billing uses the pointcut Timing.endTiming. </para> </listitem> <listitem> <para> Billing needs access to the timer associated with a connection. </para> </listitem> </itemizedlist> </para> </sect3> </sect2> </sect1> <!-- ============================================================ --> <!-- ============================================================ --> <sect1 id="examples-reusable"> <title>Reusable Aspects</title> <sect2 id="tracing-using-aspects-revisited" xreflabel="tracing-using-aspects-revisited"> <title>Tracing using Aspects, Revisited</title> <para> (The code for this example is in <filename><replaceable>InstallDir</replaceable>/examples/tracing</filename>.) </para> <sect3> <title>Tracing&mdash;Version 3</title> <para> One advantage of not exposing the methods traceEntry and traceExit as public operations is that we can easily change their interface without any dramatic consequences in the rest of the code. </para> <para> Consider, again, the program without AspectJ. Suppose, for example, that at some point later the requirements for tracing change, stating that the trace messages should always include the string representation of the object whose methods are being traced. This can be achieved in at least two ways. One way is keep the interface of the methods <literal>traceEntry</literal> and <literal>traceExit</literal> as it was before, </para> <programlisting><![CDATA[ public static void traceEntry(String str); public static void traceExit(String str); ]]></programlisting> <para> In this case, the caller is responsible for ensuring that the string representation of the object is part of the string given as argument. So, calls must look like: </para> <programlisting><![CDATA[ Trace.traceEntry("Square.distance in " + toString()); ]]></programlisting> <para> Another way is to enforce the requirement with a second argument in the trace operations, e.g. </para> <programlisting><![CDATA[ public static void traceEntry(String str, Object obj); public static void traceExit(String str, Object obj); ]]></programlisting> <para> In this case, the caller is still responsible for sending the right object, but at least there is some guarantees that some object will be passed. The calls will look like: </para> <programlisting><![CDATA[ Trace.traceEntry("Square.distance", this); ]]></programlisting> <para> In either case, this change to the requirements of tracing will have dramatic consequences in the rest of the code -- every call to the trace operations traceEntry and traceExit must be changed! </para> <para> Here's another advantage of doing tracing with an aspect. We've already seen that in version 2 <literal>traceEntry</literal> and <literal>traceExit</literal> are not publicly exposed. So changing their interfaces, or the way they are used, has only a small effect inside the <classname>Trace</classname> class. Here's a partial view at the implementation of <classname>Trace</classname>, version 3. The differences with respect to version 2 are stressed in the comments: </para> <programlisting><![CDATA[ abstract aspect Trace { public static int TRACELEVEL = 0; protected static PrintStream stream = null; protected static int callDepth = 0; public static void initStream(PrintStream s) { stream = s; } protected static void traceEntry(String str, Object o) { if (TRACELEVEL == 0) return; if (TRACELEVEL == 2) callDepth++; printEntering(str + ": " + o.toString()); } protected static void traceExit(String str, Object o) { if (TRACELEVEL == 0) return; printExiting(str + ": " + o.toString()); if (TRACELEVEL == 2) callDepth--; } private static void printEntering(String str) { printIndent(); stream.println("Entering " + str); } private static void printExiting(String str) { printIndent(); stream.println("Exiting " + str); } private static void printIndent() { for (int i = 0; i < callDepth; i++) stream.print(" "); } abstract pointcut myClass(Object obj); pointcut myConstructor(Object obj): myClass(obj) && execution(new(..)); pointcut myMethod(Object obj): myClass(obj) && execution(* *(..)) && !execution(String toString()); before(Object obj): myConstructor(obj) { traceEntry("" + thisJoinPointStaticPart.getSignature(), obj); } after(Object obj): myConstructor(obj) { traceExit("" + thisJoinPointStaticPart.getSignature(), obj); } before(Object obj): myMethod(obj) { traceEntry("" + thisJoinPointStaticPart.getSignature(), obj); } after(Object obj): myMethod(obj) { traceExit("" + thisJoinPointStaticPart.getSignature(), obj); } } ]]></programlisting> <para> As you can see, we decided to apply the first design by preserving the interface of the methods <literal>traceEntry</literal> and <literal>traceExit</literal>. But it doesn't matter&mdash;we could as easily have applied the second design (the code in the directory <filename>examples/tracing/version3</filename> has the second design). The point is that the effects of this change in the tracing requirements are limited to the <classname>Trace</classname> aspect class. </para> <para> One implementation change worth noticing is the specification of the pointcuts. They now expose the object. To maintain full consistency with the behavior of version 2, we should have included tracing for static methods, by defining another pointcut for static methods and advising it. We leave that as an exercise. </para> <para> Moreover, we had to exclude the execution join point of the method <filename>toString</filename> from the <literal>methods</literal> pointcut. The problem here is that <literal>toString</literal> is being called from inside the advice. Therefore if we trace it, we will end up in an infinite recursion of calls. This is a subtle point, and one that you must be aware when writing advice. If the advice calls back to the objects, there is always the possibility of recursion. Keep that in mind! </para> <para> In fact, esimply excluding the execution join point may not be enough, if there are calls to other traced methods within it -- in which case, the restriction should be </para> <programlisting><![CDATA[ && !cflow(execution(String toString())) ]]></programlisting> <para> excluding both the execution of toString methods and all join points under that execution. </para> <para> In summary, to implement the change in the tracing requirements we had to make a couple of changes in the implementation of the <classname>Trace</classname> aspect class, including changing the specification of the pointcuts. That's only natural. But the implementation changes were limited to this aspect. Without aspects, we would have to change the implementation of every application class. </para> <para> Finally, to run this version of tracing, go to the directory <filename>examples</filename> and type: </para> <programlisting><![CDATA[ ajc -argfile tracing/tracev3.lst ]]></programlisting> <para> The file tracev3.lst lists the application classes as well as this version of the files <filename>Trace.java</filename> and <filename>TraceMyClasses.java</filename>. To run the program, type </para> <programlisting><![CDATA[ java tracing.version3.TraceMyClasses ]]></programlisting> <para>The output should be:</para> <programlisting><![CDATA[ --> tracing.TwoDShape(double, double) <-- tracing.TwoDShape(double, double) --> tracing.Circle(double, double, double) <-- tracing.Circle(double, double, double) --> tracing.TwoDShape(double, double) <-- tracing.TwoDShape(double, double) --> tracing.Circle(double, double, double) <-- tracing.Circle(double, double, double) --> tracing.Circle(double) <-- tracing.Circle(double) --> tracing.TwoDShape(double, double) <-- tracing.TwoDShape(double, double) --> tracing.Square(double, double, double) <-- tracing.Square(double, double, double) --> tracing.Square(double, double) <-- tracing.Square(double, double) --> double tracing.Circle.perimeter() <-- double tracing.Circle.perimeter() c1.perimeter() = 12.566370614359172 --> double tracing.Circle.area() <-- double tracing.Circle.area() c1.area() = 12.566370614359172 --> double tracing.Square.perimeter() <-- double tracing.Square.perimeter() s1.perimeter() = 4.0 --> double tracing.Square.area() <-- double tracing.Square.area() s1.area() = 1.0 --> double tracing.TwoDShape.distance(TwoDShape) --> double tracing.TwoDShape.getX() <-- double tracing.TwoDShape.getX() --> double tracing.TwoDShape.getY() <-- double tracing.TwoDShape.getY() <-- double tracing.TwoDShape.distance(TwoDShape) c2.distance(c1) = 4.242640687119285 --> double tracing.TwoDShape.distance(TwoDShape) --> double tracing.TwoDShape.getX() <-- double tracing.TwoDShape.getX() --> double tracing.TwoDShape.getY() <-- double tracing.TwoDShape.getY() <-- double tracing.TwoDShape.distance(TwoDShape) s1.distance(c1) = 2.23606797749979 --> String tracing.Square.toString() --> String tracing.TwoDShape.toString() <-- String tracing.TwoDShape.toString() <-- String tracing.Square.toString() s1.toString(): Square side = 1.0 @ (1.0, 2.0) ]]></programlisting> </sect3> </sect2> </sect1> </chapter>